Skip to contentSkip to conversation

tracefs/eventfs: Fix failed second run of test_ownership

PATCH New Steven Rostedt 1 participant 5 messages May 23, 2024

Subsystems (2)
lore
Share & tools
lore

Steven Rostedt

author maintainer
#0 lore raw

v2 cover tracefs/eventfs: Fix failed second run of test_ownership

The test_ownership test of the kselftests was failing again. That's because the original fix was incorrect and a fix to a race condition showed how the original fix was broken.

Instead of using tracefs_inodes to find the eventfs_inode that needs to be reset on remount, use the "events" directory descriptor to descend into its files and directories to catch all changes.

Changes since v1: https://lore.kernel.org/linux-trace-kernel/[email protected]

- Added other fixes underneath and rebased it on:
https://lore.kernel.org/lkml/[email protected]/

- The real fix is to not use the tracefs_inodes to find the eventfs_inodes that need to be cleared on remount. Instead, the events descriptor needs to be used to descend its directories and files to update their attributes

- The d_iput callback logic was misplaced. It should be done in the drop_inode callback.

git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace.git
eventfs/urgent

Head SHA1: 41b7db11bcac4638fa489c58d35e7d2146b665ab

Steven Rostedt (Google) (4):
eventfs: Keep the directories from having the same inode number as files
tracefs: Update inode permissions on remount
eventfs: Update all the eventfs_inodes from the events descriptor
tracefs: Clear EVENT_INODE flag in tracefs_drop_inode()

----

 fs/tracefs/event_inode.c | 57 +++++++++++++++++++++++++++++++++++++-----------
 fs/tracefs/inode.c       | 48 ++++++++++++++++++++++++----------------
 2 files changed, 73 insertions(+), 32 deletions(-)
author maintainer
#1 lore raw

PATCH v2 1/4 eventfs: Keep the directories from having the same inode number as files

From: "Steven Rostedt (Google)" <[email protected]>

The directories require unique inode numbers but all the eventfs files have the same inode number. Prevent the directories from having the same inode numbers as the files as that can confuse some tooling.

Fixes 834bf76add3e6 ("eventfs: Save directory inodes in the eventfs_inode structure")
---
 fs/tracefs/event_inode.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
fs/tracefs/event_inode.c +5 -1
index 0256afdd4acf..55a40a730b10 100644
@@ -50,8 +50,12 @@ static struct eventfs_root_inode *get_root_inode(struct eventfs_inode *ei)
 /* Just try to make something consistent and unique */
 static int eventfs_dir_ino(struct eventfs_inode *ei)
 {
-	if (!ei->ino)
+	if (!ei->ino) {
 		ei->ino = get_next_ino();
+		/* Must not have the file inode number */
+		if (ei->ino == EVENTFS_FILE_INODE_INO)
+			ei->ino = get_next_ino();
+	}
 
 	return ei->ino;
 }
Signature · 1 line
2.43.0
author maintainer
#2 lore raw

PATCH v2 2/4 tracefs: Update inode permissions on remount

In reply to Steven Rostedt

From: "Steven Rostedt (Google)" <[email protected]>

When a remount happens, if a gid or uid is specified update the inodes to have the same gid and uid. This will allow the simplification of the permissions logic for the dynamically created files and directories.

Fixes baa23a8d4360d ("tracefs: Reset permissions on remount if permissions are options")
---
 fs/tracefs/event_inode.c | 17 +++++++++++++----
 fs/tracefs/inode.c       | 15 ++++++++++++---
 2 files changed, 25 insertions(+), 7 deletions(-)
fs/tracefs/event_inode.c +13 -4
index 55a40a730b10..5dfb1ccd56ea 100644
@@ -317,20 +317,29 @@ void eventfs_remount(struct tracefs_inode *ti, bool update_uid, bool update_gid)
 	if (!ei)
 		return;
 
-	if (update_uid)
+	if (update_uid) {
 		ei->attr.mode &= ~EVENTFS_SAVE_UID;
+		ei->attr.uid = ti->vfs_inode.i_uid;
+	}
+
 
-	if (update_gid)
+	if (update_gid) {
 		ei->attr.mode &= ~EVENTFS_SAVE_GID;
+		ei->attr.gid = ti->vfs_inode.i_gid;
+	}
 
 	if (!ei->entry_attrs)
 		return;
 
 	for (int i = 0; i < ei->nr_entries; i++) {
-		if (update_uid)
+		if (update_uid) {
 			ei->entry_attrs[i].mode &= ~EVENTFS_SAVE_UID;
-		if (update_gid)
+			ei->entry_attrs[i].uid = ti->vfs_inode.i_uid;
+		}
+		if (update_gid) {
 			ei->entry_attrs[i].mode &= ~EVENTFS_SAVE_GID;
+			ei->entry_attrs[i].gid = ti->vfs_inode.i_gid;
+		}
 	}
 }
 
fs/tracefs/inode.c +12 -3
index a827f6a716c4..9252e0d78ea2 100644
@@ -373,12 +373,21 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
 
 		rcu_read_lock();
 		list_for_each_entry_rcu(ti, &tracefs_inodes, list) {
-			if (update_uid)
+			if (update_uid) {
 				ti->flags &= ~TRACEFS_UID_PERM_SET;
+				ti->vfs_inode.i_uid = fsi->uid;
+			}
 
-			if (update_gid)
+			if (update_gid) {
 				ti->flags &= ~TRACEFS_GID_PERM_SET;
-
+				ti->vfs_inode.i_gid = fsi->gid;
+			}
+
+			/*
+			 * Note, the above ti->vfs_inode updates are
+			 * used in eventfs_remount() so they must come
+			 * before calling it.
+			 */
 			if (ti->flags & TRACEFS_EVENT_INODE)
 				eventfs_remount(ti, update_uid, update_gid);
 		}
Signature · 1 line
2.43.0
author maintainer
#3 lore raw

PATCH v2 3/4 eventfs: Update all the eventfs_inodes from the events descriptor

In reply to Steven Rostedt

From: "Steven Rostedt (Google)" <[email protected]>

The change to update the permissions of the eventfs_inode had the misconception that using the tracefs_inode would find all the eventfs_inodes that have been updated and reset them on remount. The problem with this approach is that the eventfs_inodes are freed when they are no longer used (basically the reason the eventfs system exists). When they are freed, the updated eventfs_inodes are not reset on a remount because their tracefs_inodes have been freed.

Instead, since the events directory eventfs_inode always has a tracefs_inode pointing to it (it is not freed when finished), and the events directory has a link to all its children, have the eventfs_remount() function only operate on the events eventfs_inode and have it descend into its children updating their uid and gids.

Fixes baa23a8d4360d ("tracefs: Reset permissions on remount if permissions are options")
---
 fs/tracefs/event_inode.c | 44 ++++++++++++++++++++++++++++------------
 1 file changed, 31 insertions(+), 13 deletions(-)
fs/tracefs/event_inode.c +31 -13
index 5dfb1ccd56ea..129d0f54ba62 100644
@@ -305,27 +305,27 @@ 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)
+static void eventfs_set_attrs(struct eventfs_inode *ei, bool update_uid, kuid_t uid,
+			      bool update_gid, kgid_t gid, int level)
 {
-	struct eventfs_inode *ei = ti->private;
+	struct eventfs_inode *ei_child;
 
-	if (!ei)
+	/* Update events/<system>/<event> */
+	if (WARN_ON_ONCE(level > 3))
 		return;
 
 	if (update_uid) {
 		ei->attr.mode &= ~EVENTFS_SAVE_UID;
-		ei->attr.uid = ti->vfs_inode.i_uid;
+		ei->attr.uid = uid;
 	}
 
-
 	if (update_gid) {
 		ei->attr.mode &= ~EVENTFS_SAVE_GID;
-		ei->attr.gid = ti->vfs_inode.i_gid;
+		ei->attr.gid = gid;
+	}
+
+	list_for_each_entry(ei_child, &ei->children, list) {
+		eventfs_set_attrs(ei_child, update_uid, uid, update_gid, gid, level + 1);
 	}
 
 	if (!ei->entry_attrs)
@@ -334,13 +334,31 @@ void eventfs_remount(struct tracefs_inode *ti, bool update_uid, bool update_gid)
 	for (int i = 0; i < ei->nr_entries; i++) {
 		if (update_uid) {
 			ei->entry_attrs[i].mode &= ~EVENTFS_SAVE_UID;
-			ei->entry_attrs[i].uid = ti->vfs_inode.i_uid;
+			ei->entry_attrs[i].uid = uid;
 		}
 		if (update_gid) {
 			ei->entry_attrs[i].mode &= ~EVENTFS_SAVE_GID;
-			ei->entry_attrs[i].gid = ti->vfs_inode.i_gid;
+			ei->entry_attrs[i].gid = gid;
 		}
 	}
+
+}
+
+/*
+ * 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;
+
+	/* Only the events directory does the updates */
+	if (!ei || !ei->is_events || ei->is_freed)
+		return;
+
+	eventfs_set_attrs(ei, update_uid, ti->vfs_inode.i_uid,
+			  update_gid, ti->vfs_inode.i_gid, 0);
 }
 
 /* Return the evenfs_inode of the "events" directory */
Signature · 1 line
2.43.0
author maintainer
#4 lore raw

PATCH v2 4/4 tracefs: Clear EVENT_INODE flag in tracefs_drop_inode()

In reply to Steven Rostedt

From: "Steven Rostedt (Google)" <[email protected]>

When the inode is being dropped from the dentry, the TRACEFS_EVENT_INODE flag needs to be cleared to prevent a remount from calling eventfs_remount() on the tracefs_inode private data. There's a race between the inode is dropped (and the dentry freed) to where the inode is actually freed. If a remount happens between the two, the eventfs_inode could be accessed after it is freed (only the dentry keeps a ref count on it).

Currently the TRACEFS_EVENT_INODE flag is cleared from the dentry iput() function. But this is incorrect, as it is possible that the inode has another reference to it. The flag should only be cleared when the inode is really being dropped and has no more references. That happens in the drop_inode callback of the inode, as that gets called when the last reference of the inode is released.

Remove the tracefs_d_iput() function and move its logic to the more appropriate tracefs_drop_inode() callback function.

Fixes baa23a8d4360d ("tracefs: Reset permissions on remount if permissions are options")
---
 fs/tracefs/inode.c | 33 +++++++++++++++++----------------
 1 file changed, 17 insertions(+), 16 deletions(-)
fs/tracefs/inode.c +17 -16
index 9252e0d78ea2..7c29f4afc23d 100644
@@ -426,10 +426,26 @@ static int tracefs_show_options(struct seq_file *m, struct dentry *root)
 	return 0;
 }
 
+static int tracefs_drop_inode(struct inode *inode)
+{
+	struct tracefs_inode *ti = get_tracefs(inode);
+
+	/*
+	 * This inode is being freed and cannot be used for
+	 * eventfs. Clear the flag so that it doesn't call into
+	 * eventfs during the remount flag updates. The eventfs_inode
+	 * gets freed after an RCU cycle, so the content will still
+	 * be safe if the iteration is going on now.
+	 */
+	ti->flags &= ~TRACEFS_EVENT_INODE;
+
+	return 1;
+}
+
 static const struct super_operations tracefs_super_operations = {
 	.alloc_inode    = tracefs_alloc_inode,
 	.free_inode     = tracefs_free_inode,
-	.drop_inode     = generic_delete_inode,
+	.drop_inode     = tracefs_drop_inode,
 	.statfs		= simple_statfs,
 	.show_options	= tracefs_show_options,
 };
@@ -455,22 +471,7 @@ static int tracefs_d_revalidate(struct dentry *dentry, unsigned int flags)
 	return !(ei && ei->is_freed);
 }
 
-static void tracefs_d_iput(struct dentry *dentry, struct inode *inode)
-{
-	struct tracefs_inode *ti = get_tracefs(inode);
-
-	/*
-	 * This inode is being freed and cannot be used for
-	 * eventfs. Clear the flag so that it doesn't call into
-	 * eventfs during the remount flag updates. The eventfs_inode
-	 * gets freed after an RCU cycle, so the content will still
-	 * be safe if the iteration is going on now.
-	 */
-	ti->flags &= ~TRACEFS_EVENT_INODE;
-}
-
 static const struct dentry_operations tracefs_dentry_operations = {
-	.d_iput = tracefs_d_iput,
 	.d_revalidate = tracefs_d_revalidate,
 	.d_release = tracefs_d_release,
 };
Signature · 1 line
2.43.0