[PATCH v2] sched/numa: Prevent race on sysctl_numa_balancing static key

Chen Jinghuang <[email protected]>
Newsgroups gmane.linux.kernel
Message-ID <[email protected]>
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 <[email protected]>
Suggested-by: K Prateek Nayak <[email protected]>
Signed-off-by: Chen Jinghuang <[email protected]>
---
Changes since v1: https://lore.kernel.org/lkml/[email protected]/
- 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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.