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

Wentao Liang posted 1 patch 1 week, 1 day ago
drivers/gpu/drm/i915/gt/intel_breadcrumbs.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
[PATCH] drm/i915/gt: Fix ce use-after-free in signal_irq_work()
Posted by Wentao Liang 1 week, 1 day ago
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@vger.kernel.org
Signed-off-by: Wentao Liang <vulab@iscas.ac.cn>
---
 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