Sched_ext development
 help / color / mirror / Atom feed
* [PATCH] sched_ext: Close the pre-enable ops error claim window
@ 2026-09-10  8:45 Qiurong Fang
  2026-09-10  9:01 ` sashiko-bot
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Qiurong Fang @ 2026-09-10  8:45 UTC (permalink / raw)
  To: tj; +Cc: sched-ext, void, arighi, changwoo

From: fangqiurong <fangqiurong@kylinos.cn>

scx_alloc_and_add_sched() publishes ops->priv before
scx_root_enable_workfn() switches the state to SCX_ENABLING. An error
claimed via scx_bpf_error_bstr() from an associated BPF program in that
window is consumed by scx_disable_workfn(), which takes the pre-enable
shortcut in scx_root_disable(). The shortcut returns without any
teardown and restores SCX_DISABLED with an unconditional
scx_set_enable_state() xchg racing the enable workfn's own transition.
The enable then completes with the claim consumed: the scheduler stays
up but can never be disabled again, and bpf_scx_unreg() frees it while
still in use, resulting in a use-after-free. Both WARN_ON_ONCE()s fire
back to back:

  WARNING: kernel/sched/ext/ext.c:7522 at scx_root_enable_workfn+0xeec/0x1be0, CPU#3: scx_enable_help/276
  WARNING: kernel/sched/ext/ext.c:6398 at scx_root_disable+0xb50/0xdb8, CPU#0: sched_ext_helpe/664

scx_root_enable_workfn() switches to SCX_ENABLING before the
allocation, so ops->priv is never visible while SCX_DISABLED and
claimed errors get the regular scx_root_disable() teardown serialized
on scx_enable_mutex behind the in-flight enable.

Fixes: 105dcd005be2 ("sched_ext: Introduce scx_prog_sched()")
Cc: stable@vger.kernel.org
Signed-off-by: fangqiurong <fangqiurong@kylinos.cn>
---
 kernel/sched/ext/ext.c | 24 +++++++++++++++---------
 1 file changed, 15 insertions(+), 9 deletions(-)

diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c
index 10af28a9f2c0..67626c162878 100644
--- a/kernel/sched/ext/ext.c
+++ b/kernel/sched/ext/ext.c
@@ -6395,8 +6395,9 @@ static void scx_root_disable(struct scx_sched *sch)
 	case SCX_DISABLED:
 		pr_warn("sched_ext: ops error detected without ops (%s)\n",
 			sch->exit_info->msg);
-		WARN_ON_ONCE(scx_set_enable_state(SCX_DISABLED) != SCX_DISABLING);
-		goto done;
+		if (scx_tryset_enable_state(SCX_DISABLED, SCX_DISABLING))
+			goto done;
+		break;
 	default:
 		break;
 	}
@@ -7506,22 +7507,27 @@ static void scx_root_enable_workfn(struct kthread_work *work)
 #ifdef CONFIG_EXT_SUB_SCHED
 	cgroup_get(cgrp);
 #endif
+	/* Transition to SCX_ENABLING before @ops->priv publication */
+	if (!scx_tryset_enable_state(SCX_ENABLING, SCX_DISABLED)) {
+		pr_warn("sched_ext: enable raced with a state transition, aborting\n");
+		ret = -EBUSY;
+#ifdef CONFIG_EXT_SUB_SCHED
+		cgroup_put(cgrp);
+#endif
+		goto err_free_tid_hash;
+	}
+	WARN_ON_ONCE(scx_root);
+
 	sch = scx_alloc_and_add_sched(cmd, cgrp, NULL);
 	if (IS_ERR(sch)) {
 		ret = PTR_ERR(sch);
+		scx_tryset_enable_state(SCX_DISABLED, SCX_ENABLING);
 		goto err_free_tid_hash;
 	}
 
 	if (sch->is_cid_type)
 		static_branch_enable(&__scx_is_cid_type);
 
-	/*
-	 * Transition to ENABLING and clear exit info to arm the disable path.
-	 * Failure triggers full disabling from here on.
-	 */
-	WARN_ON_ONCE(scx_set_enable_state(SCX_ENABLING) != SCX_DISABLED);
-	WARN_ON_ONCE(scx_root);
-
 	atomic_long_set(&scx_nr_rejected, 0);
 
 	for_each_possible_cpu(cpu) {
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-09-13 15:59 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-10  8:45 [PATCH] sched_ext: Close the pre-enable ops error claim window Qiurong Fang
2026-09-10  9:01 ` sashiko-bot
2026-09-10 21:40 ` Tejun Heo
2026-09-11  2:42 ` [PATCH v2] " Qiurong Fang
2026-09-11 15:24   ` Tejun Heo
2026-09-12 13:15   ` [PATCH v3] " Qiurong Fang
2026-09-13 15:59     ` Tejun Heo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox