tracing/tracefs: Fixes for v6.9
Subsystems (3)
Steven Rostedt
maintainercover tracing/tracefs: Fixes for v6.9
tracing and tracefs fixes for v6.9
- Minor fix for user_events interface
The ABI of creating a user event states that the fields
are separated by semicolons, and spaces should be ignored.
But the parsing expected at least one space to be there (which was incorrect). Fix the reading of the string to handle fields separated by semicolons but no space between them.
- Add test to user event selftests for no spaces between fields.
- Fix RCU callback of freeing an eventfs_inode.
The freeing of the eventfs_inode from the kref going to zero freed the contents of the eventfs_inode and then used kfree_rcu() to free the inode itself. But the contents should also be protected by RCU. Switch to a call_rcu() that calls a function to free all of the eventfs_inode after the RCU synchronization.
- The tracefs maps its own descriptor to a file represented by eventfs. The freeing of this descriptor needs to know when the last reference of an eventfs_inode is released, but currently there is no interface for that. Add a "release" callback to the eventfs_inode entry array that allows for freeing of data that can be referenced by the eventfs_inode being opened. Then increment the ref counter for this descriptor when the eventfs_inode file is created, and decrement/free it when the last reference to the eventfs_inode is released and the file is removed.
- Fix the permission processing of eventfs.
The change to make the permissions of eventfs default to the mount point but keep track of when changes were made had a side effect that could cause security concerns. When the tracefs is remounted with a given gid or uid, all the files within it should inherit that gid or uid. But if the admin had changed the permission of some file within the tracefs file system, it would not get updated by the remount. This caused the kselftest of file permissions to fail the second time it is run. The first time, all changes would look fine, but the second time, because the changes were "saved", the remount did not reset them.
Create a link list of all existing tracefs inodes, and clear the saved flags on them on a remount if the remount changes the corresponding gid or uid fields.
This also simplifies the code by removing the distinction between the toplevel eventfs and an instance eventfs. They should both act the same. They were different because of a misconception due to the remount not resetting the flags. Now that remount resets all the files and directories to default to the root node if a uid/gid is specified, it makes the logic simpler to implement.
Beau Belgrave (2):
tracing/user_events: Fix non-spaced field matching selftests/user_events: Add non-spacing separator check
Steven Rostedt (Google) (7):
eventfs/tracing: Add callback for release of an eventfs_inode
eventfs: Free all of the eventfs_inode after RCU
tracefs: Reset permissions on remount if permissions are options
tracefs: Still use mount point as default permissions for instances
eventfs: Do not differentiate the toplevel events directory
eventfs: Do not treat events directory different than other directories
eventfs: Have "events" directory get permissions from its parent
----
fs/tracefs/event_inode.c | 148 ++++++++++++++-------- fs/tracefs/inode.c | 92 +++++++++++++- fs/tracefs/internal.h | 14 +- include/linux/tracefs.h | 3 + kernel/trace/trace_events.c | 12 ++ kernel/trace/trace_events_user.c | 76 ++++++++++- tools/testing/selftests/user_events/ftrace_test.c | 8 ++ 7 files changed, 293 insertions(+), 60 deletions(-)
Steven Rostedt
maintainerPATCH 1/9 tracing/user_events: Fix non-spaced field matching
From: Beau Belgrave <[email protected]>
When the ABI was updated to prevent same name w/different args, it missed an important corner case when fields don't end with a space. Typically, space is used for fields to help separate them, like "u8 field1; u8 field2". If no spaces are used, like
"u8 field1;u8 field2", then the parsing works for the first time. However, the match check fails on a subsequent register, leading to confusion.
This is because the match check uses argv_split() and assumes that all fields will be split upon the space. When spaces are used, we get back { "u8", "field1;" }, without spaces we get back { "u8", "field1;u8" }. This causes a mismatch, and the user program gets back -EADDRINUSE.
Add a method to detect this case before calling argv_split(). If found force a space after the field separator character ';'. This ensures all cases work properly for matching.
With this fix, the following are all treated as matching:
u8 field1;u8 field2
u8 field1; u8 field2
u8 field1;\tu8 field2
u8 field1;\nu8 field2
--- kernel/trace/trace_events_user.c | 76 +++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-)
kernel/trace/trace_events_user.c +75 -1
@@ -1989,6 +1989,80 @@ static int user_event_set_tp_name(struct user_event *user)return 0 ; } +/* +* Counts how many ';' without a trailing space are in the args. +*/ +static int count_semis_no_space (char *args) +{ +int count =0 ; + +while ((args =strchr (args,';' ))) { + args++; + +if (!isspace (*args)) + count++; + } + +return count; +} + +/* +* Copies the arguments while ensuring all ';' have a trailing space. +*/ +static char *insert_space_after_semis (char *args,int count) +{ +char *fixed, *pos; +int len; + + len =strlen (args) + count; + fixed =kmalloc (len +1 ,GFP_KERNEL ); + +if (!fixed) +return NULL ; + + pos = fixed; + +/* Insert a space after ';' if there is no trailing space. */ +while (*args) { + *pos = *args++; + +if (*pos++ ==';' && !isspace (*args)) + *pos++ =' ' ; + } + + *pos ='\0' ; + +return fixed; +} + +static char **user_event_argv_split (char *args,int *argc) +{ +char **split; +char *fixed; +int count; + +/* Count how many ';' without a trailing space */ + count =count_semis_no_space (args); + +/* No fixup is required */ +if (!count) +return argv_split (GFP_KERNEL , args, argc); + +/* We must fixup 'field;field' to 'field; field' */ + fixed =insert_space_after_semis (args, count); + +if (!fixed) +return NULL ; + +/* We do a normal split afterwards */ + split =argv_split (GFP_KERNEL , fixed, argc); + +/* We can free since argv_split makes a copy */ +kfree (fixed); + +return split; +} +/* * Parses the event name, arguments and flags then registers if successful. * The name buffer lifetime is owned by this method for success cases only.
@@ -2012,7 +2086,7 @@ static int user_event_parse(struct user_event_group *group, char *name,return -EPERM ;if (args) { - argv =argv_split (GFP_KERNEL , args, &argc); + argv =user_event_argv_split (args, &argc);if (!argv)return -ENOMEM ;
Signature · 1 line
2.43.0
Steven Rostedt
maintainerPATCH 2/9 selftests/user_events: Add non-spacing separator check
In reply to Steven RostedtFrom: Beau Belgrave <[email protected]>
The ABI documentation indicates that field separators do not need a space between them, only a ';'. When no spacing is used, the register must work. Any subsequent register, with or without spaces, must match and not return -EADDRINUSE.
Add a non-spacing separator case to our self-test register case to ensure it works going forward.
--- tools/testing/selftests/user_events/ftrace_test.c | 8 ++++++++ 1 file changed, 8 insertions(+)
tools/testing/selftests/user_events/ftrace_test.c +8 -0
@@ -261,6 +261,12 @@ TEST_F(user, register_events) {ASSERT_EQ (0 ,ioctl (self->data_fd,DIAG_IOCSREG , ®));ASSERT_EQ (0 , reg.write_index); +/* Register without separator spacing should still match */ + reg.enable_bit =29 ; + reg.name_args = (__u64 )"__test_event u32 field1;u32 field2" ; +ASSERT_EQ (0 ,ioctl (self->data_fd,DIAG_IOCSREG , ®)); +ASSERT_EQ (0 , reg.write_index); +/* Multiple registers to same name but different args should fail */ reg.enable_bit =29 ; reg.name_args = (__u64 )"__test_event u32 field1;" ;
@@ -288,6 +294,8 @@ TEST_F(user, register_events) {ASSERT_EQ (0 ,ioctl (self->data_fd,DIAG_IOCSUNREG , &unreg)); unreg.disable_bit =30 ;ASSERT_EQ (0 ,ioctl (self->data_fd,DIAG_IOCSUNREG , &unreg)); + unreg.disable_bit =29 ; +ASSERT_EQ (0 ,ioctl (self->data_fd,DIAG_IOCSUNREG , &unreg));/* Delete should have been auto-done after close and unregister */ close (self->data_fd);
Signature · 1 line
2.43.0
Steven Rostedt
maintainerPATCH 3/9 eventfs/tracing: Add callback for release of an eventfs_inode
In reply to Steven RostedtFrom: "Steven Rostedt (Google)" <[email protected]>
Synthetic events create and destroy tracefs files when they are created and removed. The tracing subsystem has its own file descriptor representing the state of the events attached to the tracefs files. There's a race between the eventfs files and this file descriptor of the tracing system where the following can cause an issue:
With two scripts 'A' and 'B' doing:
Script 'A':
echo "hello int aaa" > /sys/kernel/tracing/synthetic_events while :
do
echo 0 > /sys/kernel/tracing/events/synthetic/hello/enable
done
Script 'B':
echo > /sys/kernel/tracing/synthetic_events
Script 'A' creates a synthetic event "hello" and then just writes zero into its enable file.
Script 'B' removes all synthetic events (including the newly created "hello" event).
What happens is that the opening of the "enable" file has:
{
struct trace_event_file *file = inode->i_private;
int ret;
ret = tracing_check_open_get_tr(file->tr);[..]
But deleting the events frees the "file" descriptor, and a "use after free" happens with the dereference at "file->tr".
The file descriptor does have a reference counter, but there needs to be a way to decrement it from the eventfs when the eventfs_inode is removed that represents this file descriptor.
Add an optional "release" callback to the eventfs_entry array structure, that gets called when the eventfs file is about to be removed. This allows for the creating on the eventfs file to increment the tracing file descriptor ref counter. When the eventfs file is deleted, it can call the release function that will call the put function for the tracing file descriptor.
This will protect the tracing file from being freed while a eventfs file that references it is being opened.
--- fs/tracefs/event_inode.c | 23 +++++++++++++++++++++-- include/linux/tracefs.h | 3 +++ kernel/trace/trace_events.c | 12 ++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-)
fs/tracefs/event_inode.c +21 -2
@@ -84,10 +84,17 @@ enum {static void release_ei (struct kref *ref) { struct eventfs_inode *ei = container_of(ref, struct eventfs_inode, kref); +const struct eventfs_entry *entry;struct eventfs_root_inode *rei;WARN_ON_ONCE (!ei->is_freed); +for (int i =0 ; i < ei->nr_entries; i++) { + entry = &ei->entries[i]; +if (entry->release) + entry->release (entry->name, ei->data); + } +kfree (ei->entry_attrs);kfree_const (ei->name);if (ei->is_events) {
@@ -112,6 +119,18 @@ static inline void free_ei(struct eventfs_inode *ei) } } +/* +* Called when creation of an ei fails, do not call release() functions. +*/ +static inline void cleanup_ei (struct eventfs_inode *ei) +{ +if (ei) { +/* Set nr_entries to 0 to prevent release() function being called */ + ei->nr_entries =0 ; +free_ei (ei); + } +} +static inline struct eventfs_inode *get_ei (struct eventfs_inode *ei) {if (ei)
@@ -734,7 +753,7 @@ struct eventfs_inode *eventfs_create_dir(const char *name, struct eventfs_inode/* Was the parent freed? */ if (list_empty (&ei->list)) { -free_ei (ei); +cleanup_ei (ei); ei =NULL ; }return ei;
@@ -835,7 +854,7 @@ struct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentryreturn ei; fail: -free_ei (ei); +cleanup_ei (ei);tracefs_failed_creating (dentry);return ERR_PTR (-ENOMEM ); }
include/linux/tracefs.h +3 -0
@@ -62,6 +62,8 @@ struct eventfs_file;typedef int (*eventfs_callback )(const char *name,umode_t *mode,void **data,const struct file_operations **fops); +typedef void (*eventfs_release )(const char *name,void *data); +/** * struct eventfs_entry - dynamically created eventfs file call back handler * @name: Then name of the dynamic file in an eventfs directory
@@ -72,6 +74,7 @@ typedef int (*eventfs_callback)(const char *name, umode_t *mode, void **data,struct eventfs_entry {const char *name;eventfs_callback callback; +eventfs_release release; };struct eventfs_inode ;
kernel/trace/trace_events.c +12 -0
@@ -2552,6 +2552,14 @@ static int event_callback(const char *name, umode_t *mode, void **data,return 0 ; } +/* The file is incremented on creation and freeing the enable file decrements it */ +static void event_release (const char *name,void *data) +{ +struct trace_event_file *file = data; + +event_file_put (file); +} +static int event_create_dir (struct eventfs_inode *parent,struct trace_event_file *file) {
@@ -2566,6 +2574,7 @@ event_create_dir(struct eventfs_inode *parent, struct trace_event_file *file) { .name = "enable", .callback = event_callback, + .release = event_release, }, { .name = "filter",
@@ -2634,6 +2643,9 @@ event_create_dir(struct eventfs_inode *parent, struct trace_event_file *file)return ret; } +/* Gets decremented on freeing of the "enable" file */ +event_file_get (file); +return 0 ; }
Signature · 1 line
2.43.0
Steven Rostedt
maintainerPATCH 4/9 eventfs: Free all of the eventfs_inode after RCU
In reply to Steven RostedtFrom: "Steven Rostedt (Google)" <[email protected]>
The freeing of eventfs_inode via a kfree_rcu() callback. But the content of the eventfs_inode was being freed after the last kref. This is dangerous, as changes are being made that can access the content of an eventfs_inode from an RCU loop.
Instead of using kfree_rcu() use call_rcu() that calls a function to do all the freeing of the eventfs_inode after a RCU grace period has expired.
--- fs/tracefs/event_inode.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-)
fs/tracefs/event_inode.c +16 -9
@@ -73,6 +73,21 @@ enum {#define EVENTFS_MODE_MASK (EVENTFS_SAVE_MODE - 1) +static void free_ei_rcu (struct rcu_head *rcu) +{ + struct eventfs_inode *ei = container_of(rcu, struct eventfs_inode, rcu); +struct eventfs_root_inode *rei; + +kfree (ei->entry_attrs); +kfree_const (ei->name); +if (ei->is_events) { + rei =get_root_inode (ei); +kfree (rei); + }else { +kfree (ei); + } +} +/* * eventfs_inode reference count management. *
@@ -85,7 +100,6 @@ static void release_ei(struct kref *ref) { struct eventfs_inode *ei = container_of(ref, struct eventfs_inode, kref);const struct eventfs_entry *entry; -struct eventfs_root_inode *rei;WARN_ON_ONCE (!ei->is_freed);
@@ -95,14 +109,7 @@ static void release_ei(struct kref *ref) entry->release (entry->name, ei->data); } -kfree (ei->entry_attrs); -kfree_const (ei->name); -if (ei->is_events) { - rei =get_root_inode (ei); -kfree_rcu (rei, ei.rcu); - }else { -kfree_rcu (ei, rcu); - } +call_rcu (&ei->rcu, free_ei_rcu); }static inline void put_ei (struct eventfs_inode *ei)
Signature · 1 line
2.43.0
Steven Rostedt
maintainerPATCH 5/9 tracefs: Reset permissions on remount if permissions are options
In reply to Steven RostedtFrom: "Steven Rostedt (Google)" <[email protected]>
There's an inconsistency with the way permissions are handled in tracefs. Because the permissions are generated when accessed, they default to the root inode's permission if they were never set by the user. If the user sets the permissions, then a flag is set and the permissions are saved via the inode (for tracefs files) or an internal attribute field (for eventfs).
But if a remount happens that specify the permissions, all the files that were not changed by the user gets updated, but the ones that were are not. If the user were to remount the file system with a given permission, then all files and directories within that file system should be updated.
This can cause security issues if a file's permission was updated but the admin forgot about it. They could incorrectly think that remounting with permissions set would update all files, but miss some.
For example:
# cd /sys/kernel/tracing # chgrp 1002 current_tracer # ls -l [..] -rw-r----- 1 root root 0 May 1 21:25 buffer_size_kb -rw-r----- 1 root root 0 May 1 21:25 buffer_subbuf_size_kb -r--r----- 1 root root 0 May 1 21:25 buffer_total_size_kb -rw-r----- 1 root lkp 0 May 1 21:25 current_tracer -rw-r----- 1 root root 0 May 1 21:25 dynamic_events -r--r----- 1 root root 0 May 1 21:25 dyn_ftrace_total_info -r--r----- 1 root root 0 May 1 21:25 enabled_functions
Where current_tracer now has group "lkp".
# mount -o remount,gid=1001 . # ls -l -rw-r----- 1 root tracing 0 May 1 21:25 buffer_size_kb -rw-r----- 1 root tracing 0 May 1 21:25 buffer_subbuf_size_kb -r--r----- 1 root tracing 0 May 1 21:25 buffer_total_size_kb -rw-r----- 1 root lkp 0 May 1 21:25 current_tracer -rw-r----- 1 root tracing 0 May 1 21:25 dynamic_events -r--r----- 1 root tracing 0 May 1 21:25 dyn_ftrace_total_info -r--r----- 1 root tracing 0 May 1 21:25 enabled_functions
Everything changed but the "current_tracer".
Add a new link list that keeps track of all the tracefs_inodes which has the permission flags that tell if the file/dir should use the root inode's permission or not. Then on remount, clear all the flags so that the default behavior of using the root inode's permission is done for all files and directories.
--- fs/tracefs/event_inode.c | 29 ++++++++++++++++++ fs/tracefs/inode.c | 65 +++++++++++++++++++++++++++++++++++++++- fs/tracefs/internal.h | 7 ++++- 3 files changed, 99 insertions(+), 2 deletions(-)
fs/tracefs/event_inode.c +29 -0
@@ -308,6 +308,35 @@ static const struct file_operations eventfs_file_operations = { .llseek = generic_file_llseek, }; +/* +* On a remount of tracefs, if UID or GID options are set, then +* the mount point inode permissions should be used. +* Reset the saved permission flags appropriately. +*/ +void eventfs_remount (struct tracefs_inode *ti,bool update_uid,bool update_gid) +{ +struct eventfs_inode *ei = ti->private; + +if (!ei) +return ; + +if (update_uid) + ei->attr.mode &= ~EVENTFS_SAVE_UID ; + +if (update_gid) + ei->attr.mode &= ~EVENTFS_SAVE_GID ; + +if (!ei->entry_attrs) +return ; + +for (int i =0 ; i < ei->nr_entries; i++) { +if (update_uid) + ei->entry_attrs[i].mode &= ~EVENTFS_SAVE_UID ; +if (update_gid) + ei->entry_attrs[i].mode &= ~EVENTFS_SAVE_GID ; + } +} +/* Return the evenfs_inode of the "events" directory */ static struct eventfs_inode *eventfs_find_events (struct dentry *dentry) {
fs/tracefs/inode.c +64 -1
103 lines not on this page · raw message
fs/tracefs/internal.h +6 -1
@@ -11,8 +11,12 @@ enum { };struct tracefs_inode { -struct inode vfs_inode; +union { +struct inode vfs_inode; +struct rcu_head rcu; + };/* The below gets initialized with memset_after(ti, 0, vfs_inode) */ +struct list_head list;unsigned long flags;void *private; };
@@ -73,6 +77,7 @@ struct dentry *tracefs_end_creating(struct dentry *dentry);struct dentry *tracefs_failed_creating (struct dentry *dentry);struct inode *tracefs_get_inode (struct super_block *sb); +void eventfs_remount (struct tracefs_inode *ti,bool update_uid,bool update_gid);void eventfs_d_release (struct dentry *dentry);#endif /* _TRACEFS_INTERNAL_H */
Signature · 1 line
2.43.0
Steven Rostedt
maintainerPATCH 6/9 tracefs: Still use mount point as default permissions for instances
In reply to Steven RostedtFrom: "Steven Rostedt (Google)" <[email protected]>
If the instances directory's permissions were never change, then have it and its children use the mount point permissions as the default.
Currently, the permissions of instance directories are determined by the instance directory's permissions itself. But if the tracefs file system is remounted and changes the permissions, the instance directory and its children should use the new permission.
But because both the instance directory and its children use the instance directory's inode for permissions, it misses the update.
To demonstrate this:
# cd /sys/kernel/tracing/ # mkdir instances/foo # ls -ld instances/foo drwxr-x--- 5 root root 0 May 1 19:07 instances/foo # ls -ld instances drwxr-x--- 3 root root 0 May 1 18:57 instances # ls -ld current_tracer -rw-r----- 1 root root 0 May 1 18:57 current_tracer
# mount -o remount,gid=1002 . # ls -ld instances drwxr-x--- 3 root root 0 May 1 18:57 instances # ls -ld instances/foo/ drwxr-x--- 5 root root 0 May 1 19:07 instances/foo/ # ls -ld current_tracer -rw-r----- 1 root lkp 0 May 1 18:57 current_tracer
Notice that changing the group id to that of "lkp" did not affect the instances directory nor its children. It should have been:
# ls -ld current_tracer -rw-r----- 1 root root 0 May 1 19:19 current_tracer # ls -ld instances/foo/ drwxr-x--- 5 root root 0 May 1 19:25 instances/foo/ # ls -ld instances drwxr-x--- 3 root root 0 May 1 19:19 instances
# mount -o remount,gid=1002 . # ls -ld current_tracer -rw-r----- 1 root lkp 0 May 1 19:19 current_tracer # ls -ld instances drwxr-x--- 3 root lkp 0 May 1 19:19 instances # ls -ld instances/foo/ drwxr-x--- 5 root lkp 0 May 1 19:25 instances/foo/
Where all files were updated by the remount gid update.
--- fs/tracefs/inode.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-)
fs/tracefs/inode.c +25 -2
@@ -180,16 +180,39 @@ static void set_tracefs_inode_owner(struct inode *inode) {struct tracefs_inode *ti =get_tracefs (inode);struct inode *root_inode = ti->private; +kuid_t uid; +kgid_t gid; + + uid = root_inode->i_uid; + gid = root_inode->i_gid; + +/* +* If the root is not the mount point, then check the root's +* permissions. If it was never set, then default to the +* mount point. +*/ +if (root_inode !=d_inode (root_inode->i_sb->s_root)) { +struct tracefs_inode *rti; + + rti =get_tracefs (root_inode); + root_inode =d_inode (root_inode->i_sb->s_root); + +if (!(rti->flags &TRACEFS_UID_PERM_SET )) + uid = root_inode->i_uid; + +if (!(rti->flags &TRACEFS_GID_PERM_SET )) + gid = root_inode->i_gid; + }/* * If this inode has never been referenced, then update * the permissions to the superblock. */ if (!(ti->flags &TRACEFS_UID_PERM_SET )) - inode->i_uid = root_inode->i_uid; + inode->i_uid = uid;if (!(ti->flags &TRACEFS_GID_PERM_SET )) - inode->i_gid = root_inode->i_gid; + inode->i_gid = gid; } static int tracefs_permission(struct mnt_idmap *idmap,
Signature · 1 line
2.43.0
Steven Rostedt
maintainerPATCH 7/9 eventfs: Do not differentiate the toplevel events directory
In reply to Steven RostedtFrom: "Steven Rostedt (Google)" <[email protected]>
The toplevel events directory is really no different than the events directory of instances. Having the two be different caused inconsistencies and made it harder to fix the permissions bugs.
Make all events directories act the same.
--- fs/tracefs/event_inode.c | 29 ++++++++--------------------- fs/tracefs/internal.h | 7 +++---- 2 files changed, 11 insertions(+), 25 deletions(-)
fs/tracefs/event_inode.c +8 -21
@@ -68,7 +68,6 @@ enum {EVENTFS_SAVE_MODE =BIT (16 ),EVENTFS_SAVE_UID =BIT (17 ), EVENTFS_SAVE_GID = BIT(18), - EVENTFS_TOPLEVEL = BIT(19), };#define EVENTFS_MODE_MASK (EVENTFS_SAVE_MODE - 1)
@@ -239,14 +238,10 @@ static int eventfs_set_attr(struct mnt_idmap *idmap, struct dentry *dentry,return ret; } -static void update_top_events_attr (struct eventfs_inode *ei,struct super_block *sb) +static void update_events_attr (struct eventfs_inode *ei,struct super_block *sb) {struct inode *root; -/* Only update if the "events" was on the top level */ -if (!ei || !(ei->attr.mode &EVENTFS_TOPLEVEL )) -return ; -/* Get the tracefs root inode. */ root =d_inode (sb->s_root); ei->attr.uid = root->i_uid;
@@ -259,10 +254,10 @@ static void set_top_events_ownership(struct inode *inode)struct eventfs_inode *ei = ti->private;/* The top events directory doesn't get automatically updated */ -if (!ei || !ei->is_events || !(ei->attr.mode &EVENTFS_TOPLEVEL )) +if (!ei || !ei->is_events)return ; -update_top_events_attr (ei, inode->i_sb); +update_events_attr (ei, inode->i_sb);if (!(ei->attr.mode &EVENTFS_SAVE_UID )) inode->i_uid = ei->attr.uid;
@@ -291,7 +286,7 @@ static int eventfs_permission(struct mnt_idmap *idmap,return generic_permission (idmap, inode, mask); } -static const struct inode_operations eventfs_root_dir_inode_operations = { +static const struct inode_operations eventfs_dir_inode_operations = { .lookup = eventfs_root_lookup, .setattr = eventfs_set_attr, .getattr = eventfs_get_attr,
@@ -359,7 +354,7 @@ static struct eventfs_inode *eventfs_find_events(struct dentry *dentry)// Walk upwards until you find the events inode }while (!ei->is_events); -update_top_events_attr (ei, dentry->d_sb); +update_events_attr (ei, dentry->d_sb);return ei; }
@@ -465,7 +460,7 @@ static struct dentry *lookup_dir_entry(struct dentry *dentry,update_inode_attr (dentry, inode, &ei->attr,S_IFDIR |S_IRWXU |S_IRUGO |S_IXUGO ); - inode->i_op = &eventfs_root_dir_inode_operations; + inode->i_op = &eventfs_dir_inode_operations; inode->i_fop = &eventfs_file_operations;/* All directories will have the same inode number */
@@ -845,14 +840,6 @@ struct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentry uid =d_inode (dentry->d_parent)->i_uid; gid =d_inode (dentry->d_parent)->i_gid; -/* -* If the events directory is of the top instance, then parent -* is NULL. Set the attr.mode to reflect this and its permissions will -* default to the tracefs root dentry. -*/ -if (!parent) - ei->attr.mode =EVENTFS_TOPLEVEL ; -/* This is used as the default ownership of the files and directories */ ei->attr.uid = uid; ei->attr.gid = gid;
@@ -861,13 +848,13 @@ struct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentryINIT_LIST_HEAD (&ei->list); ti =get_tracefs (inode); - ti->flags |=TRACEFS_EVENT_INODE |TRACEFS_EVENT_TOP_INODE ; + ti->flags |=TRACEFS_EVENT_INODE ; ti->private = ei; inode->i_mode =S_IFDIR |S_IRWXU |S_IRUGO |S_IXUGO ; inode->i_uid = uid; inode->i_gid = gid; - inode->i_op = &eventfs_root_dir_inode_operations; + inode->i_op = &eventfs_dir_inode_operations; inode->i_fop = &eventfs_file_operations; dentry->d_fsdata =get_ei (ei);
fs/tracefs/internal.h +3 -4
@@ -4,10 +4,9 @@enum {TRACEFS_EVENT_INODE =BIT (1 ), -TRACEFS_EVENT_TOP_INODE =BIT (2 ), -TRACEFS_GID_PERM_SET =BIT (3 ), -TRACEFS_UID_PERM_SET =BIT (4 ), -TRACEFS_INSTANCE_INODE =BIT (5 ), +TRACEFS_GID_PERM_SET =BIT (2 ), +TRACEFS_UID_PERM_SET =BIT (3 ), +TRACEFS_INSTANCE_INODE =BIT (4 ), };struct tracefs_inode {
Signature · 1 line
2.43.0
Steven Rostedt
maintainerPATCH 8/9 eventfs: Do not treat events directory different than other directories
In reply to Steven RostedtFrom: "Steven Rostedt (Google)" <[email protected]>
Treat the events directory the same as other directories when it comes to permissions. The events directory was considered different because it's dentry is persistent, whereas the other directory dentries are created when accessed. But the way tracefs now does its ownership by using the root dentry's permissions as the default permissions, the events directory can get out of sync when a remount is performed setting the group and user permissions.
Remove the special case for the events directory on setting the attributes. This allows the updates caused by remount to work properly as well as simplifies the code.
--- fs/tracefs/event_inode.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-)
fs/tracefs/event_inode.c +1 -15
@@ -206,21 +206,7 @@ static int eventfs_set_attr(struct mnt_idmap *idmap, struct dentry *dentry,* determined by the parent directory. */ if (dentry->d_inode->i_mode &S_IFDIR ) { -/* -* The events directory dentry is never freed, unless its -* part of an instance that is deleted. It's attr is the -* default for its child files and directories. -* Do not update it. It's not used for its own mode or ownership. -*/ -if (ei->is_events) { -/* But it still needs to know if it was modified */ -if (iattr->ia_valid &ATTR_UID ) - ei->attr.mode |=EVENTFS_SAVE_UID ; -if (iattr->ia_valid &ATTR_GID ) - ei->attr.mode |=EVENTFS_SAVE_GID ; - }else { -update_attr (&ei->attr, iattr); - } +update_attr (&ei->attr, iattr); }else { name = dentry->d_name.name;
Signature · 1 line
2.43.0
Steven Rostedt
maintainerPATCH 9/9 eventfs: Have "events" directory get permissions from its parent
In reply to Steven RostedtFrom: "Steven Rostedt (Google)" <[email protected]>
The events directory gets its permissions from the root inode. But this can cause an inconsistency if the instances directory changes its permissions, as the permissions of the created directories under it should inherit the permissions of the instances directory when directories under it are created.
Currently the behavior is:
# cd /sys/kernel/tracing # chgrp 1002 instances # mkdir instances/foo # ls -l instances/foo [..] -r--r----- 1 root lkp 0 May 1 18:55 buffer_total_size_kb -rw-r----- 1 root lkp 0 May 1 18:55 current_tracer -rw-r----- 1 root lkp 0 May 1 18:55 error_log drwxr-xr-x 1 root root 0 May 1 18:55 events --w------- 1 root lkp 0 May 1 18:55 free_buffer drwxr-x--- 2 root lkp 0 May 1 18:55 options drwxr-x--- 10 root lkp 0 May 1 18:55 per_cpu -rw-r----- 1 root lkp 0 May 1 18:55 set_event
All the files and directories under "foo" has the "lkp" group except the "events" directory. That's because its getting its default value from the mount point instead of its parent.
Have the "events" directory make its default value based on its parent's permissions. That now gives:
# ls -l instances/foo [..] -rw-r----- 1 root lkp 0 May 1 21:16 buffer_subbuf_size_kb -r--r----- 1 root lkp 0 May 1 21:16 buffer_total_size_kb -rw-r----- 1 root lkp 0 May 1 21:16 current_tracer -rw-r----- 1 root lkp 0 May 1 21:16 error_log drwxr-xr-x 1 root lkp 0 May 1 21:16 events --w------- 1 root lkp 0 May 1 21:16 free_buffer drwxr-x--- 2 root lkp 0 May 1 21:16 options drwxr-x--- 10 root lkp 0 May 1 21:16 per_cpu -rw-r----- 1 root lkp 0 May 1 21:16 set_event
--- fs/tracefs/event_inode.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-)
fs/tracefs/event_inode.c +24 -6
@@ -37,6 +37,7 @@ static DEFINE_MUTEX(eventfs_mutex);struct eventfs_root_inode {struct eventfs_inode ei; +struct inode *parent_inode;struct dentry *events_dir; };
@@ -226,12 +227,23 @@ static int eventfs_set_attr(struct mnt_idmap *idmap, struct dentry *dentry,static void update_events_attr (struct eventfs_inode *ei,struct super_block *sb) { -struct inode *root; +struct eventfs_root_inode *rei; +struct inode *parent; + + rei =get_root_inode (ei); + +/* Use the parent inode permissions unless root set its permissions */ + parent = rei->parent_inode; -/* Get the tracefs root inode. */ - root =d_inode (sb->s_root); - ei->attr.uid = root->i_uid; - ei->attr.gid = root->i_gid; +if (rei->ei.attr.mode &EVENTFS_SAVE_UID ) + ei->attr.uid = rei->ei.attr.uid; +else + ei->attr.uid = parent->i_uid; + +if (rei->ei.attr.mode &EVENTFS_SAVE_GID ) + ei->attr.gid = rei->ei.attr.gid; +else + ei->attr.gid = parent->i_gid; }static void set_top_events_ownership (struct inode *inode)
@@ -817,6 +829,7 @@ struct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentry// Note: we have a ref to the dentry from tracefs_start_creating() rei =get_root_inode (ei); rei->events_dir = dentry; + rei->parent_inode =d_inode (dentry->d_sb->s_root); ei->entries = entries; ei->nr_entries = size;
@@ -826,10 +839,15 @@ struct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentry uid =d_inode (dentry->d_parent)->i_uid; gid =d_inode (dentry->d_parent)->i_gid; -/* This is used as the default ownership of the files and directories */ ei->attr.uid = uid; ei->attr.gid = gid; +/* +* When the "events" directory is created, it takes on the +* permissions of its parent. But can be reset on remount. +*/ + ei->attr.mode |=EVENTFS_SAVE_UID |EVENTFS_SAVE_GID ; +INIT_LIST_HEAD (&ei->children);INIT_LIST_HEAD (&ei->list);
Signature · 1 line
2.43.0