Re: [PATCH v4 1/5] sched/debug: Protect lockless rq->rd access in print_dl_rq()
Aaron Tomlin <[email protected]>
| Newsgroups | gmane.linux.kernel |
|---|---|
| Message-ID | <dhan7n5pirwqppnttiulycf6fg36wmwrzvkrgvbdc2vlrpu5hw@4jbfwacxun63> |
On Mon, Aug 10, 2026 at 03:53:38PM +0200, Daniel Vacek wrote: > On Mon, 10 Aug 2026 at 03:58, Aaron Tomlin <[email protected]> wrote: > > In print_dl_rq(), cpu_rq(cpu)->rd is dereferenced locklessly to display > > deadline bandwidth statistics. > > > > During CPU hot-unplug or cgroup cpuset repartitioning events, > > partition_sched_domains() calls cpu_attach_domain(), which executes > > rq_attach_root() to detach the CPU from its root_domain. When the > > reference count of the detached root_domain drops to zero, > > rq_attach_root() calls call_rcu(&old_rd->rcu, free_rootdomain) to > > schedule memory teardown after an RCU grace period. > > > > However, rq_attach_root() previously updated rq->rd using a plain C store > > without an RCU publication barrier (i.e., rcu_assign_pointer()). Without a > > release memory barrier on the writer side, CPU or compiler reordering could > > allow the new rq->rd pointer store to become visible to other CPUs before > > the initialization writes to rd->dl_bw are committed. > > > > Furthermore, because print_dl_rq() did not hold an RCU read lock while > > dereferencing cpu_rq(cpu)->rd, an RCU grace period could elapse > > concurrently while debugfs is reading the file, allowing > > free_rootdomain() to execute kfree(old_rd) and causing a use-after-free > > race condition when print_dl_rq() reads dl_bw->bw. > > > > Resolve this by using rcu_assign_pointer(rq->rd, rd) in rq_attach_root() to > > guarantee a release memory barrier when publishing a root_domain. > > Finally, fetch rq->rd using READ_ONCE() inside an RCU read-side critical > > section in print_dl_rq(). > > > > Fixes: 02968ccf7b80 ("sched: add /proc/sched_debug file") > > Reported-by: sashiko-bot <[email protected]> > > Signed-off-by: Aaron Tomlin <[email protected]> > > --- > > kernel/sched/debug.c | 12 +++++++++--- > > kernel/sched/topology.c | 2 +- > > 2 files changed, 10 insertions(+), 4 deletions(-) > > > > diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c > > index 40584b27ea0c..2c2156dfab00 100644 > > --- a/kernel/sched/debug.c > > +++ b/kernel/sched/debug.c > > @@ -1081,6 +1081,7 @@ void print_rt_rq(struct seq_file *m, int cpu, struct rt_rq *rt_rq) > > void print_dl_rq(struct seq_file *m, int cpu, struct dl_rq *dl_rq) > > { > > struct dl_bw *dl_bw; > > + struct root_domain *rd; > > > > SEQ_printf(m, "\n"); > > SEQ_printf(m, "dl_rq[%d]:\n", cpu); > > @@ -1089,9 +1090,14 @@ void print_dl_rq(struct seq_file *m, int cpu, struct dl_rq *dl_rq) > > SEQ_printf(m, " .%-30s: %lu\n", #x, (unsigned long)(dl_rq->x)) > > > > PU(dl_nr_running); > > - dl_bw = &cpu_rq(cpu)->rd->dl_bw; > > - SEQ_printf(m, " .%-30s: %lld\n", "dl_bw->bw", dl_bw->bw); > > - SEQ_printf(m, " .%-30s: %lld\n", "dl_bw->total_bw", dl_bw->total_bw); > > + rcu_read_lock(); > > + rd = READ_ONCE(cpu_rq(cpu)->rd); > > + if (rd) { > > + dl_bw = &rd->dl_bw; > > + SEQ_printf(m, " .%-30s: %lld\n", "dl_bw->bw", dl_bw->bw); > > + SEQ_printf(m, " .%-30s: %lld\n", "dl_bw->total_bw", dl_bw->total_bw); > > + } > > + rcu_read_unlock(); > > Hey Aaron. How about simple > > @@ -1089,7 +1089,8 @@ void print_dl_rq(struct seq_file *m, int cpu, > struct dl_rq *dl_rq) > SEQ_printf(m, " .%-30s: %lu\n", #x, (unsigned long)(dl_rq->x)) > > PU(dl_nr_running); > - dl_bw = &cpu_rq(cpu)->rd->dl_bw; > + guard(rcu)(); > + dl_bw = &rcu_dereference(cpu_rq(cpu)->rd)->dl_bw; > SEQ_printf(m, " .%-30s: %lld\n", "dl_bw->bw", dl_bw->bw); > SEQ_printf(m, " .%-30s: %lld\n", "dl_bw->total_bw", dl_bw->total_bw); Hey Daniel, Very elegant! As mentioned here [1], since I introduced rcu_assign_pointer(rq->rd, rd) in rq_attach_root(), I will now annotate 'rd' of struct rq with __rcu. [1]: https://lore.kernel.org/lkml/7nn7bjzwovzu67ke6zlmmg4bv3utc6jv2sbzpklzumr3zt7csy@ip5icrcz6b35/ > > > > #undef PU > > } > > diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c > > index 622e2e01974c..b411cc00029c 100644 > > --- a/kernel/sched/topology.c > > +++ b/kernel/sched/topology.c > > @@ -496,7 +496,7 @@ void rq_attach_root(struct rq *rq, struct root_domain *rd) > > } > > > > atomic_inc(&rd->refcount); > > - rq->rd = rd; > > + rcu_assign_pointer(rq->rd, rd); > > > > cpumask_set_cpu(rq->cpu, rd->span); > > if (cpumask_test_cpu(rq->cpu, cpu_active_mask)) > > -- > > 2.55.0 > > Kind regards, -- Aaron Tomlin