Re: [PATCH] sched/cpuacct: fix use-after-free in cpuacct_account_field()

From: Tejun Heo

Date: Tue Apr 07 2026 - 15:01:17 EST


Hello,

On Tue, Apr 07, 2026 at 09:58:11AM +0200, Peter Zijlstra wrote:
> On Sat, Apr 04, 2026 at 10:47:42PM -0400, Rik van Riel wrote:
> > cpuacct_css_free() calls free_percpu() on ca->cpustat and ca->cpuusage,
> > then kfree(ca). However, a timer interrupt on another CPU can
> > concurrently access this data through cpuacct_account_field(), which
> > walks the cpuacct hierarchy via task_ca()/parent_ca() and performs
> > __this_cpu_add(ca->cpustat->cpustat[index], val).
> >
> > The race window exists because put_css_set_locked() drops the CSS
> > reference (css_put) before the css_set is RCU-freed (kfree_rcu). This
> > means the CSS percpu_ref can reach zero and trigger the css_free chain
> > while readers obtained the CSS pointer from the old css_set that is
> > still visible via RCU.
> >
> > Although css_free_rwork_fn is already called after one RCU grace period,
> > the css_set -> CSS reference drop in put_css_set_locked() creates a
> > window where the CSS free chain races with readers still holding the
> > old css_set reference.
>
> To me this reads like a cgroup fail, not a cpuacct fail per se. But I'm
> forever confused there. TJ?

css_free() is already called after a RCU grace period after the refcnt
reaches zero. The patch is adding another RCU grace period in the path,
which likely is just patching over the underlying problem.

cpuacct_account_field() is called from ticks. The task->cgroups is the RCU
protected pointer to the css_set from which the cpuacct pointer is read.
Each css_set pins the csses that it points to. The cpuacct's refcnt can't
reach zero as long as task->cgroups point to it and if the timer tick is
what's accessing it, the built-in RCU grace period in cgroup core should be
enough.

How reproducible is the problem? Do you have the KASAN report?

Thanks.

--
tejun