[PATCH v2] drm/bridge: ti-sn65dsi83: Cancel reset_work on remove to avoid use-after-free

Fan Wu <[email protected]>
Newsgroups org.kernel.vger.stable,org.freedesktop.lists.dri-devel,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
The error recovery code queues reset_work from the threaded IRQ handler
and polling monitor_work. Neither the remove path nor the probe failure
path after the IRQ is registered drains that work before devres releases
the bridge allocation.

Use drm_bridge_unplug() before stopping the work. It prevents a concurrent
atomic commit from entering the bridge and re-arming monitor_work through
monitor_start(), and waits for in-flight bridge critical sections to
finish. Then disable the IRQ and drain monitor_work and reset_work.

Use the same shutdown sequence when attaching the DSI host fails. This
prevents error recovery work queued by an early IRQ from accessing the
devm-managed bridge after the failed probe returns.

This issue was found by an in-house static analysis tool.
Compile-tested only; runtime testing is appreciated.

Fixes: ad5c6ecef27e ("drm: bridge: ti-sn65dsi83: Add error recovery mechanism")
Cc: [email protected]
Assisted-by: Codex:gpt-5.6
Signed-off-by: Fan Wu <[email protected]>
---
Changes since v1:
  - Move drm_bridge_unplug() ahead of the work cancellation so a concurrent
    atomic commit cannot re-arm monitor_work via monitor_start() after the
    cancel.
  - Drain from the probe-error path (err_remove_bridge) too, closing a UAF
    where a spurious IRQ during probe queues reset_work and a later
    -EPROBE_DEFER frees ctx.
  - Factor the sequence into sn65dsi83_stop_error_recovery() shared by
    both paths.

 drivers/gpu/drm/bridge/ti-sn65dsi83.c | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c
index 42b451432bbb..5f1a96856eac 100644
--- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c
+++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c
@@ -997,6 +997,18 @@ static irqreturn_t sn65dsi83_irq(int irq, void *data)
 	return IRQ_HANDLED;
 }
 
+static void sn65dsi83_stop_error_recovery(struct sn65dsi83 *ctx)
+{
+	/* Block new bridge users and wait for existing critical sections. */
+	drm_bridge_unplug(&ctx->bridge);
+
+	if (ctx->irq)
+		disable_irq(ctx->irq);
+
+	cancel_delayed_work_sync(&ctx->monitor_work);
+	cancel_work_sync(&ctx->reset_work);
+}
+
 static int sn65dsi83_probe(struct i2c_client *client)
 {
 	const struct i2c_device_id *id = i2c_client_get_device_id(client);
@@ -1061,7 +1073,7 @@ static int sn65dsi83_probe(struct i2c_client *client)
 	return 0;
 
 err_remove_bridge:
-	drm_bridge_remove(&ctx->bridge);
+	sn65dsi83_stop_error_recovery(ctx);
 	return ret;
 }
 
@@ -1069,6 +1081,6 @@ static void sn65dsi83_remove(struct i2c_client *client)
 {
 	struct sn65dsi83 *ctx = i2c_get_clientdata(client);
 
-	drm_bridge_unplug(&ctx->bridge);
+	sn65dsi83_stop_error_recovery(ctx);
 }
 
-- 
2.51.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.