mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
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: 59eda0e07f ("new fs_pin killing logics")
Signed-off-by: Nickolai Zeldovich <nickolai@csail.mit.edu>
Link: https://patch.msgid.link/20261027015813.841821-1-nickolai@csail.mit.edu
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
parent
38bf603d1e
commit
f3a6dae67c
15
fs/fs_pin.c
15
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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user