Patch "eventfs: Delete eventfs_inode when the last dentry is freed" has been added to the 6.6-stable tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is a note to let you know that I've just added the patch titled

    eventfs: Delete eventfs_inode when the last dentry is freed

to the 6.6-stable tree which can be found at:
    http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary

The filename of the patch is:
     eventfs-delete-eventfs_inode-when-the-last-dentry-is-freed.patch
and it can be found in the queue-6.6 subdirectory.

If you, or anyone else, feels it should not be added to the stable tree,
please let <stable@xxxxxxxxxxxxxxx> know about it.


>From SRS0=eEWY=JP=rostedt.homelinux.com=rostedt@xxxxxxxxxx Tue Feb  6 13:10:54 2024
From: Steven Rostedt <rostedt@xxxxxxxxxxx>
Date: Tue, 06 Feb 2024 07:09:26 -0500
Subject: eventfs: Delete eventfs_inode when the last dentry is freed
To: linux-kernel@xxxxxxxxxxxxxxx, stable@xxxxxxxxxxxxxxx
Cc: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx>, Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>, Sasha Levin <sashal@xxxxxxxxxx>, Masami Hiramatsu <mhiramat@xxxxxxxxxx>, Mark Rutland <mark.rutland@xxxxxxx>, Mathieu Desnoyers <mathieu.desnoyers@xxxxxxxxxxxx>, Andrew Morton <akpm@xxxxxxxxxxxxxxxxxxxx>, Ajay Kaher <akaher@xxxxxxxxxx>
Message-ID: <20240206120949.792406858@xxxxxxxxxxxxxxxxxxxxx>

From: "Steven Rostedt (Google)" <rostedt@xxxxxxxxxxx>

commit 020010fbfa202aa528a52743eba4ab0da3400a4e upstream.

There exists a race between holding a reference of an eventfs_inode dentry
and the freeing of the eventfs_inode. If user space has a dentry held long
enough, it may still be able to access the dentry's eventfs_inode after it
has been freed.

To prevent this, have he eventfs_inode freed via the last dput() (or via
RCU if the eventfs_inode does not have a dentry).

This means reintroducing the eventfs_inode del_list field at a temporary
place to put the eventfs_inode. It needs to mark it as freed (via the
list) but also must invalidate the dentry immediately as the return from
eventfs_remove_dir() expects that they are. But the dentry invalidation
must not be called under the eventfs_mutex, so it must be done after the
eventfs_inode is marked as free (put on a deletion list).

Link: https://lkml.kernel.org/r/20231101172650.123479767@xxxxxxxxxxx

Cc: stable@xxxxxxxxxxxxxxx
Cc: Masami Hiramatsu <mhiramat@xxxxxxxxxx>
Cc: Mark Rutland <mark.rutland@xxxxxxx>
Cc: Andrew Morton <akpm@xxxxxxxxxxxxxxxxxxxx>
Cc: Ajay Kaher <akaher@xxxxxxxxxx>
Fixes: 5bdcd5f5331a2 ("eventfs: Implement removal of meta data from eventfs")
Signed-off-by: Steven Rostedt (Google) <rostedt@xxxxxxxxxxx>
Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>
---
 fs/tracefs/event_inode.c |  146 +++++++++++++++++++++--------------------------
 fs/tracefs/internal.h    |    2 
 2 files changed, 69 insertions(+), 79 deletions(-)

--- a/fs/tracefs/event_inode.c
+++ b/fs/tracefs/event_inode.c
@@ -85,8 +85,7 @@ static int eventfs_set_attr(struct mnt_i
 
 	mutex_lock(&eventfs_mutex);
 	ei = dentry->d_fsdata;
-	/* The LSB is set when the eventfs_inode is being freed */
-	if (((unsigned long)ei & 1UL) || ei->is_freed) {
+	if (ei->is_freed) {
 		/* Do not allow changes if the event is about to be removed. */
 		mutex_unlock(&eventfs_mutex);
 		return -ENODEV;
@@ -276,35 +275,17 @@ static void free_ei(struct eventfs_inode
 void eventfs_set_ei_status_free(struct tracefs_inode *ti, struct dentry *dentry)
 {
 	struct tracefs_inode *ti_parent;
-	struct eventfs_inode *ei_child, *tmp;
 	struct eventfs_inode *ei;
 	int i;
 
 	/* The top level events directory may be freed by this */
 	if (unlikely(ti->flags & TRACEFS_EVENT_TOP_INODE)) {
-		LIST_HEAD(ef_del_list);
-
 		mutex_lock(&eventfs_mutex);
-
 		ei = ti->private;
-
-		/* Record all the top level files */
-		list_for_each_entry_srcu(ei_child, &ei->children, list,
-					 lockdep_is_held(&eventfs_mutex)) {
-			list_add_tail(&ei_child->del_list, &ef_del_list);
-		}
-
 		/* Nothing should access this, but just in case! */
 		ti->private = NULL;
-
 		mutex_unlock(&eventfs_mutex);
 
-		/* Now safely free the top level files and their children */
-		list_for_each_entry_safe(ei_child, tmp, &ef_del_list, del_list) {
-			list_del(&ei_child->del_list);
-			eventfs_remove_dir(ei_child);
-		}
-
 		free_ei(ei);
 		return;
 	}
@@ -319,14 +300,6 @@ void eventfs_set_ei_status_free(struct t
 	if (!ei)
 		goto out;
 
-	/*
-	 * If ei was freed, then the LSB bit is set for d_fsdata.
-	 * But this should not happen, as it should still have a
-	 * ref count that prevents it. Warn in case it does.
-	 */
-	if (WARN_ON_ONCE((unsigned long)ei & 1))
-		goto out;
-
 	/* This could belong to one of the files of the ei */
 	if (ei->dentry != dentry) {
 		for (i = 0; i < ei->nr_entries; i++) {
@@ -336,6 +309,8 @@ void eventfs_set_ei_status_free(struct t
 		if (WARN_ON_ONCE(i == ei->nr_entries))
 			goto out;
 		ei->d_children[i] = NULL;
+	} else if (ei->is_freed) {
+		free_ei(ei);
 	} else {
 		ei->dentry = NULL;
 	}
@@ -962,13 +937,65 @@ struct eventfs_inode *eventfs_create_eve
 	return ERR_PTR(-ENOMEM);
 }
 
+static LLIST_HEAD(free_list);
+
+static void eventfs_workfn(struct work_struct *work)
+{
+        struct eventfs_inode *ei, *tmp;
+        struct llist_node *llnode;
+
+	llnode = llist_del_all(&free_list);
+        llist_for_each_entry_safe(ei, tmp, llnode, llist) {
+		/* This dput() matches the dget() from unhook_dentry() */
+		for (int i = 0; i < ei->nr_entries; i++) {
+			if (ei->d_children[i])
+				dput(ei->d_children[i]);
+		}
+		/* This should only get here if it had a dentry */
+		if (!WARN_ON_ONCE(!ei->dentry))
+			dput(ei->dentry);
+        }
+}
+
+static DECLARE_WORK(eventfs_work, eventfs_workfn);
+
 static void free_rcu_ei(struct rcu_head *head)
 {
 	struct eventfs_inode *ei = container_of(head, struct eventfs_inode, rcu);
 
+	if (ei->dentry) {
+		/* Do not free the ei until all references of dentry are gone */
+		if (llist_add(&ei->llist, &free_list))
+			queue_work(system_unbound_wq, &eventfs_work);
+		return;
+	}
+
+	/* If the ei doesn't have a dentry, neither should its children */
+	for (int i = 0; i < ei->nr_entries; i++) {
+		WARN_ON_ONCE(ei->d_children[i]);
+	}
+
 	free_ei(ei);
 }
 
+static void unhook_dentry(struct dentry *dentry)
+{
+	if (!dentry)
+		return;
+
+	/* Keep the dentry from being freed yet (see eventfs_workfn()) */
+	dget(dentry);
+
+	dentry->d_fsdata = NULL;
+	d_invalidate(dentry);
+	mutex_lock(&eventfs_mutex);
+	/* dentry should now have at least a single reference */
+	WARN_ONCE((int)d_count(dentry) < 1,
+		  "dentry %px (%s) less than one reference (%d) after invalidate\n",
+		  dentry, dentry->d_name.name, d_count(dentry));
+	mutex_unlock(&eventfs_mutex);
+}
+
 /**
  * eventfs_remove_rec - remove eventfs dir or file from list
  * @ei: eventfs_inode to be removed.
@@ -1006,33 +1033,6 @@ static void eventfs_remove_rec(struct ev
 	list_add_tail(&ei->del_list, head);
 }
 
-static void unhook_dentry(struct dentry **dentry, struct dentry **list)
-{
-	if (*dentry) {
-		unsigned long ptr = (unsigned long)*list;
-
-		/* Keep the dentry from being freed yet */
-		dget(*dentry);
-
-		/*
-		 * Paranoid: The dget() above should prevent the dentry
-		 * from being freed and calling eventfs_set_ei_status_free().
-		 * But just in case, set the link list LSB pointer to 1
-		 * and have eventfs_set_ei_status_free() check that to
-		 * make sure that if it does happen, it will not think
-		 * the d_fsdata is an eventfs_inode.
-		 *
-		 * For this to work, no eventfs_inode should be allocated
-		 * on a odd space, as the ef should always be allocated
-		 * to be at least word aligned. Check for that too.
-		 */
-		WARN_ON_ONCE(ptr & 1);
-
-		(*dentry)->d_fsdata = (void *)(ptr | 1);
-		*list = *dentry;
-		*dentry = NULL;
-	}
-}
 /**
  * eventfs_remove_dir - remove eventfs dir or file from list
  * @ei: eventfs_inode to be removed.
@@ -1043,40 +1043,28 @@ void eventfs_remove_dir(struct eventfs_i
 {
 	struct eventfs_inode *tmp;
 	LIST_HEAD(ei_del_list);
-	struct dentry *dentry_list = NULL;
-	struct dentry *dentry;
-	int i;
 
 	if (!ei)
 		return;
 
+	/*
+	 * Move the deleted eventfs_inodes onto the ei_del_list
+	 * which will also set the is_freed value. Note, this has to be
+	 * done under the eventfs_mutex, but the deletions of
+	 * the dentries must be done outside the eventfs_mutex.
+	 * Hence moving them to this temporary list.
+	 */
 	mutex_lock(&eventfs_mutex);
 	eventfs_remove_rec(ei, &ei_del_list, 0);
+	mutex_unlock(&eventfs_mutex);
 
 	list_for_each_entry_safe(ei, tmp, &ei_del_list, del_list) {
-		for (i = 0; i < ei->nr_entries; i++)
-			unhook_dentry(&ei->d_children[i], &dentry_list);
-		unhook_dentry(&ei->dentry, &dentry_list);
+		for (int i = 0; i < ei->nr_entries; i++)
+			unhook_dentry(ei->d_children[i]);
+		unhook_dentry(ei->dentry);
+		list_del(&ei->del_list);
 		call_srcu(&eventfs_srcu, &ei->rcu, free_rcu_ei);
 	}
-	mutex_unlock(&eventfs_mutex);
-
-	while (dentry_list) {
-		unsigned long ptr;
-
-		dentry = dentry_list;
-		ptr = (unsigned long)dentry->d_fsdata & ~1UL;
-		dentry_list = (struct dentry *)ptr;
-		dentry->d_fsdata = NULL;
-		d_invalidate(dentry);
-		mutex_lock(&eventfs_mutex);
-		/* dentry should now have at least a single reference */
-		WARN_ONCE((int)d_count(dentry) < 1,
-			  "dentry %px (%s) less than one reference (%d) after invalidate\n",
-			  dentry, dentry->d_name.name, d_count(dentry));
-		mutex_unlock(&eventfs_mutex);
-		dput(dentry);
-	}
 }
 
 /**
--- a/fs/tracefs/internal.h
+++ b/fs/tracefs/internal.h
@@ -54,10 +54,12 @@ struct eventfs_inode {
 	void				*data;
 	/*
 	 * Union - used for deletion
+	 * @llist:	for calling dput() if needed after RCU
 	 * @del_list:	list of eventfs_inode to delete
 	 * @rcu:	eventfs_inode to delete in RCU
 	 */
 	union {
+		struct llist_node	llist;
 		struct list_head	del_list;
 		struct rcu_head		rcu;
 	};


Patches currently in stable-queue which might be from rostedt@xxxxxxxxxx are

queue-6.6/eventfs-keep-all-directory-links-at-1.patch
queue-6.6/eventfs-make-sure-that-parent-d_inode-is-locked-in-creating-files-dirs.patch
queue-6.6/eventfs-save-directory-inodes-in-the-eventfs_inode-structure.patch
queue-6.6/revert-eventfs-save-ownership-and-mode.patch
queue-6.6/tracefs-zero-out-the-tracefs_inode-when-allocating-it.patch
queue-6.6/eventfs-read-ei-entries-before-ei-children-in-eventfs_iterate.patch
queue-6.6/eventfs-do-not-invalidate-dentry-in-create_file-dir_dentry.patch
queue-6.6/eventfs-fix-file-and-directory-uid-and-gid-ownership.patch
queue-6.6/eventfs-remove-lookup-parameter-from-create_dir-file_dentry.patch
queue-6.6/eventfs-use-gfp_nofs-for-allocation-when-eventfs_mutex-is-held.patch
queue-6.6/eventfs-remove-fsnotify-functions-from-lookup.patch
queue-6.6/eventfs-use-err_cast-in-eventfs_create_events_dir.patch
queue-6.6/revert-eventfs-use-simple_recursive_removal-to-clean-up-dentries.patch
queue-6.6/eventfs-use-simple_recursive_removal-to-clean-up-dentries.patch
queue-6.6/eventfs-stop-using-dcache_readdir-for-getdents.patch
queue-6.6/eventfs-have-event-files-and-directories-default-to-parent-uid-and-gid.patch
queue-6.6/eventfs-use-eventfs_remove_events_dir.patch
queue-6.6/eventfs-delete-eventfs_inode-when-the-last-dentry-is-freed.patch
queue-6.6/tracefs-avoid-using-the-ei-dentry-pointer-unnecessarily.patch
queue-6.6/tracefs-remove-stale-update_gid-code.patch
queue-6.6/eventfs-initialize-the-tracefs-inode-properly.patch
queue-6.6/eventfs-remove-special-processing-of-dput-of-events-directory.patch
queue-6.6/eventfs-save-ownership-and-mode.patch
queue-6.6/tracefs-check-for-dentry-d_inode-exists-in-set_gid.patch
queue-6.6/eventfs-do-ctx-pos-update-for-all-iterations-in-eventfs_iterate.patch
queue-6.6/tracefs-dentry-lookup-crapectomy.patch
queue-6.6/eventfs-move-taking-of-inode_lock-into-dcache_dir_open_wrapper.patch
queue-6.6/eventfs-have-a-free_ei-that-just-frees-the-eventfs_inode.patch
queue-6.6/revert-eventfs-remove-is_freed-union-with-rcu-head.patch
queue-6.6/eventfs-have-the-inodes-all-for-files-and-directories-all-be-the-same.patch
queue-6.6/eventfs-use-kcalloc-instead-of-kzalloc.patch
queue-6.6/eventfs-test-for-ei-is_freed-when-accessing-ei-dentry.patch
queue-6.6/eventfs-fix-bitwise-fields-for-is_events.patch
queue-6.6/eventfs-fix-warn_on-in-create_file_dentry.patch
queue-6.6/eventfs-fix-events-beyond-name_max-blocking-tasks.patch
queue-6.6/eventfs-shortcut-eventfs_iterate-by-skipping-entries-already-read.patch
queue-6.6/revert-eventfs-do-not-allow-null-parent-to-eventfs_start_creating.patch
queue-6.6/eventfs-do-not-allow-null-parent-to-eventfs_start_creating.patch
queue-6.6/eventfs-do-not-create-dentries-nor-inodes-in-iterate_shared.patch
queue-6.6/eventfs-have-eventfs_iterate-stop-immediately-if-ei-is_freed-is-set.patch
queue-6.6/eventfs-fix-typo-in-eventfs_inode-union-comment.patch
queue-6.6/eventfs-remove-expectation-that-ei-is_freed-means-ei-dentry-null.patch
queue-6.6/eventfs-restructure-eventfs_inode-structure-to-be-more-condensed.patch
queue-6.6/eventfs-warn-if-an-eventfs_inode-is-freed-without-is_freed-being-set.patch
queue-6.6/eventfs-get-rid-of-dentry-pointers-without-refcounts.patch
queue-6.6/eventfs-remove-unused-d_parent-pointer-field.patch
queue-6.6/eventfs-hold-eventfs_mutex-when-calling-callback-functions.patch
queue-6.6/eventfs-remove-is_freed-union-with-rcu-head.patch
queue-6.6/tracefs-eventfs-modify-mismatched-function-name.patch
queue-6.6/eventfs-fix-kerneldoc-of-eventfs_remove_rec.patch
queue-6.6/tracefs-eventfs-use-root-and-instance-inodes-as-default-ownership.patch
queue-6.6/revert-eventfs-check-for-null-ef-in-eventfs_set_attr.patch
queue-6.6/eventfs-fix-failure-path-in-eventfs_create_events_dir.patch
queue-6.6/eventfs-clean-up-dentry-ops-and-add-revalidate-function.patch




[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux