Skip to content

Commit e92b99a

Browse files
committed
Merge tag 'trace-v6.9-rc6-2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace
Pull tracing and tracefs fixes from Steven Rostedt: - 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 tracing subsystem 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. This prevents races between freeing the descriptor and the opening of the eventfs file. - 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. * tag 'trace-v6.9-rc6-2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: eventfs: Have "events" directory get permissions from its parent eventfs: Do not treat events directory different than other directories eventfs: Do not differentiate the toplevel events directory tracefs: Still use mount point as default permissions for instances tracefs: Reset permissions on remount if permissions are options eventfs: Free all of the eventfs_inode after RCU eventfs/tracing: Add callback for release of an eventfs_inode
2 parents 4fbcf58 + d57cf30 commit e92b99a

File tree

5 files changed

+210
-59
lines changed

5 files changed

+210
-59
lines changed

fs/tracefs/event_inode.c

Lines changed: 97 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ static DEFINE_MUTEX(eventfs_mutex);
3737

3838
struct eventfs_root_inode {
3939
struct eventfs_inode ei;
40+
struct inode *parent_inode;
4041
struct dentry *events_dir;
4142
};
4243

@@ -68,11 +69,25 @@ enum {
6869
EVENTFS_SAVE_MODE = BIT(16),
6970
EVENTFS_SAVE_UID = BIT(17),
7071
EVENTFS_SAVE_GID = BIT(18),
71-
EVENTFS_TOPLEVEL = BIT(19),
7272
};
7373

7474
#define EVENTFS_MODE_MASK (EVENTFS_SAVE_MODE - 1)
7575

76+
static void free_ei_rcu(struct rcu_head *rcu)
77+
{
78+
struct eventfs_inode *ei = container_of(rcu, struct eventfs_inode, rcu);
79+
struct eventfs_root_inode *rei;
80+
81+
kfree(ei->entry_attrs);
82+
kfree_const(ei->name);
83+
if (ei->is_events) {
84+
rei = get_root_inode(ei);
85+
kfree(rei);
86+
} else {
87+
kfree(ei);
88+
}
89+
}
90+
7691
/*
7792
* eventfs_inode reference count management.
7893
*
@@ -84,18 +99,17 @@ enum {
8499
static void release_ei(struct kref *ref)
85100
{
86101
struct eventfs_inode *ei = container_of(ref, struct eventfs_inode, kref);
87-
struct eventfs_root_inode *rei;
102+
const struct eventfs_entry *entry;
88103

89104
WARN_ON_ONCE(!ei->is_freed);
90105

91-
kfree(ei->entry_attrs);
92-
kfree_const(ei->name);
93-
if (ei->is_events) {
94-
rei = get_root_inode(ei);
95-
kfree_rcu(rei, ei.rcu);
96-
} else {
97-
kfree_rcu(ei, rcu);
106+
for (int i = 0; i < ei->nr_entries; i++) {
107+
entry = &ei->entries[i];
108+
if (entry->release)
109+
entry->release(entry->name, ei->data);
98110
}
111+
112+
call_rcu(&ei->rcu, free_ei_rcu);
99113
}
100114

101115
static inline void put_ei(struct eventfs_inode *ei)
@@ -112,6 +126,18 @@ static inline void free_ei(struct eventfs_inode *ei)
112126
}
113127
}
114128

129+
/*
130+
* Called when creation of an ei fails, do not call release() functions.
131+
*/
132+
static inline void cleanup_ei(struct eventfs_inode *ei)
133+
{
134+
if (ei) {
135+
/* Set nr_entries to 0 to prevent release() function being called */
136+
ei->nr_entries = 0;
137+
free_ei(ei);
138+
}
139+
}
140+
115141
static inline struct eventfs_inode *get_ei(struct eventfs_inode *ei)
116142
{
117143
if (ei)
@@ -181,21 +207,7 @@ static int eventfs_set_attr(struct mnt_idmap *idmap, struct dentry *dentry,
181207
* determined by the parent directory.
182208
*/
183209
if (dentry->d_inode->i_mode & S_IFDIR) {
184-
/*
185-
* The events directory dentry is never freed, unless its
186-
* part of an instance that is deleted. It's attr is the
187-
* default for its child files and directories.
188-
* Do not update it. It's not used for its own mode or ownership.
189-
*/
190-
if (ei->is_events) {
191-
/* But it still needs to know if it was modified */
192-
if (iattr->ia_valid & ATTR_UID)
193-
ei->attr.mode |= EVENTFS_SAVE_UID;
194-
if (iattr->ia_valid & ATTR_GID)
195-
ei->attr.mode |= EVENTFS_SAVE_GID;
196-
} else {
197-
update_attr(&ei->attr, iattr);
198-
}
210+
update_attr(&ei->attr, iattr);
199211

200212
} else {
201213
name = dentry->d_name.name;
@@ -213,18 +225,25 @@ static int eventfs_set_attr(struct mnt_idmap *idmap, struct dentry *dentry,
213225
return ret;
214226
}
215227

216-
static void update_top_events_attr(struct eventfs_inode *ei, struct super_block *sb)
228+
static void update_events_attr(struct eventfs_inode *ei, struct super_block *sb)
217229
{
218-
struct inode *root;
230+
struct eventfs_root_inode *rei;
231+
struct inode *parent;
219232

220-
/* Only update if the "events" was on the top level */
221-
if (!ei || !(ei->attr.mode & EVENTFS_TOPLEVEL))
222-
return;
233+
rei = get_root_inode(ei);
223234

224-
/* Get the tracefs root inode. */
225-
root = d_inode(sb->s_root);
226-
ei->attr.uid = root->i_uid;
227-
ei->attr.gid = root->i_gid;
235+
/* Use the parent inode permissions unless root set its permissions */
236+
parent = rei->parent_inode;
237+
238+
if (rei->ei.attr.mode & EVENTFS_SAVE_UID)
239+
ei->attr.uid = rei->ei.attr.uid;
240+
else
241+
ei->attr.uid = parent->i_uid;
242+
243+
if (rei->ei.attr.mode & EVENTFS_SAVE_GID)
244+
ei->attr.gid = rei->ei.attr.gid;
245+
else
246+
ei->attr.gid = parent->i_gid;
228247
}
229248

230249
static void set_top_events_ownership(struct inode *inode)
@@ -233,10 +252,10 @@ static void set_top_events_ownership(struct inode *inode)
233252
struct eventfs_inode *ei = ti->private;
234253

235254
/* The top events directory doesn't get automatically updated */
236-
if (!ei || !ei->is_events || !(ei->attr.mode & EVENTFS_TOPLEVEL))
255+
if (!ei || !ei->is_events)
237256
return;
238257

239-
update_top_events_attr(ei, inode->i_sb);
258+
update_events_attr(ei, inode->i_sb);
240259

241260
if (!(ei->attr.mode & EVENTFS_SAVE_UID))
242261
inode->i_uid = ei->attr.uid;
@@ -265,7 +284,7 @@ static int eventfs_permission(struct mnt_idmap *idmap,
265284
return generic_permission(idmap, inode, mask);
266285
}
267286

268-
static const struct inode_operations eventfs_root_dir_inode_operations = {
287+
static const struct inode_operations eventfs_dir_inode_operations = {
269288
.lookup = eventfs_root_lookup,
270289
.setattr = eventfs_set_attr,
271290
.getattr = eventfs_get_attr,
@@ -282,6 +301,35 @@ static const struct file_operations eventfs_file_operations = {
282301
.llseek = generic_file_llseek,
283302
};
284303

304+
/*
305+
* On a remount of tracefs, if UID or GID options are set, then
306+
* the mount point inode permissions should be used.
307+
* Reset the saved permission flags appropriately.
308+
*/
309+
void eventfs_remount(struct tracefs_inode *ti, bool update_uid, bool update_gid)
310+
{
311+
struct eventfs_inode *ei = ti->private;
312+
313+
if (!ei)
314+
return;
315+
316+
if (update_uid)
317+
ei->attr.mode &= ~EVENTFS_SAVE_UID;
318+
319+
if (update_gid)
320+
ei->attr.mode &= ~EVENTFS_SAVE_GID;
321+
322+
if (!ei->entry_attrs)
323+
return;
324+
325+
for (int i = 0; i < ei->nr_entries; i++) {
326+
if (update_uid)
327+
ei->entry_attrs[i].mode &= ~EVENTFS_SAVE_UID;
328+
if (update_gid)
329+
ei->entry_attrs[i].mode &= ~EVENTFS_SAVE_GID;
330+
}
331+
}
332+
285333
/* Return the evenfs_inode of the "events" directory */
286334
static struct eventfs_inode *eventfs_find_events(struct dentry *dentry)
287335
{
@@ -304,7 +352,7 @@ static struct eventfs_inode *eventfs_find_events(struct dentry *dentry)
304352
// Walk upwards until you find the events inode
305353
} while (!ei->is_events);
306354

307-
update_top_events_attr(ei, dentry->d_sb);
355+
update_events_attr(ei, dentry->d_sb);
308356

309357
return ei;
310358
}
@@ -410,7 +458,7 @@ static struct dentry *lookup_dir_entry(struct dentry *dentry,
410458
update_inode_attr(dentry, inode, &ei->attr,
411459
S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO);
412460

413-
inode->i_op = &eventfs_root_dir_inode_operations;
461+
inode->i_op = &eventfs_dir_inode_operations;
414462
inode->i_fop = &eventfs_file_operations;
415463

416464
/* All directories will have the same inode number */
@@ -734,7 +782,7 @@ struct eventfs_inode *eventfs_create_dir(const char *name, struct eventfs_inode
734782

735783
/* Was the parent freed? */
736784
if (list_empty(&ei->list)) {
737-
free_ei(ei);
785+
cleanup_ei(ei);
738786
ei = NULL;
739787
}
740788
return ei;
@@ -781,6 +829,7 @@ struct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentry
781829
// Note: we have a ref to the dentry from tracefs_start_creating()
782830
rei = get_root_inode(ei);
783831
rei->events_dir = dentry;
832+
rei->parent_inode = d_inode(dentry->d_sb->s_root);
784833

785834
ei->entries = entries;
786835
ei->nr_entries = size;
@@ -790,29 +839,26 @@ struct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentry
790839
uid = d_inode(dentry->d_parent)->i_uid;
791840
gid = d_inode(dentry->d_parent)->i_gid;
792841

793-
/*
794-
* If the events directory is of the top instance, then parent
795-
* is NULL. Set the attr.mode to reflect this and its permissions will
796-
* default to the tracefs root dentry.
797-
*/
798-
if (!parent)
799-
ei->attr.mode = EVENTFS_TOPLEVEL;
800-
801-
/* This is used as the default ownership of the files and directories */
802842
ei->attr.uid = uid;
803843
ei->attr.gid = gid;
804844

845+
/*
846+
* When the "events" directory is created, it takes on the
847+
* permissions of its parent. But can be reset on remount.
848+
*/
849+
ei->attr.mode |= EVENTFS_SAVE_UID | EVENTFS_SAVE_GID;
850+
805851
INIT_LIST_HEAD(&ei->children);
806852
INIT_LIST_HEAD(&ei->list);
807853

808854
ti = get_tracefs(inode);
809-
ti->flags |= TRACEFS_EVENT_INODE | TRACEFS_EVENT_TOP_INODE;
855+
ti->flags |= TRACEFS_EVENT_INODE;
810856
ti->private = ei;
811857

812858
inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO;
813859
inode->i_uid = uid;
814860
inode->i_gid = gid;
815-
inode->i_op = &eventfs_root_dir_inode_operations;
861+
inode->i_op = &eventfs_dir_inode_operations;
816862
inode->i_fop = &eventfs_file_operations;
817863

818864
dentry->d_fsdata = get_ei(ei);
@@ -835,7 +881,7 @@ struct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentry
835881
return ei;
836882

837883
fail:
838-
free_ei(ei);
884+
cleanup_ei(ei);
839885
tracefs_failed_creating(dentry);
840886
return ERR_PTR(-ENOMEM);
841887
}

0 commit comments

Comments
 (0)