Skip to content

Commit b156040

Browse files
committed
tracing: Have format file honor EVENT_FILE_FL_FREED
When eventfs was introduced, special care had to be done to coordinate the freeing of the file meta data with the files that are exposed to user space. The file meta data would have a ref count that is set when the file is created and would be decremented and freed after the last user that opened the file closed it. When the file meta data was to be freed, it would set a flag (EVENT_FILE_FL_FREED) to denote that the file is freed, and any new references made (like new opens or reads) would fail as it is marked freed. This allowed other meta data to be freed after this flag was set (under the event_mutex). All the files that were dynamically created in the events directory had a pointer to the file meta data and would call event_release() when the last reference to the user space file was closed. This would be the time that it is safe to free the file meta data. A shortcut was made for the "format" file. It's i_private would point to the "call" entry directly and not point to the file's meta data. This is because all format files are the same for the same "call", so it was thought there was no reason to differentiate them. The other files maintain state (like the "enable", "trigger", etc). But this meant if the file were to disappear, the "format" file would be unaware of it. This caused a race that could be trigger via the user_events test (that would create dynamic events and free them), and running a loop that would read the user_events format files: In one console run: # cd tools/testing/selftests/user_events # while true; do ./ftrace_test; done And in another console run: # cd /sys/kernel/tracing/ # while true; do cat events/user_events/__test_event/format; done 2>/dev/null With KASAN memory checking, it would trigger a use-after-free bug report (which was a real bug). This was because the format file was not checking the file's meta data flag "EVENT_FILE_FL_FREED", so it would access the event that the file meta data pointed to after the event was freed. After inspection, there are other locations that were found to not check the EVENT_FILE_FL_FREED flag when accessing the trace_event_file. Add a new helper function: event_file_file() that will make sure that the event_mutex is held, and will return NULL if the trace_event_file has the EVENT_FILE_FL_FREED flag set. Have the first reference of the struct file pointer use event_file_file() and check for NULL. Later uses can still use the event_file_data() helper function if the event_mutex is still held and was not released since the event_file_file() call. Link: https://lore.kernel.org/all/[email protected]/ Cc: [email protected] Cc: Masami Hiramatsu <[email protected]> Cc: Mathieu Desnoyers <[email protected]> Cc: Ajay Kaher <[email protected]> Cc: Ilkka Naulapää <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Al Viro <[email protected]> Cc: Dan Carpenter <[email protected]> Cc: Beau Belgrave <[email protected]> Cc: Florian Fainelli <[email protected]> Cc: Alexey Makhalov <[email protected]> Cc: Vasavi Sirnapalli <[email protected]> Link: https://lore.kernel.org/[email protected] Fixes: b63db58 ("eventfs/tracing: Add callback for release of an eventfs_inode") Reported-by: Mathias Krause <[email protected]> Tested-by: Mathias Krause <[email protected]> Signed-off-by: Steven Rostedt (Google) <[email protected]>
1 parent de9c2c6 commit b156040

File tree

5 files changed

+49
-19
lines changed

5 files changed

+49
-19
lines changed

kernel/trace/trace.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1634,6 +1634,29 @@ static inline void *event_file_data(struct file *filp)
16341634
extern struct mutex event_mutex;
16351635
extern struct list_head ftrace_events;
16361636

1637+
/*
1638+
* When the trace_event_file is the filp->i_private pointer,
1639+
* it must be taken under the event_mutex lock, and then checked
1640+
* if the EVENT_FILE_FL_FREED flag is set. If it is, then the
1641+
* data pointed to by the trace_event_file can not be trusted.
1642+
*
1643+
* Use the event_file_file() to access the trace_event_file from
1644+
* the filp the first time under the event_mutex and check for
1645+
* NULL. If it is needed to be retrieved again and the event_mutex
1646+
* is still held, then the event_file_data() can be used and it
1647+
* is guaranteed to be valid.
1648+
*/
1649+
static inline struct trace_event_file *event_file_file(struct file *filp)
1650+
{
1651+
struct trace_event_file *file;
1652+
1653+
lockdep_assert_held(&event_mutex);
1654+
file = READ_ONCE(file_inode(filp)->i_private);
1655+
if (!file || file->flags & EVENT_FILE_FL_FREED)
1656+
return NULL;
1657+
return file;
1658+
}
1659+
16371660
extern const struct file_operations event_trigger_fops;
16381661
extern const struct file_operations event_hist_fops;
16391662
extern const struct file_operations event_hist_debug_fops;

kernel/trace/trace_events.c

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1386,12 +1386,12 @@ event_enable_read(struct file *filp, char __user *ubuf, size_t cnt,
13861386
char buf[4] = "0";
13871387

13881388
mutex_lock(&event_mutex);
1389-
file = event_file_data(filp);
1389+
file = event_file_file(filp);
13901390
if (likely(file))
13911391
flags = file->flags;
13921392
mutex_unlock(&event_mutex);
13931393

1394-
if (!file || flags & EVENT_FILE_FL_FREED)
1394+
if (!file)
13951395
return -ENODEV;
13961396

13971397
if (flags & EVENT_FILE_FL_ENABLED &&
@@ -1424,8 +1424,8 @@ event_enable_write(struct file *filp, const char __user *ubuf, size_t cnt,
14241424
case 1:
14251425
ret = -ENODEV;
14261426
mutex_lock(&event_mutex);
1427-
file = event_file_data(filp);
1428-
if (likely(file && !(file->flags & EVENT_FILE_FL_FREED))) {
1427+
file = event_file_file(filp);
1428+
if (likely(file)) {
14291429
ret = tracing_update_buffers(file->tr);
14301430
if (ret < 0) {
14311431
mutex_unlock(&event_mutex);
@@ -1540,7 +1540,8 @@ enum {
15401540

15411541
static void *f_next(struct seq_file *m, void *v, loff_t *pos)
15421542
{
1543-
struct trace_event_call *call = event_file_data(m->private);
1543+
struct trace_event_file *file = event_file_data(m->private);
1544+
struct trace_event_call *call = file->event_call;
15441545
struct list_head *common_head = &ftrace_common_fields;
15451546
struct list_head *head = trace_get_fields(call);
15461547
struct list_head *node = v;
@@ -1572,7 +1573,8 @@ static void *f_next(struct seq_file *m, void *v, loff_t *pos)
15721573

15731574
static int f_show(struct seq_file *m, void *v)
15741575
{
1575-
struct trace_event_call *call = event_file_data(m->private);
1576+
struct trace_event_file *file = event_file_data(m->private);
1577+
struct trace_event_call *call = file->event_call;
15761578
struct ftrace_event_field *field;
15771579
const char *array_descriptor;
15781580

@@ -1627,12 +1629,14 @@ static int f_show(struct seq_file *m, void *v)
16271629

16281630
static void *f_start(struct seq_file *m, loff_t *pos)
16291631
{
1632+
struct trace_event_file *file;
16301633
void *p = (void *)FORMAT_HEADER;
16311634
loff_t l = 0;
16321635

16331636
/* ->stop() is called even if ->start() fails */
16341637
mutex_lock(&event_mutex);
1635-
if (!event_file_data(m->private))
1638+
file = event_file_file(m->private);
1639+
if (!file)
16361640
return ERR_PTR(-ENODEV);
16371641

16381642
while (l < *pos && p)
@@ -1706,8 +1710,8 @@ event_filter_read(struct file *filp, char __user *ubuf, size_t cnt,
17061710
trace_seq_init(s);
17071711

17081712
mutex_lock(&event_mutex);
1709-
file = event_file_data(filp);
1710-
if (file && !(file->flags & EVENT_FILE_FL_FREED))
1713+
file = event_file_file(filp);
1714+
if (file)
17111715
print_event_filter(file, s);
17121716
mutex_unlock(&event_mutex);
17131717

@@ -1736,9 +1740,13 @@ event_filter_write(struct file *filp, const char __user *ubuf, size_t cnt,
17361740
return PTR_ERR(buf);
17371741

17381742
mutex_lock(&event_mutex);
1739-
file = event_file_data(filp);
1740-
if (file)
1741-
err = apply_event_filter(file, buf);
1743+
file = event_file_file(filp);
1744+
if (file) {
1745+
if (file->flags & EVENT_FILE_FL_FREED)
1746+
err = -ENODEV;
1747+
else
1748+
err = apply_event_filter(file, buf);
1749+
}
17421750
mutex_unlock(&event_mutex);
17431751

17441752
kfree(buf);
@@ -2485,7 +2493,6 @@ static int event_callback(const char *name, umode_t *mode, void **data,
24852493
if (strcmp(name, "format") == 0) {
24862494
*mode = TRACE_MODE_READ;
24872495
*fops = &ftrace_event_format_fops;
2488-
*data = call;
24892496
return 1;
24902497
}
24912498

kernel/trace/trace_events_hist.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5601,7 +5601,7 @@ static int hist_show(struct seq_file *m, void *v)
56015601

56025602
mutex_lock(&event_mutex);
56035603

5604-
event_file = event_file_data(m->private);
5604+
event_file = event_file_file(m->private);
56055605
if (unlikely(!event_file)) {
56065606
ret = -ENODEV;
56075607
goto out_unlock;
@@ -5880,7 +5880,7 @@ static int hist_debug_show(struct seq_file *m, void *v)
58805880

58815881
mutex_lock(&event_mutex);
58825882

5883-
event_file = event_file_data(m->private);
5883+
event_file = event_file_file(m->private);
58845884
if (unlikely(!event_file)) {
58855885
ret = -ENODEV;
58865886
goto out_unlock;

kernel/trace/trace_events_inject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ event_inject_write(struct file *filp, const char __user *ubuf, size_t cnt,
299299
strim(buf);
300300

301301
mutex_lock(&event_mutex);
302-
file = event_file_data(filp);
302+
file = event_file_file(filp);
303303
if (file) {
304304
call = file->event_call;
305305
size = parse_entry(buf, call, &entry);

kernel/trace/trace_events_trigger.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ static void *trigger_start(struct seq_file *m, loff_t *pos)
159159

160160
/* ->stop() is called even if ->start() fails */
161161
mutex_lock(&event_mutex);
162-
event_file = event_file_data(m->private);
162+
event_file = event_file_file(m->private);
163163
if (unlikely(!event_file))
164164
return ERR_PTR(-ENODEV);
165165

@@ -213,7 +213,7 @@ static int event_trigger_regex_open(struct inode *inode, struct file *file)
213213

214214
mutex_lock(&event_mutex);
215215

216-
if (unlikely(!event_file_data(file))) {
216+
if (unlikely(!event_file_file(file))) {
217217
mutex_unlock(&event_mutex);
218218
return -ENODEV;
219219
}
@@ -293,7 +293,7 @@ static ssize_t event_trigger_regex_write(struct file *file,
293293
strim(buf);
294294

295295
mutex_lock(&event_mutex);
296-
event_file = event_file_data(file);
296+
event_file = event_file_file(file);
297297
if (unlikely(!event_file)) {
298298
mutex_unlock(&event_mutex);
299299
kfree(buf);

0 commit comments

Comments
 (0)