• Steven Rostedt (Google)'s avatar
    eventfs: Remember what dentries were created on dir open · ef36b4f9
    Steven Rostedt (Google) authored
    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/20230919211804.230edf1e@gandalf.local.home/
    Link: https://lore.kernel.org/linux-trace-kernel/20230922163446.1431d4fa@gandalf.local.home
    
    Cc: Mark Rutland <mark.rutland@arm.com>
    Cc: Ajay Kaher <akaher@vmware.com>
    Fixes: 63940449 ("eventfs: Implement eventfs lookup, read, open functions")
    Reported-by: default avatar"Masami Hiramatsu (Google)" <mhiramat@kernel.org>
    Signed-off-by: default avatarSteven Rostedt (Google) <rostedt@goodmis.org>
    ef36b4f9
event_inode.c 24.2 KB