summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNickolai Zeldovich <nickolai@csail.mit.edu>2026-10-23 21:55:13 -0400
committerChristian Brauner <brauner@kernel.org>2026-10-30 16:02:40 +0100
commit38bf603d1ea5d5a65a9cb277df75327ffec01ff1 (patch)
tree641f0b7681333882b774d5722c17db29b5fcdecc
parentb2aa970a0a03c117306f48d75a4c3125a8cc9957 (diff)
downloadlinux-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.c7
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);
}