[PATCH v2] sched/numa: Prevent race on sysctl_numa_balancing static key
From: Chen Jinghuang
Date: Tue Aug 04 2026 - 06:28:42 EST
While fuzzing with syzkaller, a concurrent 1/0 write race to
/proc/sys/kernel/numa_balancing was found that trips a jump_label
WARN_ON_ONCE().
Concurrent writes of 1/0 to /proc/sys/kernel/numa_balancing enable/disable
the same static key. Enable sets key->enabled to -1 while holding the
lock, restoring it to 1 only after jump_label_update(); disable checks
enabled before taking the lock. Under concurrency, disable reads -1 and
trips WARN_ON_ONCE().
Timeline:
write 1 → enable write 0 → disable
│ │
├─ static_key_enable_cpuslocked() ├─ static_key_disable_cpuslocked()
│ jump_label_lock() │ atomic_read(enabled) ← before lock
│ atomic_set(enabled, -1) ◄───────┼── reads -1
│ jump_label_update() │ WARN_ON_ONCE(enabled!=0)
│ atomic_set_release(enabled,1) │ return ← disable skipped
│ jump_label_unlock() │
Serialize the enable/disable switch at the convergence point in
sysctl_numa_balancing() with a mutex, so the transient -1 in
key->enabled never leaks to a concurrent disable and this WARN_ON_ONCE
no longer trips.
This follows existing kernel practice, e.g. timer_key_mutex guarding
timers_update_migration() (kernel/time/timer.c) and perf_sched_mutex
guarding static_branch_enable() (kernel/events/core.c).
Fixes: 1dbb6704de91 ("jump_label: Fix concurrent static_key_enable/disable()")
Reported-by: Zhang zhaotian <zhangzhaotian@xxxxxxxxxxxxxx>
Suggested-by: K Prateek Nayak <kprateek.nayak@xxxxxxx>
Signed-off-by: Chen Jinghuang <chenjinghuang2@xxxxxxxxxx>
---
Changes since v1: https://lore.kernel.org/lkml/20260803123046.297426-1-chenjinghuang2@xxxxxxxxxx/
- Move numabalancing_mutex into the write path of sysctl_numa_balancing()
(no locking needed during init).
- Use guard(mutex) instead of the explicit mutex_lock()/mutex_unlock().
---
kernel/sched/core.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 96226707c2f6..87c7c727b743 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -4650,6 +4650,8 @@ static void reset_memory_tiering(void)
}
}
+static DEFINE_MUTEX(numabalancing_mutex);
+
static int sysctl_numa_balancing(const struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
@@ -4666,6 +4668,7 @@ static int sysctl_numa_balancing(const struct ctl_table *table, int write,
if (err < 0)
return err;
if (write) {
+ guard(mutex)(&numabalancing_mutex);
if (!(sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING) &&
(state & NUMA_BALANCING_MEMORY_TIERING))
reset_memory_tiering();
--
2.34.1