* [PATCH v2] exit: hold a reference to thread_pid across proc_flush_pid
@ 2026-08-31 0:12 Daehyeon Ko
2026-08-31 14:58 ` Oleg Nesterov
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Daehyeon Ko @ 2026-08-31 0:12 UTC (permalink / raw)
To: Oleg Nesterov
Cc: Mateusz Guzik, Christian Brauner, linux-kernel, linux-fsdevel,
syzbot+0aee5e8066eddbbe7397, syzbot+e8b3520b53e78e90034e
Commit 0a36bad01731 ("release_task: kill the no longer needed
get/put_pid(thread_pid)") removed the reference around proc_flush_pid().
It assumed that free_pids(post.pids) at the end of release_task() would
keep thread_pid alive until then.
That assumption is wrong. __change_pid() only records a detached PID in
post.pids when pid_has_task() is false for every PIDTYPE. If another task
still uses the exiting task's PID as its process group or session ID,
__unhash_process() removes the exiting task's PIDTYPE_PID link but leaves
the PID out of post.pids. release_task() therefore holds no reference to
it after dropping tasklist_lock.
The other task can then remove the remaining PIDTYPE links. Its
free_pids() call schedules delayed_put_pid(), and the RCU callback can free
the PID before the first release_task() reaches proc_flush_pid().
An unprivileged reproducer races wait4(-1) against setsid() to trigger this
ordering. Three of three fresh v7.2 KASAN boots reported:
BUG: KASAN: slab-use-after-free in
proc_invalidate_siblings_dcache+0x3e2/0x3f0
Read of size 8 by task h7_pid_reaper/1921
Call Trace:
proc_invalidate_siblings_dcache
release_task
wait_consider_task
__do_wait
do_wait
kernel_wait4
Freed by task 0:
kmem_cache_free
put_pid
delayed_put_pid
rcu_core
Last potentially related work creation:
__call_rcu_common
free_pids
ksys_setsid
KASAN identified a 144-byte object from the pid cache and located the bad
read 80 bytes into the freed object, matching pid->inodes. With an
explicit reference, three of three fresh boots completed without a KASAN
report. The concurrent RCU callback dropped its reference while
proc_flush_pid() was protected, and the balancing put_pid() performed the
final free afterward.
Take a reference before __unhash_process() clears p->thread_pid and release
it after proc_flush_pid() completes.
A tested source reproducer is available privately on request. No
controlled read or write, information leak, or privilege escalation is
claimed. The mainline patch applies directly to v6.19.y and newer;
v6.16.y through v6.18.y need a context-adjusted backport.
Fixes: 0a36bad01731 ("release_task: kill the no longer needed get/put_pid(thread_pid)")
Reported-by: syzbot+0aee5e8066eddbbe7397@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=0aee5e8066eddbbe7397
Reported-by: syzbot+e8b3520b53e78e90034e@syzkaller.appspotmail.com
Link: https://syzkaller.appspot.com/bug?extid=e8b3520b53e78e90034e
Cc: <stable@vger.kernel.org> # see patch description, needs adjustments for 6.16.y-6.18.y
Assisted-by: LLM
Signed-off-by: Daehyeon Ko <4ncienth@gmail.com>
---
Changes in v2:
- Rewrite the root-cause explanation around __change_pid(), pid_has_task(),
and the last PIDTYPE detach, as requested by Oleg.
- Explain in the source comment why thread_pid needs an explicit reference.
- Rebase onto current Torvalds master cee9395acd80.
kernel/exit.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/kernel/exit.c b/kernel/exit.c
index 97686af89501..4e028f157597 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -261,8 +261,11 @@ void release_task(struct task_struct *p)
pidfs_exit(p);
cgroup_task_release(p);
- /* Retrieve @thread_pid before __unhash_process() may set it to NULL. */
- thread_pid = task_pid(p);
+ /*
+ * Pin @thread_pid before __unhash_process() clears it. The last
+ * PIDTYPE detach can otherwise free it before proc_flush_pid().
+ */
+ thread_pid = get_pid(task_pid(p));
write_lock_irq(&tasklist_lock);
ptrace_release_task(p);
@@ -291,8 +294,8 @@ void release_task(struct task_struct *p)
}
write_unlock_irq(&tasklist_lock);
- /* @thread_pid can't go away until free_pids() below */
proc_flush_pid(thread_pid);
+ put_pid(thread_pid);
exit_cred_namespaces(p);
add_device_randomness(&p->se.sum_exec_runtime,
sizeof(p->se.sum_exec_runtime));
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
--
2.55.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v2] exit: hold a reference to thread_pid across proc_flush_pid
2026-08-31 0:12 [PATCH v2] exit: hold a reference to thread_pid across proc_flush_pid Daehyeon Ko
@ 2026-08-31 14:58 ` Oleg Nesterov
2026-09-04 9:40 ` Christian Brauner
2026-09-01 8:01 ` Bradley Morgan
2026-09-04 9:40 ` Christian Brauner
2 siblings, 1 reply; 5+ messages in thread
From: Oleg Nesterov @ 2026-08-31 14:58 UTC (permalink / raw)
To: Daehyeon Ko, Christian Brauner
Cc: Mateusz Guzik, linux-kernel, linux-fsdevel,
syzbot+0aee5e8066eddbbe7397, syzbot+e8b3520b53e78e90034e
On 08/31, Daehyeon Ko wrote:
>
> Commit 0a36bad01731 ("release_task: kill the no longer needed
> get/put_pid(thread_pid)") removed the reference around proc_flush_pid().
> It assumed that free_pids(post.pids) at the end of release_task() would
> keep thread_pid alive until then.
>
> That assumption is wrong. __change_pid() only records a detached PID in
> post.pids when pid_has_task() is false for every PIDTYPE. If another task
> still uses the exiting task's PID as its process group or session ID,
> __unhash_process() removes the exiting task's PIDTYPE_PID link but leaves
> the PID out of post.pids. release_task() therefore holds no reference to
> it after dropping tasklist_lock.
>
> The other task can then remove the remaining PIDTYPE links. Its
> free_pids() call schedules delayed_put_pid(), and the RCU callback can free
> the PID before the first release_task() reaches proc_flush_pid().
Much better, thanks. The comment above get_pid(task_pid(p)) looks better too.
Acked-by: Oleg Nesterov <oleg@redhat.com>
> A tested source reproducer is available privately on request. No
> controlled read or write, information leak, or privilege escalation is
> claimed. The mainline patch applies directly to v6.19.y and newer;
> v6.16.y through v6.18.y need a context-adjusted backport.
Up to you and Christian (I am hoping we can take your fix), but I think
that this paragraph should be removed from the changelog... At least the
"A tested source reproducer is available privately on request." part.
Heh. I didn't even know that 0a36bad01731 was backported to stable. Why?
I guess I missed some AUTOSEL email...
Thanks,
Oleg.
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2] exit: hold a reference to thread_pid across proc_flush_pid
2026-08-31 0:12 [PATCH v2] exit: hold a reference to thread_pid across proc_flush_pid Daehyeon Ko
2026-08-31 14:58 ` Oleg Nesterov
@ 2026-09-01 8:01 ` Bradley Morgan
2026-09-04 9:40 ` Christian Brauner
2 siblings, 0 replies; 5+ messages in thread
From: Bradley Morgan @ 2026-09-01 8:01 UTC (permalink / raw)
To: 4ncienth
Cc: brauner, linux-fsdevel, linux-kernel, mjguzik, oleg,
syzbot+0aee5e8066eddbbe7397, syzbot+e8b3520b53e78e90034e
On 31 August 2026 01:12:21 BST, Daehyeon Ko <4ncienth@gmail.com> wrote:
>Commit 0a36bad01731 ("release_task: kill the no longer needed
>get/put_pid(thread_pid)") removed the reference around proc_flush_pid().
>It assumed that free_pids(post.pids) at the end of release_task() would
>keep thread_pid alive until then.
>
>That assumption is wrong. __change_pid() only records a detached PID in
>post.pids when pid_has_task() is false for every PIDTYPE. If another task
>still uses the exiting task's PID as its process group or session ID,
>__unhash_process() removes the exiting task's PIDTYPE_PID link but leaves
>the PID out of post.pids. release_task() therefore holds no reference to
>it after dropping tasklist_lock.
>
>The other task can then remove the remaining PIDTYPE links. Its
>free_pids() call schedules delayed_put_pid(), and the RCU callback can
>free
>the PID before the first release_task() reaches proc_flush_pid().
>
>An unprivileged reproducer races wait4(-1) against setsid() to trigger
>this
Gnarly!
>ordering. Three of three fresh v7.2 KASAN boots reported:
>
> BUG: KASAN: slab-use-after-free in
> proc_invalidate_siblings_dcache+0x3e2/0x3f0
> Read of size 8 by task h7_pid_reaper/1921
>
> Call Trace:
> proc_invalidate_siblings_dcache
> release_task
> wait_consider_task
> __do_wait
> do_wait
> kernel_wait4
>
> Freed by task 0:
> kmem_cache_free
> put_pid
> delayed_put_pid
> rcu_core
>
> Last potentially related work creation:
> __call_rcu_common
> free_pids
> ksys_setsid
>
>KASAN identified a 144-byte object from the pid cache and located the bad
>read 80 bytes into the freed object, matching pid->inodes. With an
>explicit reference, three of three fresh boots completed without a KASAN
>report. The concurrent RCU callback dropped its reference while
>proc_flush_pid() was protected, and the balancing put_pid() performed the
>final free afterward.
>
Ok
>Take a reference before __unhash_process() clears p->thread_pid and
>release
>it after proc_flush_pid() completes.
>
>A tested source reproducer is available privately on request. No
>controlled read or write, information leak, or privilege escalation is
>claimed. The mainline patch applies directly to v6.19.y and newer;
>v6.16.y through v6.18.y need a context-adjusted backport.
>
>Fixes: 0a36bad01731 ("release_task: kill the no longer needed get/put_pid(thread_pid)")
>Reported-by: syzbot+0aee5e8066eddbbe7397@syzkaller.appspotmail.com
>Closes: https://syzkaller.appspot.com/bug?extid=0aee5e8066eddbbe7397
>Reported-by: syzbot+e8b3520b53e78e90034e@syzkaller.appspotmail.com
>Link: https://syzkaller.appspot.com/bug?extid=e8b3520b53e78e90034e
>Cc: <stable@vger.kernel.org> # see patch description, needs adjustments for 6.16.y-6.18.y
>Assisted-by: LLM
Thanks,
Reviewed-by: Bradley Morgan <brads@mainlining.org>
>Signed-off-by: Daehyeon Ko <4ncienth@gmail.com>
>---
>Changes in v2:
>- Rewrite the root-cause explanation around __change_pid(), pid_has_task(),
> and the last PIDTYPE detach, as requested by Oleg.
>- Explain in the source comment why thread_pid needs an explicit reference.
>- Rebase onto current Torvalds master cee9395acd80.
>
> kernel/exit.c | 9 ++++++---
> 1 file changed, 6 insertions(+), 3 deletions(-)
>
>diff --git a/kernel/exit.c b/kernel/exit.c
>index 97686af89501..4e028f157597 100644
>--- a/kernel/exit.c
>+++ b/kernel/exit.c
>@@ -261,8 +261,11 @@ void release_task(struct task_struct *p)
> pidfs_exit(p);
> cgroup_task_release(p);
>
>- /* Retrieve @thread_pid before __unhash_process() may set it to NULL. */
>- thread_pid = task_pid(p);
>+ /*
>+ * Pin @thread_pid before __unhash_process() clears it. The last
>+ * PIDTYPE detach can otherwise free it before proc_flush_pid().
>+ */
>+ thread_pid = get_pid(task_pid(p));
>
> write_lock_irq(&tasklist_lock);
> ptrace_release_task(p);
>@@ -291,8 +294,8 @@ void release_task(struct task_struct *p)
> }
>
> write_unlock_irq(&tasklist_lock);
>- /* @thread_pid can't go away until free_pids() below */
> proc_flush_pid(thread_pid);
>+ put_pid(thread_pid);
> exit_cred_namespaces(p);
> add_device_randomness(&p->se.sum_exec_runtime,
> sizeof(p->se.sum_exec_runtime));
>
>base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
>
--- Thanks!
https://lore.kernel.org/all/EE579805-42F2-4C58-B752-F28779EEB717@grrlz.net/
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2] exit: hold a reference to thread_pid across proc_flush_pid
2026-08-31 14:58 ` Oleg Nesterov
@ 2026-09-04 9:40 ` Christian Brauner
0 siblings, 0 replies; 5+ messages in thread
From: Christian Brauner @ 2026-09-04 9:40 UTC (permalink / raw)
To: Oleg Nesterov
Cc: Daehyeon Ko, Mateusz Guzik, linux-kernel, linux-fsdevel,
syzbot+0aee5e8066eddbbe7397, syzbot+e8b3520b53e78e90034e
On Mon, Aug 31, 2026 at 04:58:58PM +0200, Oleg Nesterov wrote:
> On 08/31, Daehyeon Ko wrote:
> >
> > Commit 0a36bad01731 ("release_task: kill the no longer needed
> > get/put_pid(thread_pid)") removed the reference around proc_flush_pid().
> > It assumed that free_pids(post.pids) at the end of release_task() would
> > keep thread_pid alive until then.
> >
> > That assumption is wrong. __change_pid() only records a detached PID in
> > post.pids when pid_has_task() is false for every PIDTYPE. If another task
> > still uses the exiting task's PID as its process group or session ID,
> > __unhash_process() removes the exiting task's PIDTYPE_PID link but leaves
> > the PID out of post.pids. release_task() therefore holds no reference to
> > it after dropping tasklist_lock.
> >
> > The other task can then remove the remaining PIDTYPE links. Its
> > free_pids() call schedules delayed_put_pid(), and the RCU callback can free
> > the PID before the first release_task() reaches proc_flush_pid().
>
> Much better, thanks. The comment above get_pid(task_pid(p)) looks better too.
>
> Acked-by: Oleg Nesterov <oleg@redhat.com>
>
>
> > A tested source reproducer is available privately on request. No
> > controlled read or write, information leak, or privilege escalation is
> > claimed. The mainline patch applies directly to v6.19.y and newer;
> > v6.16.y through v6.18.y need a context-adjusted backport.
>
> Up to you and Christian (I am hoping we can take your fix), but I think
Snatching it up, thanks!
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2] exit: hold a reference to thread_pid across proc_flush_pid
2026-08-31 0:12 [PATCH v2] exit: hold a reference to thread_pid across proc_flush_pid Daehyeon Ko
2026-08-31 14:58 ` Oleg Nesterov
2026-09-01 8:01 ` Bradley Morgan
@ 2026-09-04 9:40 ` Christian Brauner
2 siblings, 0 replies; 5+ messages in thread
From: Christian Brauner @ 2026-09-04 9:40 UTC (permalink / raw)
To: Oleg Nesterov, Daehyeon Ko
Cc: Mateusz Guzik, linux-kernel, linux-fsdevel,
syzbot+0aee5e8066eddbbe7397, syzbot+e8b3520b53e78e90034e
On Mon, 31 Aug 2026 09:12:21 +0900, Daehyeon Ko wrote:
> Commit 0a36bad01731 ("release_task: kill the no longer needed
> get/put_pid(thread_pid)") removed the reference around proc_flush_pid().
> It assumed that free_pids(post.pids) at the end of release_task() would
> keep thread_pid alive until then.
>
> That assumption is wrong. __change_pid() only records a detached PID in
> post.pids when pid_has_task() is false for every PIDTYPE. If another task
> still uses the exiting task's PID as its process group or session ID,
> __unhash_process() removes the exiting task's PIDTYPE_PID link but leaves
> the PID out of post.pids. release_task() therefore holds no reference to
> it after dropping tasklist_lock.
>
> [...]
Applied to the vfs.fixes branch of the vfs/vfs.git tree.
Patches in the vfs.fixes branch should appear in linux-next soon.
Please report any outstanding bugs that were missed during review in a
new review to the original patch series allowing us to drop it.
It's encouraged to provide Acked-bys and Reviewed-bys even though the
patch has now been applied. If possible patch trailers will be updated.
Note that commit hashes shown below are subject to change due to rebase,
trailer updates or similar. If in doubt, please check the listed branch.
tree: https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git
branch: vfs.fixes
[1/1] exit: hold a reference to thread_pid across proc_flush_pid
https://git.kernel.org/vfs/vfs/c/cdd812d0683d
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-09-04 9:41 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-31 0:12 [PATCH v2] exit: hold a reference to thread_pid across proc_flush_pid Daehyeon Ko
2026-08-31 14:58 ` Oleg Nesterov
2026-09-04 9:40 ` Christian Brauner
2026-09-01 8:01 ` Bradley Morgan
2026-09-04 9:40 ` Christian Brauner
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox