[PATCH] sched/cache: Fix use-after-free of mm in account_mm_sched()

From: Zenghui Yu (Huawei)

Date: Mon Sep 14 2026 - 10:00:18 EST


account_mm_sched() accounts runtime against rq->curr and dereferences its
->mm: it updates the percpu chunk mm->sc_stat.pcpu_sched and may write
mm->sc_stat.cpu = -1.

update_se(), which samples rq->curr and calls account_mm_sched(), is not
only called from local contexts (tick, context switch) but also through
update_curr() from enqueue/dequeue paths, which frequently run on a remote
CPU while holding this rq's lock (cross-CPU try_to_wake_up(), load
balancing).

In those remote contexts rq->curr is a task concurrently running on its
home CPU. The rq lock guarantees that rq->curr's identity does not change,
but it says nothing about the lifetime of rq->curr->mm: that task does not
need the rq lock to execute execve or exit, and switches and drops its ->mm
under task_lock() and mmput(), neither of which orders against the remote
CPU. A remote CPU can therefore sample a valid mm pointer right before it
is freed and write to it afterwards, corrupting the freed mm_struct (and
the pcpu_sched percpu chunk, which mm_destroy_sched() frees even earlier).

Observed with CONFIG_SLUB_DEBUG=y as a sporadic "Poison overwritten" report
on the mm_struct cache, with the overwritten bytes resolving to
&mm->sc_stat.cpu.

Only account the physically running task (p == current), whose ->mm cannot
go away while it is the one executing this code. Local tick, context
switch and sched_ttwu_pending() paths are unaffected; updates skipped in
remote contexts only cause minor under-accounting of the sc_stat runtime
heuristics.

Fixes: df0d98475954 ("sched/cache: Introduce infrastructure for cache-aware load balancing")
Assisted-by: GLM-5.3 OpenCode
Signed-off-by: Zenghui Yu (Huawei) <zenghui.yu@xxxxxxxxx>
---
kernel/sched/fair.c | 3 +++
1 file changed, 3 insertions(+)

diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index ade1eceb39b8..2bbf59370d23 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -1731,6 +1731,9 @@ void account_mm_sched(struct rq *rq, struct task_struct *p, s64 delta_exec)
int mm_sched_llc = -1;
unsigned long epoch;

+ if (p != current)
+ return;
+
if (!sched_cache_enabled())
return;

--
2.53.0