The Linux Kernel Mailing List
 help / color / mirror / Atom feed
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 --]

      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