[PATCH v3 2/4] sched/debug: Protect lockless rq->rd access in print_dl_rq()

Aaron Tomlin <[email protected]>
Newsgroups gmane.linux.kernel
Message-ID <[email protected]>
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.

Because print_dl_rq() does not hold an RCU read lock while dereferencing
cpu_rq(cpu)->rd, an RCU grace period can elapse concurrently while
debugfs is reading the file. This allows free_rootdomain() to execute
kfree(old_rd), introducing a use-after-free race condition when
print_dl_rq() reads dl_bw->bw.

Fix this by fetching rq->rd using READ_ONCE() inside an RCU read-side
critical section. Holding the RCU read lock guarantees that the struct
root_domain memory remains valid while being accessed.

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 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c
index 78fc02d71710..e9d40a660346 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();
 
 #undef PU
 }
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.