From: "Michal Koutný" <mkoutny@suse.com>
To: Salvatore Bonaccorso <carnil@debian.org>
Cc: Noah Elias Feldt <N.Feldt@mittwald.de>, Tejun Heo <tj@kernel.org>,
Johannes Weiner <hannes@cmpxchg.org>,
Dan Schatzberg <dschatzberg@meta.com>,
Peter Zijlstra <peterz@infradead.org>,
1144314@bugs.debian.org, cgroups@vger.kernel.org,
linux-kernel@vger.kernel.org, regressions@lists.linux.dev,
stable@vger.kernel.org
Subject: Re: refcount_t: addition on 0; use-after-free, regression from 260fbcb92bbe ("cgroup: Move dying_tasks cleanup from cgroup_task_release() to cgroup_task_free()")
Date: Fri, 21 Aug 2026 18:59:26 +0200 [thread overview]
Message-ID: <aoh7Kbt_WbUcfUxh@localhost.localdomain> (raw)
In-Reply-To: <178682274912.1759826.11422695267451330087@eldamar.lan>
[-- Attachment #1: Type: text/plain, Size: 3420 bytes --]
Hi Salvatore,
thanks for the nice report and sorry for not so prompt response.
On Sat, Aug 15, 2026 at 09:41:14PM +0200, Salvatore Bonaccorso <carnil@debian.org> wrote:
> With an additional reproducer provided by Noah, I could bisect the
> change down to
Good job.
>
> commit 260fbcb92bbeacfcd050410fdc2d24ab15044400
> Author: Tejun Heo <tj@kernel.org>
> Date: Tue Oct 28 20:19:16 2025 -1000
>
> cgroup: Move dying_tasks cleanup from cgroup_task_release() to cgroup_task_free()
>
> Currently, cgroup_task_exit() adds thread group leaders with live member
> threads to their css_set's dying_tasks list (so cgroup.procs iteration can
> still see the leader), and cgroup_task_release() later removes them with
> list_del_init(&task->cg_list).
>
> An upcoming patch will defer the dying_tasks list addition, moving it from
> cgroup_task_exit() (called from do_exit()) to a new function called from
> finish_task_switch(). However, release_task() (which calls
> cgroup_task_release()) can run either before or after finish_task_switch(),
> creating a race where cgroup_task_release() might try to remove the task from
> dying_tasks before or while it's being added.
>
> Move the list_del_init() from cgroup_task_release() to cgroup_task_free() to
> fix this race. cgroup_task_free() runs from __put_task_struct(), which is
> always after both paths, making the cleanup safe.
>
> Cc: Dan Schatzberg <dschatzberg@meta.com>
> Cc: Peter Zijlstra <peterz@infradead.org>
> Signed-off-by: Tejun Heo <tj@kernel.org>
>
> But there was the suspect that the matching commit might be
> d245698d727a ("cgroup: Defer task cgroup unlink until after the task
> is done switching out").
I see that after 260fbcb92bbea ("cgroup: Move dying_tasks cleanup from
cgroup_task_release() to cgroup_task_free()") it may be possible that
tasks on the dying_tasks list may drop their ->usage to zero (since the
actual unlinking only happens in __put_task_struct).
Most often those would be skipped due to PF_EXITING except for the case
of thread group leaders (which the reproducer stresses) whose refcount
apparently can drop to zero after task->signal->live > 0 made them
iterable :-/
A band-aid fix could be to use tryget_task_struct() in
css_task_iter_next() (I got that hint from a LLM) and "skip" zeroed
tasks. I see that commit fbe3fb103596b ("sched_ext: Replace
tryget_task_struct() with get_task_struct()"), assumes the iterator
always succeeds in obtaining the task reference (which was the
justification of tryget removal). I expect that sched_ext should still
be fine if dying_tasks with zero references are skipped. (What are they?
Tasks which literally no one should be interested in and they're only
waiting for __put_task_struct_rcu_cb() to be called [*]).
(I'm calling that band-aid because it'd resurrect usage of
tryget_task_struct() and it keeps the dying_tasks list a weird place to
be. If anyone has a better idea?)
The commit d245698d727a ("cgroup: Defer task cgroup unlink until after
the task is done switching out") seems a reasonable separation of the
stages to me.
Regards,
Michal
[*] Except for io_uring_drop_tctx_refs() that calls __put_task_struct()
directly (no RCU) but I'd argue the same, that those should not be
possibly iterated.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 265 bytes --]
prev parent reply other threads:[~2026-08-21 16:59 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <FR6P281MB6118B1A758F3278BC9BA795AF0DB2@FR6P281MB6118.DEUP281.PROD.OUTLOOK.COM>
2026-08-15 19:41 ` refcount_t: addition on 0; use-after-free, regression from 260fbcb92bbe ("cgroup: Move dying_tasks cleanup from cgroup_task_release() to cgroup_task_free()") Salvatore Bonaccorso
2026-08-21 16:59 ` Michal Koutný [this message]
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=aoh7Kbt_WbUcfUxh@localhost.localdomain \
--to=mkoutny@suse.com \
--cc=1144314@bugs.debian.org \
--cc=N.Feldt@mittwald.de \
--cc=carnil@debian.org \
--cc=cgroups@vger.kernel.org \
--cc=dschatzberg@meta.com \
--cc=hannes@cmpxchg.org \
--cc=linux-kernel@vger.kernel.org \
--cc=peterz@infradead.org \
--cc=regressions@lists.linux.dev \
--cc=stable@vger.kernel.org \
--cc=tj@kernel.org \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox