From: Hyunwoo Kim <imv4bel@gmail.com>
To: Tim Chen <tim.c.chen@linux.intel.com>
Cc: "Chen, Yu C" <yu.c.chen@intel.com>, Kees Cook <kees@kernel.org>,
Christian Brauner <brauner@kernel.org>,
Alexander Viro <viro@zeniv.linux.org.uk>, Jan Kara <jack@suse.cz>,
Juri Lelli <juri.lelli@redhat.com>,
Vincent Guittot <vincent.guittot@linaro.org>,
Dietmar Eggemann <dietmar.eggemann@arm.com>,
Steven Rostedt <rostedt@goodmis.org>,
Ben Segall <bsegall@google.com>, Mel Gorman <mgorman@suse.de>,
Valentin Schneider <vschneid@redhat.com>,
K Prateek Nayak <kprateek.nayak@amd.com>,
Shrikanth Hegde <sshegde@linux.ibm.com>,
Qais Yousef <qyousef@layalina.io>,
Aaron Lu <ziqianlu@bytedance.com>,
Srikar Dronamraju <srikar@linux.ibm.com>,
Vineeth Remanan Pillai <vineethr@linux.ibm.com>,
linux-kernel@vger.kernel.org, linux-mm@kvack.org,
linux-fsdevel@vger.kernel.org, Ingo Molnar <mingo@redhat.com>,
Peter Zijlstra <peterz@infradead.org>,
"chen.yu@linux.dev" <chen.yu@linux.dev>,
imv4bel@gmail.com
Subject: Re: [PATCH] sched/cache: Fix use-after-free of the mm replaced by exec
Date: Tue, 1 Sep 2026 06:44:25 +0900 [thread overview]
Message-ID: <apX1ufR1reYZ9LBc@v4bel> (raw)
In-Reply-To: <a1a0bc24108e68656041dc17824d6b88b4120058.camel@linux.intel.com>
On Mon, Aug 31, 2026 at 01:25:06PM -0700, Tim Chen wrote:
> On Tue, 2026-09-01 at 04:28 +0900, Hyunwoo Kim wrote:
> > On Mon, Aug 31, 2026 at 12:22:45PM +0800, Chen, Yu C wrote:
> > > Hi Hyunwoo,
> > >
> > > On 8/30/2026 3:30 PM, Hyunwoo Kim wrote:
> > > > When the waker cannot use the wakelist, ttwu_queue() takes the target rq
> > > > lock and goes down into update_curr(). If the target rq belongs to another
> > > > CPU, the task handed to account_mm_sched() is the one running on that CPU,
> > > > not the task being woken.
> > > >
> > > > account_mm_sched() reads p->mm and updates mm->sc_stat. Nothing keeps that
> > > > mm alive. The rq lock and rq->cpu_epoch_lock it holds have nothing to do
> > > > with the lifetime of the mm.
> > > >
> > > > If that task happens to be in execve(), exec_mmap() points tsk->mm and
> > > > tsk->active_mm at the new mm, and exec_mm_put_old() from setup_new_exec()
> > > > drops the old one. free_bprm() does the same when exec fails. On the way
> > > > from mmput() down to __mmdrop(), mm_destroy_sched() calls free_percpu() on
> > > > sc_stat.pcpu_sched and free_mm() returns the mm_struct.
> > > >
> > > > Whoever already read the old pointer keeps using it. It adds to runtime in
> > > > the freed per-cpu area and reads sc_stat in the freed mm_struct. Depending
> > > > on the condition it also writes sc_stat.cpu. That is a use-after-free.
> > > >
> > > > Commit 9f23469401b0 ("sched/cache: Fix potential NULL mm pointer access")
> > > > changed the remaining p->mm dereference to the local variable, and said the
> > > > active_mm reference keeps the structure allocated. That holds for the other
> > > > paths that detach an mm, since they take an mmgrab_lazy_tlb() reference.
> > > > exec reassigns active_mm to the new mm as well, so that reference is gone.
> > > > What is left is the mm_users reference in bprm->old_mm, and dropping it is
> > > > the free.
> > > >
> > > > CPU0 CPU1
> > > >
> > > > write(pipe)
> > > > try_to_wake_up()
> > > > ttwu_queue() // takes rq0 lock
> > > > enqueue_task_fair()
> > > > update_curr()
> > > > update_se()
> > > > account_mm_sched()
> > > > mm = rq0->curr->mm
> > > > // old mm
> > > > execve()
> > > > exec_mmap() // tsk->mm = new mm
> > > > setup_new_exec()
> > > > exec_mm_put_old()
> > > > mmput() -> ... -> __mmdrop()
> > > > mm_destroy_sched() // free_percpu()
> > > > free_mm()
> > > > read mm->sc_stat.epoch
> > > > // use-after-free
> > > >
> > >
> > > Ah, thanks for catching this.
> > >
> > > > ---
> > > > fs/exec.c | 1 +
> > > > include/linux/sched.h | 4 ++++
> > > > kernel/events/core.c | 2 ++
> > > > kernel/sched/fair.c | 16 ++++++++++++++++
> > > > 4 files changed, 23 insertions(+)
> > > >
> > > > diff --git a/fs/exec.c b/fs/exec.c
> > > > index 745f6eb5279e6..6194c38807980 100644
> > > > --- a/fs/exec.c
> > > > +++ b/fs/exec.c
> > > > @@ -916,6 +916,7 @@ static void exec_mm_put_old(struct mm_struct *old_mm)
> > > > {
> > > > setmax_mm_hiwater_rss(¤t->signal->maxrss, old_mm);
> > > > mm_update_next_owner(old_mm);
> > > > + sched_cache_exec_done();
> > > > mmput(old_mm);
> > > > }
> > > > diff --git a/include/linux/sched.h b/include/linux/sched.h
> > > > index 8b3d47a325cca..6ae31bffe049e 100644
> > > > --- a/include/linux/sched.h
> > > > +++ b/include/linux/sched.h
> > > > @@ -2415,10 +2415,14 @@ struct sched_cache_stat {
> > > > int cpu;
> > > > } ____cacheline_aligned_in_smp;
> > > > +void sched_cache_exec_done(void);
> > > > +
> > > > #else
> > > > struct sched_cache_stat { };
> > > > +static inline void sched_cache_exec_done(void) { }
> > > > +
> > > > #endif
> > > > #ifndef MODULE
> > > > diff --git a/kernel/events/core.c b/kernel/events/core.c
> > > > index a6c8e38a31104..2f29cbccf03f1 100644
> > > > --- a/kernel/events/core.c
> > > > +++ b/kernel/events/core.c
> > > > @@ -5427,6 +5427,8 @@ attach_task_ctx_data(struct task_struct *task, struct kmem_cache *ctx_cache,
> > > > if (!cd)
> > > > return -ENOMEM;
> > > > + /* @old, loaded by the try_cmpxchg() below, is only stable under RCU. */
> > > > + guard(rcu)();
> > >
> > > Is this change related to this UAF issue?
> >
> > Duh.. that one is unrelated. My mistake.
> >
> > >
> > > > +/* exec() has switched to the new mm and is about to drop the old one. */
> > > > +void sched_cache_exec_done(void)
> > > > +{
> > > > + struct rq_flags rf;
> > > > + struct rq *rq;
> > > > +
> > > > + /*
> > > > + * account_mm_sched() dereferences rq->curr->mm under this rq's lock,
> > > > + * so a remote CPU can still be using the old mm. The lock cycle waits
> > > > + * for it, and the store to tsk->mm cannot be reordered past the
> > > > + * release, so later acquirers see the new mm.
> > > > + */
> > > > + rq = this_rq_lock_irq(&rf);
> > >
> > > A smart fix, learnt! It behaves like a synchronize_rcu() to protect against
> > > the read in account_mm_sched(). Small open: since the context of invoking
> > > account_mm_sched() is preemption-disabled, I wonder if we can simply use
> > > synchronize_rcu() directly instead of this_rq_lock_irq() - just to avoid
> > > contention for rq-lock in heavy system?
>
> Thanks to Kyunwoo to show this potential use-after-free problem.
It is a real, triggerable issue.
>
> While synchronize_rcu() avoids rq lock, it could introduce a long delay
> of a full grace period that will be undesirable.
>
>
> I think a better fix is to decouple sc_stat from mm and make it its own structure.
> Then we can do proper refcounting and rcu management on sc_stat itself.
> We will let task points to sc_stat with proper ref count and change the access
> as task->sc_stat directly nstead of via task->mm->sc_stat.
>
> Then we will know sc_stat exists in account_mm_sched() execution
> as long as a task points to it and do not have to worry that sc_stat is
> released due to the mm life cycle.
>
> In the first 2 patches of the prctl control series for cache aware scheduling
> we posted, we introduced the change I described above and should
> fix this issue without additional rq locking or synchronize_rcu() overhead.
> https://lore.kernel.org/lkml/cover.1787955777.git.tim.c.chen@linux.intel.com/
>
> We should probably prioritize to merge the first couple of patches from
> that series now in light of this issue. It will make maintenance and future
> modification of the cache aware scheduling code easier.
That is a fairly large change.
If you add a Reported-by: tag for this UAF to the patch, I am fine with
handling it that way.
Best regards,
Hyunwoo Kim
next prev parent reply other threads:[~2026-08-31 21:44 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-30 7:30 [PATCH] sched/cache: Fix use-after-free of the mm replaced by exec Hyunwoo Kim
2026-08-31 4:22 ` Chen, Yu C
2026-08-31 19:28 ` Hyunwoo Kim
2026-08-31 20:25 ` Tim Chen
2026-08-31 21:44 ` Hyunwoo Kim [this message]
2026-08-31 22:10 ` Tim Chen
2026-09-01 3:00 ` Chen Yu
2026-09-01 10:44 ` Hyunwoo Kim
2026-09-01 20:49 ` Tim Chen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=apX1ufR1reYZ9LBc@v4bel \
--to=imv4bel@gmail.com \
--cc=brauner@kernel.org \
--cc=bsegall@google.com \
--cc=chen.yu@linux.dev \
--cc=dietmar.eggemann@arm.com \
--cc=jack@suse.cz \
--cc=juri.lelli@redhat.com \
--cc=kees@kernel.org \
--cc=kprateek.nayak@amd.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=mgorman@suse.de \
--cc=mingo@redhat.com \
--cc=peterz@infradead.org \
--cc=qyousef@layalina.io \
--cc=rostedt@goodmis.org \
--cc=srikar@linux.ibm.com \
--cc=sshegde@linux.ibm.com \
--cc=tim.c.chen@linux.intel.com \
--cc=vincent.guittot@linaro.org \
--cc=vineethr@linux.ibm.com \
--cc=viro@zeniv.linux.org.uk \
--cc=vschneid@redhat.com \
--cc=yu.c.chen@intel.com \
--cc=ziqianlu@bytedance.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.