Skip to content

Commit ef36b4f

Browse files
committed
eventfs: Remember what dentries were created on dir open
Using the following code with libtracefs: int dfd; // create the directory events/kprobes/kp1 tracefs_kprobe_raw(NULL, "kp1", "schedule_timeout", "time=$arg1"); // Open the kprobes directory dfd = tracefs_instance_file_open(NULL, "events/kprobes", O_RDONLY); // Do a lookup of the kprobes/kp1 directory (by looking at enable) tracefs_file_exists(NULL, "events/kprobes/kp1/enable"); // Now create a new entry in the kprobes directory tracefs_kprobe_raw(NULL, "kp2", "schedule_hrtimeout", "expires=$arg1"); // Do another lookup to create the dentries tracefs_file_exists(NULL, "events/kprobes/kp2/enable")) // Close the directory close(dfd); What happened above, the first open (dfd) will call dcache_dir_open_wrapper() that will create the dentries and up their ref counts. Now the creation of "kp2" will add another dentry within the kprobes directory. Upon the close of dfd, eventfs_release() will now do a dput for all the entries in kprobes. But this is where the problem lies. The open only upped the dentry of kp1 and not kp2. Now the close is decrementing both kp1 and kp2, which causes kp2 to get a negative count. Doing a "trace-cmd reset" which deletes all the kprobes cause the kernel to crash! (due to the messed up accounting of the ref counts). To solve this, save all the dentries that are opened in the dcache_dir_open_wrapper() into an array, and use this array to know what dentries to do a dput on in eventfs_release(). Since the dcache_dir_open_wrapper() calls dcache_dir_open() which uses the file->private_data, we need to also add a wrapper around dcache_readdir() that uses the cursor assigned to the file->private_data. This is because the dentries need to also be saved in the file->private_data. To do this create the structure: struct dentry_list { void *cursor; struct dentry **dentries; }; Which will hold both the cursor and the dentries. Some shuffling around is needed to make sure that dcache_dir_open() and dcache_readdir() only see the cursor. Link: https://lore.kernel.org/linux-trace-kernel/[email protected]/ Link: https://lore.kernel.org/linux-trace-kernel/[email protected] Cc: Mark Rutland <[email protected]> Cc: Ajay Kaher <[email protected]> Fixes: 6394044 ("eventfs: Implement eventfs lookup, read, open functions") Reported-by: "Masami Hiramatsu (Google)" <[email protected]> Signed-off-by: Steven Rostedt (Google) <[email protected]>
1 parent 45d99ea commit ef36b4f

File tree

1 file changed

+70
-17
lines changed

1 file changed

+70
-17
lines changed

fs/tracefs/event_inode.c

Lines changed: 70 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ static struct dentry *eventfs_root_lookup(struct inode *dir,
7070
struct dentry *dentry,
7171
unsigned int flags);
7272
static int dcache_dir_open_wrapper(struct inode *inode, struct file *file);
73+
static int dcache_readdir_wrapper(struct file *file, struct dir_context *ctx);
7374
static int eventfs_release(struct inode *inode, struct file *file);
7475

7576
static const struct inode_operations eventfs_root_dir_inode_operations = {
@@ -79,7 +80,7 @@ static const struct inode_operations eventfs_root_dir_inode_operations = {
7980
static const struct file_operations eventfs_file_operations = {
8081
.open = dcache_dir_open_wrapper,
8182
.read = generic_read_dir,
82-
.iterate_shared = dcache_readdir,
83+
.iterate_shared = dcache_readdir_wrapper,
8384
.llseek = generic_file_llseek,
8485
.release = eventfs_release,
8586
};
@@ -396,6 +397,11 @@ static struct dentry *eventfs_root_lookup(struct inode *dir,
396397
return ret;
397398
}
398399

400+
struct dentry_list {
401+
void *cursor;
402+
struct dentry **dentries;
403+
};
404+
399405
/**
400406
* eventfs_release - called to release eventfs file/dir
401407
* @inode: inode to be released
@@ -404,26 +410,25 @@ static struct dentry *eventfs_root_lookup(struct inode *dir,
404410
static int eventfs_release(struct inode *inode, struct file *file)
405411
{
406412
struct tracefs_inode *ti;
407-
struct eventfs_inode *ei;
408-
struct eventfs_file *ef;
409-
struct dentry *dentry;
410-
int idx;
413+
struct dentry_list *dlist = file->private_data;
414+
void *cursor;
415+
int i;
411416

412417
ti = get_tracefs(inode);
413418
if (!(ti->flags & TRACEFS_EVENT_INODE))
414419
return -EINVAL;
415420

416-
ei = ti->private;
417-
idx = srcu_read_lock(&eventfs_srcu);
418-
list_for_each_entry_srcu(ef, &ei->e_top_files, list,
419-
srcu_read_lock_held(&eventfs_srcu)) {
420-
mutex_lock(&eventfs_mutex);
421-
dentry = ef->dentry;
422-
mutex_unlock(&eventfs_mutex);
423-
if (dentry)
424-
dput(dentry);
421+
if (WARN_ON_ONCE(!dlist))
422+
return -EINVAL;
423+
424+
for (i = 0; dlist->dentries[i]; i++) {
425+
dput(dlist->dentries[i]);
425426
}
426-
srcu_read_unlock(&eventfs_srcu, idx);
427+
428+
cursor = dlist->cursor;
429+
kfree(dlist->dentries);
430+
kfree(dlist);
431+
file->private_data = cursor;
427432
return dcache_dir_close(inode, file);
428433
}
429434

@@ -442,22 +447,70 @@ static int dcache_dir_open_wrapper(struct inode *inode, struct file *file)
442447
struct tracefs_inode *ti;
443448
struct eventfs_inode *ei;
444449
struct eventfs_file *ef;
450+
struct dentry_list *dlist;
451+
struct dentry **dentries = NULL;
445452
struct dentry *dentry = file_dentry(file);
453+
struct dentry *d;
446454
struct inode *f_inode = file_inode(file);
455+
int cnt = 0;
447456
int idx;
457+
int ret;
448458

449459
ti = get_tracefs(f_inode);
450460
if (!(ti->flags & TRACEFS_EVENT_INODE))
451461
return -EINVAL;
452462

463+
if (WARN_ON_ONCE(file->private_data))
464+
return -EINVAL;
465+
466+
dlist = kmalloc(sizeof(*dlist), GFP_KERNEL);
467+
if (!dlist)
468+
return -ENOMEM;
469+
453470
ei = ti->private;
454471
idx = srcu_read_lock(&eventfs_srcu);
455472
list_for_each_entry_srcu(ef, &ei->e_top_files, list,
456473
srcu_read_lock_held(&eventfs_srcu)) {
457-
create_dentry(ef, dentry, false);
474+
d = create_dentry(ef, dentry, false);
475+
if (d) {
476+
struct dentry **tmp;
477+
478+
tmp = krealloc(dentries, sizeof(d) * (cnt + 2), GFP_KERNEL);
479+
if (!tmp)
480+
break;
481+
tmp[cnt] = d;
482+
tmp[cnt + 1] = NULL;
483+
cnt++;
484+
dentries = tmp;
485+
}
458486
}
459487
srcu_read_unlock(&eventfs_srcu, idx);
460-
return dcache_dir_open(inode, file);
488+
ret = dcache_dir_open(inode, file);
489+
490+
/*
491+
* dcache_dir_open() sets file->private_data to a dentry cursor.
492+
* Need to save that but also save all the dentries that were
493+
* opened by this function.
494+
*/
495+
dlist->cursor = file->private_data;
496+
dlist->dentries = dentries;
497+
file->private_data = dlist;
498+
return ret;
499+
}
500+
501+
/*
502+
* This just sets the file->private_data back to the cursor and back.
503+
*/
504+
static int dcache_readdir_wrapper(struct file *file, struct dir_context *ctx)
505+
{
506+
struct dentry_list *dlist = file->private_data;
507+
int ret;
508+
509+
file->private_data = dlist->cursor;
510+
ret = dcache_readdir(file, ctx);
511+
dlist->cursor = file->private_data;
512+
file->private_data = dlist;
513+
return ret;
461514
}
462515

463516
/**

0 commit comments

Comments
 (0)