[PATCH 1/3] cgroup/cpuset: Protect is_in_v2_mode() in cpuset_num_cpus()

From: Andrea Righi

Date: Tue Sep 29 2026 - 04:46:48 EST


cpuset_num_cpus() enters its RCU read-side section only after checking
is_in_v2_mode(). When cpuset is bound to a v1 hierarchy, is_in_v2_mode()
dereferences cpuset_cgrp_subsys.root, which is freed via kfree_rcu()
once that hierarchy is destroyed and cpuset is rebound to the default
hierarchy. A preemptible caller outside RCU can therefore read the flags
of a freed root.

The only current caller, fair's group share calculation, runs under the
rq lock with preemption disabled, so it can't hit this. However, the
helper already means to protect itself with RCU, and upcoming sched_ext
support exposes it to sleepable BPF programs.

Take the RCU read lock before is_in_v2_mode() so that the whole lookup
is protected regardless of the caller's context.

Signed-off-by: Andrea Righi <arighi@xxxxxxxxxx>
---
kernel/cgroup/cpuset.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index d100634fa12b7..03fa9472c0893 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -4292,8 +4292,12 @@ int cpuset_num_cpus(struct cgroup *cgrp)
int nr = num_online_cpus();
struct cpuset *cs;

+ /*
+ * is_in_v2_mode() dereferences cpuset's hierarchy root, which can be a
+ * v1 root freed via kfree_rcu() on unmount.
+ */
+ guard(rcu)();
if (is_in_v2_mode()) {
- guard(rcu)();
cs = css_cs(cgroup_e_css(cgrp, &cpuset_cgrp_subsys));
if (cs)
nr = cpumask_weight(cs->effective_cpus);
--
2.55.0