diff options
| author | Nickolai Zeldovich <nickolai@csail.mit.edu> | 2026-10-23 21:55:13 -0400 |
|---|---|---|
| committer | Christian Brauner <brauner@kernel.org> | 2026-10-30 16:02:40 +0100 |
| commit | 38bf603d1ea5d5a65a9cb277df75327ffec01ff1 (patch) | |
| tree | 641f0b7681333882b774d5722c17db29b5fcdecc | |
| parent | b2aa970a0a03c117306f48d75a4c3125a8cc9957 (diff) | |
| download | linux-38bf603d1ea5d5a65a9cb277df75327ffec01ff1.tar.gz linux-38bf603d1ea5d5a65a9cb277df75327ffec01ff1.zip | |
ucount: read the parent ucounts before dropping the rlimit count
do_dec_rlimit_put_ucounts() drops its share of iter->rlimit[type] and
only then loads iter->ns->ucounts. If the decrement leaves the count
non-zero, a concurrent caller can take it to zero and put the last
reference, which frees the ucounts and possibly its user namespace with
kfree_rcu(). An exiting task frees its pending signals without siglock
or any RCU read-side section, so when it is preempted between the
decrement and the load for a grace period it reads freed memory and
then decrements a counter through the pointer it found there.
A pending signal holds no reference on its ucounts: the reference is
taken by whoever moves the count from 0 to 1 and dropped by whoever
moves it back to 0, so the caller's share of the count is all that
keeps iter alive, and only until it is dropped.
Load the parent first; it stays pinned by the caller's share of its
own count.
Fixes: 15bc01effefe ("ucounts: Fix signal ucount refcounting")
Signed-off-by: Nickolai Zeldovich <nickolai@csail.mit.edu>
Link: https://patch.msgid.link/20261024015513.830312-1-nickolai@csail.mit.edu
Reviewed-by: Bradley Morgan <brads@mainlining.org>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
| -rw-r--r-- | kernel/ucount.c | 7 |
1 files changed, 5 insertions, 2 deletions
diff --git a/kernel/ucount.c b/kernel/ucount.c index c3de5b94d25f..94b70502dc9d 100644 --- a/kernel/ucount.c +++ b/kernel/ucount.c @@ -285,9 +285,12 @@ static void do_dec_rlimit_put_ucounts(struct ucounts *ucounts, { struct ucounts *iter, *next; for (iter = ucounts; iter != last; iter = next) { - long dec = atomic_long_sub_return(1, &iter->rlimit[type]); - WARN_ON_ONCE(dec < 0); + long dec; + + /* Our count pins @iter; once it is dropped @iter may be freed. */ next = iter->ns->ucounts; + dec = atomic_long_sub_return(1, &iter->rlimit[type]); + WARN_ON_ONCE(dec < 0); if (dec == 0) put_ucounts(iter); } |
