* [PATCH 0/2] sched_ext: two more context-safety fixes found in the NMI kfunc audit
@ 2026-09-03 3:29 Wanwu Li
2026-09-03 3:57 ` [PATCH 1/2] sched_ext: Make scx_locked_rq() return NULL from NMI Wanwu Li
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Wanwu Li @ 2026-09-03 3:29 UTC (permalink / raw)
To: tj, arighi, void, changwoo, emil; +Cc: liwanwu, sched-ext, linux-kernel
Continuing the audit that the sashiko bot kicked off on the NMI-reject
series, I went through every kfunc exposed to BPF_PROG_TYPE_TRACING
(the any / idle / cid context-filter sets). Two more context-safety
issues came out of it; this pair addresses both.
- Patch 1 fixes the scx_locked_rq() class (three "any"-category kfuncs
read scx_locked_rq() and take an unsafe fast path on a non-NULL
return from NMI). scx_locked_rq() gains an in_nmi() test at the
source that returns NULL from NMI, so the three callers fall onto
their unlocked paths. This supersedes guarding them individually.
- Patch 2 fixes an IRQ re-entrancy race on the per-CPU
per_cpu_unvisited nodemask scratch in the idle path. The search
scratch is protected only by preempt_disable(), which doesn't mask
IRQs; switch to irqsave instead. The NMI case is left as-is (a
misuse with no legitimate use and no crash risk).
Wanwu Li (2):
sched_ext: Make scx_locked_rq() return NULL from NMI
sched_ext: Protect the idle-search scratch nodemask with irqsave
kernel/sched/ext/idle.c | 14 ++++++++++++--
kernel/sched/ext/internal.h | 9 +++++++++
2 files changed, 21 insertions(+), 2 deletions(-)
--
2.25.1
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 1/2] sched_ext: Make scx_locked_rq() return NULL from NMI
2026-09-03 3:29 [PATCH 0/2] sched_ext: two more context-safety fixes found in the NMI kfunc audit Wanwu Li
@ 2026-09-03 3:57 ` Wanwu Li
2026-09-03 3:57 ` [PATCH 2/2] sched_ext: Protect the idle-search scratch nodemask with irqsave Wanwu Li
2026-09-03 18:42 ` [PATCH 0/2] sched_ext: two more context-safety fixes found in the NMI kfunc audit Tejun Heo
2 siblings, 0 replies; 4+ messages in thread
From: Wanwu Li @ 2026-09-03 3:57 UTC (permalink / raw)
To: tj; +Cc: arighi, void, changwoo, emil, sched-ext, linux-kernel, liwanwu
scx_locked_rq() reads the per-CPU scx_locked_rq_state, which tracks the
rq locked by the context running on the CPU. Tracing progs can call
kfuncs from NMI, and an NMI interrupts - rather than replaces - the
context that set scx_locked_rq_state, so a non-NULL read from NMI
falsely tells the caller that it holds the interrupted context's rq
lock.
Three "any"-category kfuncs read scx_locked_rq() on their success path
and take an unsafe fast path on a non-NULL return:
- scx_bpf_task_set_slice() writes p->scx.slice directly, racing
update_curr_scx()'s non-atomic read-modify-write of the same field.
- scx_bpf_dsq_nr_queued() resolves %SCX_DSQ_LOCAL to
(scx_locked_rq() ?: this_rq()) and can report the interrupted
context's local DSQ length instead of the caller's.
- scx_bpf_locked_rq() hands the interrupted context's rq to the BPF
program, which may then operate on it as if it owned the rq lock.
Make scx_locked_rq() return NULL from NMI so that all three take their
unlocked paths: scx_bpf_task_set_slice() stashes the request into the
atomic p->scx.slice_oob for application under the rq lock,
scx_bpf_dsq_nr_queued() falls back to this_rq(), and
scx_bpf_locked_rq() reports an error and aborts the scheduler through
the NMI-safe exit path.
The kfuncs that take scheduler locks reject NMI calls through
scx_kf_allowed_ctx() before reaching scx_locked_rq(), and the internal
callers only run from struct_ops callbacks, which never run in NMI, so
no other caller is affected.
Suggested-by: Tejun Heo <tj@kernel.org>
Link: https://lore.kernel.org/r/d84b31727f04e1ed0d40042ba1c09e61@kernel.org
Signed-off-by: Wanwu Li <liwanwu@kylinos.cn>
---
kernel/sched/ext/internal.h | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/kernel/sched/ext/internal.h b/kernel/sched/ext/internal.h
index a67277b0fee6..809e0ee0fd5f 100644
--- a/kernel/sched/ext/internal.h
+++ b/kernel/sched/ext/internal.h
@@ -2218,6 +2218,15 @@ static inline void scx_schedule_reenq_local(struct rq *rq, u64 reenq_flags)
*/
static inline struct rq *scx_locked_rq(void)
{
+ /*
+ * Tracing progs can call kfuncs from NMI. scx_locked_rq_state tracks
+ * the rq locked by the interrupted context, so a non-NULL read from
+ * NMI would falsely claim its lock. Return NULL from NMI so that
+ * callers take their unlocked paths.
+ */
+ if (unlikely(in_nmi()))
+ return NULL;
+
return __this_cpu_read(scx_locked_rq_state);
}
--
2.25.1
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 2/2] sched_ext: Protect the idle-search scratch nodemask with irqsave
2026-09-03 3:29 [PATCH 0/2] sched_ext: two more context-safety fixes found in the NMI kfunc audit Wanwu Li
2026-09-03 3:57 ` [PATCH 1/2] sched_ext: Make scx_locked_rq() return NULL from NMI Wanwu Li
@ 2026-09-03 3:57 ` Wanwu Li
2026-09-03 18:42 ` [PATCH 0/2] sched_ext: two more context-safety fixes found in the NMI kfunc audit Tejun Heo
2 siblings, 0 replies; 4+ messages in thread
From: Wanwu Li @ 2026-09-03 3:57 UTC (permalink / raw)
To: tj; +Cc: arighi, void, changwoo, emil, sched-ext, linux-kernel, liwanwu
pick_idle_cpu_from_online_nodes() uses the per-CPU per_cpu_unvisited
nodemask as scratch while walking the online nodes, protected only by
preempt_disable(). preempt_disable() does not mask IRQs and the idle
kfuncs are callable from IRQ-enabled contexts, so a nested invocation
on the same CPU can overwrite the mask with nodes_copy() while the
interrupted invocation is still iterating it, leading to a wrong node
traversal and a wrong idle CPU pick.
Switch to irqsave so a nested invocation can't run on the same CPU.
A stack-allocated nodemask would also close the race, but that would
enlarge the diff to fix a race that is already rare (per-node idle,
CONFIG_NUMA and a cross-node search all at once); irqsave is the
minimal fix for the context that actually triggers it. The NMI case is
deliberately not addressed: there is no legitimate reason to
call pick_idle from NMI and doing so poses no crash risk, so such a
caller is on its own.
Suggested-by: Tejun Heo <tj@kernel.org>
Link: https://lore.kernel.org/r/d84b31727f04e1ed0d40042ba1c09e61@kernel.org
Signed-off-by: Wanwu Li <liwanwu@kylinos.cn>
---
kernel/sched/ext/idle.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/kernel/sched/ext/idle.c b/kernel/sched/ext/idle.c
index d2973fb3af6d..93e2e0b2d1f8 100644
--- a/kernel/sched/ext/idle.c
+++ b/kernel/sched/ext/idle.c
@@ -153,7 +153,18 @@ static s32 pick_idle_cpu_from_online_nodes(const struct cpumask *cpus_allowed, i
nodemask_t *unvisited;
s32 cpu = -EBUSY;
- preempt_disable();
+ /*
+ * @per_cpu_unvisited is per-CPU scratch and the idle kfuncs can be
+ * called from IRQ-enabled contexts, so mask IRQs to keep a nested
+ * invocation from clobbering the mask an outer invocation is still
+ * iterating.
+ *
+ * NMI nesting is not handled: there is no legitimate reason to call
+ * pick_idle from NMI and doing so poses no crash risk, so such a
+ * caller is on its own.
+ */
+ guard(irqsave)();
+
unvisited = this_cpu_ptr(&per_cpu_unvisited);
/*
@@ -183,7 +194,6 @@ static s32 pick_idle_cpu_from_online_nodes(const struct cpumask *cpus_allowed, i
if (cpu >= 0)
break;
}
- preempt_enable();
return cpu;
}
--
2.25.1
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH 0/2] sched_ext: two more context-safety fixes found in the NMI kfunc audit
2026-09-03 3:29 [PATCH 0/2] sched_ext: two more context-safety fixes found in the NMI kfunc audit Wanwu Li
2026-09-03 3:57 ` [PATCH 1/2] sched_ext: Make scx_locked_rq() return NULL from NMI Wanwu Li
2026-09-03 3:57 ` [PATCH 2/2] sched_ext: Protect the idle-search scratch nodemask with irqsave Wanwu Li
@ 2026-09-03 18:42 ` Tejun Heo
2 siblings, 0 replies; 4+ messages in thread
From: Tejun Heo @ 2026-09-03 18:42 UTC (permalink / raw)
To: Wanwu Li; +Cc: arighi, void, changwoo, emil, sched-ext, linux-kernel
> Wanwu Li (2):
> sched_ext: Make scx_locked_rq() return NULL from NMI
> sched_ext: Protect the idle-search scratch nodemask with irqsave
Applied 1-2 to sched_ext/for-7.4.
Thanks.
--
tejun
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-09-03 18:42 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-03 3:29 [PATCH 0/2] sched_ext: two more context-safety fixes found in the NMI kfunc audit Wanwu Li
2026-09-03 3:57 ` [PATCH 1/2] sched_ext: Make scx_locked_rq() return NULL from NMI Wanwu Li
2026-09-03 3:57 ` [PATCH 2/2] sched_ext: Protect the idle-search scratch nodemask with irqsave Wanwu Li
2026-09-03 18:42 ` [PATCH 0/2] sched_ext: two more context-safety fixes found in the NMI kfunc audit Tejun Heo
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®