[PATCH] drm/i915/gt: Fix ce use-after-free in signal_irq_work()

From: Wentao Liang

Date: Wed Sep 16 2026 - 13:31:16 EST


The final intel_context_put() may drop the last reference to a signaler
context. GuC parallel child contexts are freed immediately by
__guc_context_destroy() rather than via call_rcu(), so reading
ce->signal_link.next to advance the RCU iterator afterwards is a
use-after-free. Cache the next context before the loop body instead.

Fixes: c744d50363b7 ("drm/i915/gt: Split the breadcrumb spinlock between global and contexts")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Wentao Liang <vulab@xxxxxxxxxxx>
---
drivers/gpu/drm/i915/gt/intel_breadcrumbs.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c b/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c
index c10ac0ab3bfa..f840e98792cd 100644
--- a/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c
+++ b/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c
@@ -174,7 +174,7 @@ static void signal_irq_work(struct irq_work *work)
struct intel_breadcrumbs *b = container_of(work, typeof(*b), irq_work);
const ktime_t timestamp = ktime_get();
struct llist_node *signal, *sn;
- struct intel_context *ce;
+ struct intel_context *ce, *cn;

signal = NULL;
if (unlikely(!llist_empty(&b->signaled_requests)))
@@ -210,9 +210,19 @@ static void signal_irq_work(struct irq_work *work)

rcu_read_lock();
atomic_inc(&b->signaler_active);
- list_for_each_entry_rcu(ce, &b->signalers, signal_link) {
+ for (ce = list_first_or_null_rcu(&b->signalers, typeof(*ce), signal_link);
+ ce;
+ ce = cn) {
struct i915_request *rq;

+ /*
+ * Grab the next signaler up front, as dropping the final
+ * reference below may free the current one before we
+ * advance the iterator.
+ */
+ cn = list_next_or_null_rcu(&b->signalers, &ce->signal_link,
+ typeof(*cn), signal_link);
+
list_for_each_entry_rcu(rq, &ce->signals, signal_link) {
bool release;

--
2.34.1