[PATCH] klist: avoid accesses after waking klist_remove()

Karl Mehltretter <[email protected]>
Newsgroups org.kernel.vger.stable,dev.linux.lists.driver-core,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
klist_release() publishes waiter->woken before its final accesses to the
stack waiter and node. klist_remove() can then return, allowing the waiter
to go out of scope and its caller to free or reuse the node.

Clear n_klist and take a task reference before publishing woken. Use
release/acquire accesses for that publication and wake the referenced task.

The task reference keeps the waiter task alive if it returns and exits
before wake_up_process(). wake_up_process() provides the full barrier
required by the sleep/wakeup protocol, so remove the explicit mb().

Fixes: 8b0c250be489 ("[PATCH] add klist_node_attached() to determine if a node is on a list or not.")
Fixes: 210272a28465 ("driver core: Remove completion from struct klist_node")
Reported-by: [email protected]
Closes: https://syzkaller.appspot.com/bug?extid=9cb1ac7fce4944ba9165
Tested-by: [email protected]
Cc: [email protected]
Assisted-by: LLM
Signed-off-by: Karl Mehltretter <[email protected]>
---
 lib/klist.c | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/lib/klist.c b/lib/klist.c
index 332a4fbf18ff..7242f4cc1c1b 100644
--- a/lib/klist.c
+++ b/lib/klist.c
@@ -36,6 +36,7 @@
 #include <linux/klist.h>
 #include <linux/export.h>
 #include <linux/sched.h>
+#include <linux/sched/task.h>
 
 /*
  * Use the lowest bit of n_klist to mark deleted nodes and exclude
@@ -187,18 +188,23 @@ static void klist_release(struct kref *kref)
 
 	WARN_ON(!knode_dead(n));
 	list_del(&n->n_node);
+	knode_set_klist(n, NULL);
 	spin_lock(&klist_remove_lock);
 	list_for_each_entry_safe(waiter, tmp, &klist_remove_waiters, list) {
+		struct task_struct *p;
+
 		if (waiter->node != n)
 			continue;
 
+		p = waiter->process;
+		get_task_struct(p);
 		list_del(&waiter->list);
-		waiter->woken = 1;
-		mb();
-		wake_up_process(waiter->process);
+		/* Publish only after the final waiter and n accesses */
+		smp_store_release(&waiter->woken, 1);
+		wake_up_process(p);
+		put_task_struct(p);
 	}
 	spin_unlock(&klist_remove_lock);
-	knode_set_klist(n, NULL);
 }
 
 static int klist_dec_and_del(struct klist_node *n)
@@ -250,7 +256,8 @@ void klist_remove(struct klist_node *n)
 
 	for (;;) {
 		set_current_state(TASK_UNINTERRUPTIBLE);
-		if (waiter.woken)
+		/* Pairs with the release store in klist_release() */
+		if (smp_load_acquire(&waiter.woken))
 			break;
 		schedule();
 	}
-- 
2.39.5 (Apple Git-154)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.