[PATCH] media: as102: keep device alive for open DVB frontend

Qing Ming <[email protected]> Wed, 5 Aug 2026 15:42:18 +0800
Newsgroups org.kernel.vger.linux-media,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
dvb_frontend_detach() defers the frontend release while an open frontend
file still holds a reference. The final file release calls ts_bus_ctrl()
before dropping that reference, but as102_usb_disconnect() has already
dropped the only as102_dev_t reference by then.

This leaves the frontend's private bus adapter pointing into freed memory.
Closing a frontend file after USB disconnect consequently reaches
as102_stream_ctrl() and accesses the released device object. KASAN reports:

  BUG: KASAN: slab-use-after-free in mutex_lock_interruptible+0xa1/0x100
  as102_stream_ctrl+0x23/0x70
  dvb_frontend_release+0x1ea/0x200

The object was allocated by as102_usb_probe() and freed by
as102_usb_disconnect().

Hold an as102 device reference from frontend attachment through the final
frontend release callback. Detach the frontend on registration failure so
the new reference is also released on that path.

Fixes: 1f862a68df24 ("[media] dvb_frontend: move kref to struct
dvb_frontend")
Cc: [email protected]
Signed-off-by: Qing Ming <[email protected]>
---
 drivers/media/dvb-frontends/as102_fe.c  |  2 ++
 drivers/media/dvb-frontends/as102_fe.h  |  1 +
 drivers/media/usb/as102/as102_drv.c     | 15 ++++++++++++++-
 drivers/media/usb/as102/as102_drv.h     |  1 +
 drivers/media/usb/as102/as102_usb_drv.c |  2 +-
 5 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/drivers/media/dvb-frontends/as102_fe.c b/drivers/media/dvb-frontends/as102_fe.c
index fecb0735d..06bfa7c9d 100644
--- a/drivers/media/dvb-frontends/as102_fe.c
+++ b/drivers/media/dvb-frontends/as102_fe.c
@@ -403,6 +403,8 @@ static void as102_fe_release(struct dvb_frontend *fe)
 {
 	struct as102_state *state = fe->demodulator_priv;
 
+	if (state->ops->release)
+		state->ops->release(state->priv);
 	kfree(state);
 }
 
diff --git a/drivers/media/dvb-frontends/as102_fe.h b/drivers/media/dvb-frontends/as102_fe.h
index a6409b7ca..b0377877e 100644
--- a/drivers/media/dvb-frontends/as102_fe.h
+++ b/drivers/media/dvb-frontends/as102_fe.h
@@ -12,6 +12,7 @@ struct as102_fe_ops {
 	int (*get_status)(void *priv, struct as10x_tune_status *tstate);
 	int (*get_stats)(void *priv, struct as10x_demod_stats *demod_stats);
 	int (*stream_ctrl)(void *priv, int acquire, uint32_t elna_cfg);
+	void (*release)(void *priv);
 };
 
 struct dvb_frontend *as102_attach(const char *name,
diff --git a/drivers/media/usb/as102/as102_drv.c b/drivers/media/usb/as102/as102_drv.c
index 6b1d3528a..bf7ff8a05 100644
--- a/drivers/media/usb/as102/as102_drv.c
+++ b/drivers/media/usb/as102/as102_drv.c
@@ -274,12 +274,22 @@ static int as102_stream_ctrl(void *priv, int acquire, uint32_t elna_cfg)
 	return ret;
 }
 
+static void as102_frontend_release(void *priv)
+{
+	struct as10x_bus_adapter_t *bus_adap = priv;
+	struct as102_dev_t *as102_dev;
+
+	as102_dev = container_of(bus_adap, struct as102_dev_t, bus_adap);
+	kref_put(&as102_dev->kref, as102_usb_release);
+}
+
 static const struct as102_fe_ops as102_fe_ops = {
 	.set_tune = as102_set_tune,
 	.get_tps  = as102_get_tps,
 	.get_status = as102_get_status,
 	.get_stats = as102_get_stats,
 	.stream_ctrl = as102_stream_ctrl,
+	.release = as102_frontend_release,
 };
 
 int as102_dvb_register(struct as102_dev_t *as102_dev)
@@ -333,12 +343,13 @@ int as102_dvb_register(struct as102_dev_t *as102_dev)
 		    __func__, ret);
 		goto efereg;
 	}
+	kref_get(&as102_dev->kref);
 
 	ret =  dvb_register_frontend(&as102_dev->dvb_adap, as102_dev->dvb_fe);
 	if (ret < 0) {
 		dev_err(dev, "%s: as102_dvb_register_frontend() failed: %d",
 		    __func__, ret);
-		goto efereg;
+		goto eferegister;
 	}
 
 	/* init bus mutex for token locking */
@@ -358,6 +369,8 @@ int as102_dvb_register(struct as102_dev_t *as102_dev)
 	pr_info("Registered device %s", as102_dev->name);
 	return 0;
 
+eferegister:
+	dvb_frontend_detach(as102_dev->dvb_fe);
 efereg:
 	dvb_dmxdev_release(&as102_dev->dvb_dmxdev);
 edmxdinit:
diff --git a/drivers/media/usb/as102/as102_drv.h b/drivers/media/usb/as102/as102_drv.h
index 4342c7ce3..c125cae23 100644
--- a/drivers/media/usb/as102/as102_drv.h
+++ b/drivers/media/usb/as102/as102_drv.h
@@ -70,5 +70,6 @@ struct as102_dev_t {
 
 int as102_dvb_register(struct as102_dev_t *dev);
 void as102_dvb_unregister(struct as102_dev_t *dev);
+void as102_usb_release(struct kref *kref);
 
 #endif
diff --git a/drivers/media/usb/as102/as102_usb_drv.c b/drivers/media/usb/as102/as102_usb_drv.c
index a11024451..6eff2ff2f 100644
--- a/drivers/media/usb/as102/as102_usb_drv.c
+++ b/drivers/media/usb/as102/as102_usb_drv.c
@@ -298,7 +298,7 @@ static int as102_usb_start_stream(struct as102_dev_t *dev)
 	return 0;
 }
 
-static void as102_usb_release(struct kref *kref)
+void as102_usb_release(struct kref *kref)
 {
 	struct as102_dev_t *as102_dev;
 
-- 
2.53.0