From f3a6dae67c9ca3d913cbb42fe324c9b47c8d9e53 Mon Sep 17 00:00:00 2001 From: Nickolai Zeldovich Date: Mon, 26 Oct 2026 21:58:13 -0400 Subject: [PATCH] fs_pin: do not trust the pin after a spurious wakeup in pin_kill() A task waiting in pin_kill() for another task's ->kill() can take p->wait.lock after the pin has been freed, and can return with its on-stack wait entry still on p->wait. With BSD process accounting this is a use-after-free of the acct structure when umount() of the mount holding the accounting file races with acct(2). pin_kill() takes a still-queued entry as proof that pin_remove() has not run, so p is alive. But autoremove_wake_function() dequeues only when try_to_wake_up() succeeds: if the waiter was already woken spuriously, pin_remove() leaves the entry queued, acct_pin_kill() frees the acct with kfree_rcu(), and the grace period can end while the waiter sits between schedule() and rcu_read_lock(). Dequeue the entry unconditionally in a private wake function; pin_remove() is the only waker and sets p->done first. Reproduced under QEMU with KASAN and test-only delays: a slab-use-after-free in pin_kill() before the patch, no report after. Fixes: 59eda0e07f43 ("new fs_pin killing logics") Signed-off-by: Nickolai Zeldovich Link: https://patch.msgid.link/20261027015813.841821-1-nickolai@csail.mit.edu Signed-off-by: Christian Brauner (Amutable) --- fs/fs_pin.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/fs/fs_pin.c b/fs/fs_pin.c index 1a508f2167e0..a4e5a5a7aca8 100644 --- a/fs/fs_pin.c +++ b/fs/fs_pin.c @@ -28,6 +28,19 @@ void pin_insert(struct fs_pin *pin, struct vfsmount *m) spin_unlock(&pin_lock); } +/* + * Unlike autoremove_wake_function(), dequeue even if the waiter was already + * running: pin_kill() trusts @p only while its entry is still queued. + */ +static int pin_kill_wake_function(struct wait_queue_entry *wait, + unsigned int mode, int sync, void *key) +{ + int ret = default_wake_function(wait, mode, sync, key); + + list_del_init_careful(&wait->entry); + return ret; +} + void pin_kill(struct fs_pin *p) { wait_queue_entry_t wait; @@ -36,7 +49,7 @@ void pin_kill(struct fs_pin *p) rcu_read_unlock(); return; } - init_wait(&wait); + init_wait_func(&wait, pin_kill_wake_function); spin_lock_irq(&p->wait.lock); if (likely(!p->done)) { p->done = -1;