Re: [PATCH v2] sched/mmcid: Synchronize exec MM teardown with affinity updates
From: Mathieu Desnoyers
Date: Sun Nov 08 2026 - 08:34:06 EST
On 2026-11-08 08:26, Weiming Shi wrote:
set_cpus_allowed_common() passes a remote task's p->mm to[...]
mm_update_cpus_allowed(), but the scheduler locks do not stabilize that
pointer against exec. A same-owner sched_setaffinity() can race the target
replacing and freeing its old mm. Current kernels are only incidentally
serialized by futex_exec_done() taking p->pi_lock when CONFIG_FUTEX=y;
valid CONFIG_FUTEX=n builds remain vulnerable.
BUG: KASAN: slab-use-after-free in set_cpus_allowed_common (kernel/sched/core.c:10835 kernel/sched/core.c:2829)
Read of size 4 at addr ffff88810991b04c by task poc/5127
Call Trace:
set_cpus_allowed_common (kernel/sched/core.c:10835 kernel/sched/core.c:2829)
set_cpus_allowed_fair (kernel/sched/fair.c:10046)
__set_cpus_allowed_ptr_locked (kernel/sched/core.c:2842 kernel/sched/core.c:3224)
__set_cpus_allowed_ptr (kernel/sched/core.c:3258)
__sched_setaffinity (kernel/sched/syscalls.c:1161)
sched_setaffinity (kernel/sched/syscalls.c:1239)
__x64_sys_sched_setaffinity (kernel/sched/syscalls.c:1275)
do_syscall_64 (arch/x86/entry/syscall_64.c:84)
entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
Freed by task 5128:
kmem_cache_free (mm/slub.c:6757)
__mmput (kernel/fork.c:1204)
mmput (kernel/fork.c:1215)
setup_new_exec (fs/exec.c:1395)
load_elf_binary (fs/binfmt_elf.c:1024)
bprm_execve (fs/exec.c:1865)
do_execveat_common.isra.0 (fs/exec.c:1963)
__x64_sys_execve (fs/exec.c:2036)
do_syscall_64 (arch/x86/entry/syscall_64.c:84)
entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
After exec publishes the new p->mm, acquire and release the task's runqueue
lock before the old mm can be dropped. Affinity updates already hold that
lock across set_cpus_allowed_common(), so the barrier waits for any update
that loaded the old mm. Updates that start afterwards observe the new mm.
This keeps allowed-mask updates synchronous, including transient affinity
expansions, without changing the MM-CID fork, exit, or scheduling paths.
This change was developed with assistance from an LLM.
Fixes: 7e019dcc470f ("sched: Improve cache locality of RSEQ concurrency IDs for intermittent workloads")
Cc: stable@xxxxxxxxxxxxxxx
Reported-by: <co+cab19e2ebe4d535a@xxxxxxx>
Signed-off-by: Weiming Shi <bestswngs@xxxxxxxxx>
---
Changes in v2:
- Replace the schedule-in deferral with an exec-side runqueue-lock barrier.
- Keep affinity expansions synchronous, including expansions narrowed before
the next schedule-in.
- Avoid changes to the MM-CID fork, exit, and scheduling state machine.
fs/exec.c | 1 +
include/linux/sched.h | 2 ++
kernel/sched/core.c | 6 ++++++
3 files changed, 9 insertions(+)
diff --git a/fs/exec.c b/fs/exec.c
index 8d2b14e1d455..2e1cac6a3de7 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -905,6 +905,7 @@ static int exec_mmap(struct linux_binprm *bprm)
BUG_ON(active_mm != old_mm);
/* Defer teardown to setup_new_exec(), outside the exec locks. */
bprm->old_mm = old_mm;
+ sched_mm_cid_exec_mmap(tsk);
} else {
mmdrop_lazy_tlb(active_mm);
}
+/* Wait for affinity updates that may still be using the old MM. */
+void sched_mm_cid_exec_mmap(struct task_struct *t)
+{
+ guard(task_rq_lock)(t);
+}
Using a lock side-effect as synchronization of what sits _outside_
of the lock is an interesting synchronization pattern. If this actually
works, it should be thoroughly documented.
It should come with at least one sequence diagram explaining why this
works, along with acquire-release MO explanation.
Thanks,
Mathieu
--
Mathieu Desnoyers
EfficiOS Inc.
https://www.efficios.com