All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kuba Piecuch <jpiecuch@google.com>
To: <sashiko-reviews@lists.linux.dev>, Kuba Piecuch <jpiecuch@google.com>
Cc: <sched-ext@lists.linux.dev>
Subject: Re: [PATCH 6.18.y 2/2] sched_ext: Fix scx_flush_disable_work() UAF race
Date: Wed, 04 Nov 2026 11:31:49 +0000	[thread overview]
Message-ID: <DMME0YKQQQ4D.PNR5XMBJHUV7@google.com> (raw)
In-Reply-To: <sashiko-outbox-195908@kernel.org>

On Tue Nov 3, 2026 at 7:15 PM UTC, sashiko-bot wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Critical] sched_ext: premature return in scx_flush_disable_work() on SCX_EXIT_DONE
> --
>
> commit fe0315a0808b95d01cd45741655ca111deb8afc1
> Author: Cheng-Yang Chou <yphbchou0911@gmail.com>
>
> sched_ext: Fix scx_flush_disable_work() UAF race
>
> This commit updates scx_flush_disable_work() to loop while syncing
> sch->error_irq_work and flushing sch->disable_work until sch->exit_kind
> reaches SCX_EXIT_NONE or SCX_EXIT_DONE. This aims to close a race window
> where bpf_scx_unreg() frees the scheduler before a concurrent scx_vexit()
> queues the disable work.
>
>> diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c
>> index d2460a88fba35..78f15ddc0aa5a 100644
>> --- a/kernel/sched/ext.c
>> +++ b/kernel/sched/ext.c
>> @@ -4217,8 +4217,13 @@ static void scx_disable(struct scx_sched *sch, enum scx_exit_kind kind)
>>   */
>>  static void scx_flush_disable_work(struct scx_sched *sch)
>>  {
>> -	irq_work_sync(&sch->error_irq_work);
>> -	kthread_flush_work(&sch->disable_work);
>> +	int kind;
>> +
>> +	do {
>> +		irq_work_sync(&sch->error_irq_work);
>> +		kthread_flush_work(&sch->disable_work);
>> +		kind = atomic_read(&sch->exit_kind);
>> +	} while (kind != SCX_EXIT_NONE && kind != SCX_EXIT_DONE);
>
> [Severity: Critical]
> Can scx_flush_disable_work() return while sch->disable_work is still
> running if sch->exit_kind transitions to SCX_EXIT_DONE between
> kthread_flush_work() and atomic_read(&sch->exit_kind)?
>
> In scx_disable_workfn(), sch->exit_kind is set to SCX_EXIT_DONE at the
> start of the work function before running scx_root_disable(sch):
>
> kernel/sched/ext.c:scx_disable_workfn() {
> 	...
> 	while (true) {
> 		if (kind == SCX_EXIT_DONE)	/* already disabled? */
> 			return;
> 		WARN_ON_ONCE(kind == SCX_EXIT_NONE);
> 		if (atomic_try_cmpxchg(&sch->exit_kind, &kind, SCX_EXIT_DONE))
> 			break;
> 	}
> 	ei->kind = kind;
> 	ei->reason = scx_exit_reason(ei->kind);
>
> 	scx_root_disable(sch);
> }
>
> If CPU A is in scx_vexit() (or scx_disable()) between scx_claim_exit() and
> queuing the work while CPU B enters bpf_scx_unreg() ->
> scx_flush_disable_work():
>
> 1. On CPU B's first pass, irq_work_sync(&sch->error_irq_work) and
>    kthread_flush_work(&sch->disable_work) return as no-ops because neither
>    work item has been queued yet.
> 2. CPU A then queues sch->error_irq_work (or sch->disable_work), and
>    scx_disable_workfn() starts running on sch->helper, sets sch->exit_kind
>    to SCX_EXIT_DONE, and enters scx_root_disable(sch).
> 3. CPU B executes kind = atomic_read(&sch->exit_kind), observes
>    SCX_EXIT_DONE, and breaks out of the loop without ever having flushed
>    the running sch->disable_work.
>
> Could that allow bpf_scx_unreg() to call kobject_put(&sch->kobj) and return
> to free the BPF struct_ops map while scx_root_disable(sch) and ops.exit()
> are still executing? Would reading sch->exit_kind before syncing and
> flushing (or performing a final kthread_flush_work(&sch->disable_work)
> after the loop) avoid returning before scx_disable_workfn() finishes?

This is an upstream bug, sent a fix for review:
https://lore.kernel.org/all/20261104111449.3984318-1-jpiecuch@google.com/

I will send the fix for 6.18 once it is merged.

I don't think it should block this series. This series reduces the race
window from potentially multi-ms, very easy to reproduce with the right
BPF progs, to a few instructions between the flush and the exit_kind read,
which I failed to reproduce without adding artificial delays in the kernel.


  reply	other threads:[~2026-11-04 11:31 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-03 19:07 [PATCH 6.18.y 0/2] sched_ext: Fix bpf_scx_unreg() teardown race Kuba Piecuch
2026-11-03 19:07 ` [PATCH 6.18.y 1/2] sched_ext: sync disable_irq_work in bpf_scx_unreg() Kuba Piecuch
2026-11-03 19:07 ` [PATCH 6.18.y 2/2] sched_ext: Fix scx_flush_disable_work() UAF race Kuba Piecuch
2026-11-03 19:15   ` sashiko-bot
2026-11-04 11:31     ` Kuba Piecuch [this message]
2026-11-04 15:03 ` [PATCH 6.18.y 0/2] sched_ext: Fix bpf_scx_unreg() teardown race Sasha Levin

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=DMME0YKQQQ4D.PNR5XMBJHUV7@google.com \
    --to=jpiecuch@google.com \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=sched-ext@lists.linux.dev \
    /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.