mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v3] sched_ext: Serialize user DSQ destruction against deferred reenqueues
@ 2026-10-08  2:47 Hui Su
  0 siblings, 0 replies; only message in thread
From: Hui Su @ 2026-10-08  2:47 UTC (permalink / raw)
  To: sched-ext, tj
  Cc: void, arighi, changwoo, mingo, peterz, juri.lelli,
	vincent.guittot, dietmar.eggemann, rostedt, bsegall, mgorman,
	vschneid, kprateek.nayak, yphbchou0911, etsal, linux-kernel,
	Hui Su, stable

A deferred user-DSQ node can be detached by
process_deferred_reenq_users() before the DSQ RCU callback reaches
exit_dsq(). Once detached, exit_dsq() cannot find the node, while the
deferred path continues using the raw DSQ pointer after dropping
deferred_reenq_lock. The callback can free the DSQ before the deferred
path checks its ID or calls reenq_user().

The first RCU grace period drains producers that found @dsq, but does not
cover a consumer that detached its request and continues using @dsq.

Avoid adding reference counting to the deferred reenqueue hot path.
Instead, synchronize the rare destruction path with outstanding users.
After the initial grace period, take every rq lock with its deferred lock.
This unlinks requests not yet detached and closes the detach-to-cursor
window for active consumers. reenq_user() leaves its iteration cursor on
@dsq->list until its final access. Since destroy_dsq() requires @dsq->nr
to be zero, a non-empty list after the sweep indicates an outstanding
cursor; defer reclamation through another RCU callback until it exits.

A test-only pre-fix fixture invoked free_dsq_rcufn() after detaching a
request and before its final DSQ access. KASAN reported the resulting
use-after-free:

    BUG: KASAN: slab-use-after-free in run_deferred+0x1312/0x1710
    Read of size 8 at addr ffff8880087009b0 by task swapper/3/0
    Call Trace:
     <IRQ>
     run_deferred+0x1312/0x1710
     ttwu_do_activate+0x29a/0x600
     try_to_wake_up+0x815/0x1700

Fixes: 84b1a0ea0b7c ("sched_ext: Implement scx_bpf_dsq_reenq() for user DSQs")
Cc: stable@vger.kernel.org
Signed-off-by: Hui Su <sh_def@163.com>
---
Tested on x86_64 KVM:
- The pre-fix fixture reproduced the UAF under KASAN; the v3 cursor/reclamation
  case completed without a report.
- CONFIG_PROVE_LOCKING=y and CONFIG_LOCKDEP=y completed the same test without
  lockdep warnings.

Changes in v3:
- Drop the refcount approach per Tejun and move synchronization to rare DSQ
  destruction.
- Sweep rq/deferred locks and defer reclamation while a reenq_user() cursor
  remains active.
- v2 was accidentally posted without the version tag.

v2 (posted without version tag):
https://lore.kernel.org/lkml/20260930143443.2862150-1-sh_def@163.com/
---
 kernel/sched/ext/ext.c | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c
index 405d0d1038f8..293e5e223277 100644
--- a/kernel/sched/ext/ext.c
+++ b/kernel/sched/ext/ext.c
@@ -5579,6 +5579,35 @@ s32 scx_init_dsq(struct scx_dispatch_q *dsq, u64 dsq_id, struct scx_sched *sch)
 	return 0;
 }
 
+/*
+ * Synchronize DSQ destruction against deferred reenqueues.
+ *
+ * Taking each rq lock with its deferred lock removes requests not yet
+ * detached and closes the detach-to-cursor window. A reenq_user() cursor
+ * stays linked on @dsq->list until its final DSQ access.
+ */
+static bool drain_dsq_reenq_users(struct scx_dispatch_q *dsq)
+{
+	s32 cpu;
+
+	/* The first RCU grace period has drained producers which found @dsq. */
+	for_each_possible_cpu(cpu) {
+		struct scx_dsq_pcpu *pcpu = per_cpu_ptr(dsq->pcpu_user, cpu);
+		struct scx_deferred_reenq_user *dru = &pcpu->deferred_reenq_user;
+		struct rq *rq = cpu_rq(cpu);
+
+		scoped_guard (rq_lock_irqsave, rq) {
+			guard(raw_spinlock)(&rq->scx.deferred_reenq_lock);
+
+			if (!list_empty(&dru->node))
+				list_del_init(&dru->node);
+		}
+	}
+
+	guard(raw_spinlock_irqsave)(&dsq->lock);
+	return list_empty(&dsq->list);
+}
+
 static void exit_dsq(struct scx_dispatch_q *dsq)
 {
 	s32 cpu;
@@ -5608,6 +5637,12 @@ static void free_dsq_rcufn(struct rcu_head *rcu)
 {
 	struct scx_dispatch_q *dsq = container_of(rcu, struct scx_dispatch_q, rcu);
 
+	/* An active reenq_user() cursor still references @dsq. */
+	if (!drain_dsq_reenq_users(dsq)) {
+		call_rcu(&dsq->rcu, free_dsq_rcufn);
+		return;
+	}
+
 	exit_dsq(dsq);
 	kfree(dsq);
 }

base-commit: f2f095c87603b8dfb6752e130d902970371c3852


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2026-10-08  2:49 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-08  2:47 [PATCH v3] sched_ext: Serialize user DSQ destruction against deferred reenqueues Hui Su

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

all inboxes | Powered by JetHome®