From: "Cen Zhang (Microsoft)" <blbllhy@gmail.com>
To: brauner@kernel.org
Cc: oleg@redhat.com, akpm@linux-foundation.org, jack@suse.cz,
avagin@gmail.com, ptikhomirov@virtuozzo.com, mjguzik@gmail.com,
include@grrlz.net, linux-kernel@vger.kernel.org,
AutonomousCodeSecurity@microsoft.com,
tgopinath@linux.microsoft.com, kys@microsoft.com,
blbllhy@gmail.com, stable@vger.kernel.org
Subject: [PATCH v2 2/2] pid: fix cad_pid use-after-free race
Date: Fri, 17 Jul 2026 23:20:00 -0400 [thread overview]
Message-ID: <20260718032000.9184-2-blbllhy@gmail.com> (raw)
In-Reply-To: <20260718032000.9184-1-blbllhy@gmail.com>
proc_do_cad_pid() reads the global cad_pid pointer and passes it to
pid_vnr() without protecting the lifetime of the referenced struct pid.
A concurrent writer can replace cad_pid and drop the final reference to
the old struct pid after the reader has loaded the pointer but before
pid_vnr() has finished dereferencing it, causing a use-after-free.
The sysctl is mode 0600, but access is checked against the owning user
namespace, so an unprivileged user can reach it via userns, pidns, and a
proc mount.
Fix this by treating cad_pid as an RCU-protected pointer at both read
sites and by waiting for a grace period before dropping the old reference
on the write side.
KASAN crash stack:
kernel/pid.c:545 pid_nr_ns() # reads freed pid->level
kernel/pid.c:556 pid_vnr()
kernel/pid.c:775 proc_do_cad_pid()
fs/proc/proc_sysctl.c proc_sys_call_handler()
fs/read_write.c vfs_read()
fs/read_write.c __x64_sys_pread64()
Fixes: 9ec52099e4b8 ("[PATCH] replace cad_pid by a struct pid")
Reported-by: AutonomousCodeSecurity@microsoft.com
Closes: https://lore.kernel.org/all/20260717210143.4734-1-blbllhy@gmail.com/
Suggested-by: Mateusz Guzik <mjguzik@gmail.com>
Reviewed-by: Bradley Morgan <include@grrlz.net>
Cc: stable@vger.kernel.org
Signed-off-by: Cen Zhang (Microsoft) <blbllhy@gmail.com>
---
v2:
- Split out kill_cad_pid() deinline into a preparatory patch.
- Annotate cad_pid as __rcu and use rcu_dereference().
- Protect kill_cad_pid() by taking a pid reference under RCU.
- Add a comment explaining why synchronize_rcu() is used instead of
call_rcu().
include/linux/sched.h | 2 +-
init/main.c | 2 +-
kernel/pid.c | 26 +++++++++++++++++++++++---
kernel/reboot.c | 2 +-
4 files changed, 26 insertions(+), 6 deletions(-)
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 373bcc0598d1..31ce72b1233c 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1767,7 +1767,7 @@ static inline bool is_lazy_mmu_mode_active(void)
}
#endif
-extern struct pid *cad_pid;
+extern struct pid __rcu *cad_pid;
/*
* Per process flags
diff --git a/init/main.c b/init/main.c
index e363232b428b..19a10d0c2760 100644
--- a/init/main.c
+++ b/init/main.c
@@ -1636,7 +1636,7 @@ static noinline void __init kernel_init_freeable(void)
*/
set_mems_allowed(node_states[N_MEMORY]);
- cad_pid = get_pid(task_pid(current));
+ rcu_assign_pointer(cad_pid, get_pid(task_pid(current)));
smp_prepare_cpus(setup_max_cpus);
diff --git a/kernel/pid.c b/kernel/pid.c
index 234ebee29375..bffc5f765080 100644
--- a/kernel/pid.c
+++ b/kernel/pid.c
@@ -559,7 +559,17 @@ EXPORT_SYMBOL_GPL(pid_vnr);
int kill_cad_pid(int sig, int priv)
{
- return kill_pid(cad_pid, sig, priv);
+ struct pid *pid;
+ int ret;
+
+ rcu_read_lock();
+ pid = get_pid(rcu_dereference(cad_pid));
+ rcu_read_unlock();
+
+ ret = kill_pid(pid, sig, priv);
+ put_pid(pid);
+
+ return ret;
}
pid_t __task_pid_nr_ns(struct task_struct *task, enum pid_type type,
@@ -773,11 +783,15 @@ static int proc_do_cad_pid(const struct ctl_table *table, int write, void *buffe
size_t *lenp, loff_t *ppos)
{
struct pid *new_pid;
+ struct pid *old_pid;
pid_t tmp_pid;
int r;
struct ctl_table tmp_table = *table;
- tmp_pid = pid_vnr(cad_pid);
+ rcu_read_lock();
+ tmp_pid = pid_vnr(rcu_dereference(cad_pid));
+ rcu_read_unlock();
+
tmp_table.data = &tmp_pid;
r = proc_dointvec(&tmp_table, write, buffer, lenp, ppos);
@@ -788,7 +802,13 @@ static int proc_do_cad_pid(const struct ctl_table *table, int write, void *buffe
if (!new_pid)
return -ESRCH;
- put_pid(xchg(&cad_pid, new_pid));
+ old_pid = unrcu_pointer(xchg(&cad_pid, RCU_INITIALIZER(new_pid)));
+ /*
+ * Wait for cad_pid readers before put_pid(). We cannot use
+ * call_rcu() here because free_pid() already owns pid->rcu.
+ */
+ synchronize_rcu();
+ put_pid(old_pid);
return 0;
}
diff --git a/kernel/reboot.c b/kernel/reboot.c
index 695c33e75efd..fc191a48c0e9 100644
--- a/kernel/reboot.c
+++ b/kernel/reboot.c
@@ -24,7 +24,7 @@
*/
static int C_A_D = 1;
-struct pid *cad_pid;
+struct pid __rcu *cad_pid;
EXPORT_SYMBOL(cad_pid);
#if defined(CONFIG_ARM)
--
2.53.0
next prev parent reply other threads:[~2026-07-18 3:20 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-18 3:19 [PATCH v2 1/2] pid: deinline kill_cad_pid Cen Zhang (Microsoft)
2026-07-18 3:20 ` Cen Zhang (Microsoft) [this message]
2026-07-18 11:54 ` [PATCH v2 2/2] pid: fix cad_pid use-after-free race Bradley Morgan
2026-07-18 13:19 ` Oleg Nesterov
2026-07-18 13:59 ` Bradley Morgan
2026-07-18 14:13 ` Oleg Nesterov
2026-07-18 14:28 ` Bradley Morgan
2026-07-18 15:37 ` Mateusz Guzik
2026-07-18 15:58 ` Oleg Nesterov
2026-07-18 10:24 ` [PATCH v2 1/2] pid: deinline kill_cad_pid Bradley Morgan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260718032000.9184-2-blbllhy@gmail.com \
--to=blbllhy@gmail.com \
--cc=AutonomousCodeSecurity@microsoft.com \
--cc=akpm@linux-foundation.org \
--cc=avagin@gmail.com \
--cc=brauner@kernel.org \
--cc=include@grrlz.net \
--cc=jack@suse.cz \
--cc=kys@microsoft.com \
--cc=linux-kernel@vger.kernel.org \
--cc=mjguzik@gmail.com \
--cc=oleg@redhat.com \
--cc=ptikhomirov@virtuozzo.com \
--cc=stable@vger.kernel.org \
--cc=tgopinath@linux.microsoft.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.