[PATCH] media: au0828: release the media source when the device is disconnected

From: YuanZhe Liu

Date: Mon Nov 02 2026 - 10:03:25 EST


au0828_enable_source() starts the media pipeline and saves the link, its
owner and the source and sink entities in struct au0828_dev. The matching
au0828_disable_source() is only called from the frontend open and release
paths.

When the device is disconnected, au0828_dvb_unregister() removes the
frontend entity through dvb_unregister_frontend(). dvb_remove_device()
frees the entity unconditionally and __media_entity_remove_links() frees
every link of it, but the source is not released: the pipeline is never
stopped and dev->active_link, dev->active_link_owner, dev->active_source
and dev->active_sink keep pointing at freed memory.

struct au0828_dev stays alive until the last V4L2 file descriptor is
closed, because the video device holds a reference to v4l2_device. A
later close therefore reaches au0828_disable_source() through
v4l2_fh_exit() and dereferences the freed link and entity.

Stop the pipeline and deactivate the link in au0828_usb_disconnect(),
before the frontend entity is unregistered and while holding graph_mutex.
Move the cleanup tail of au0828_disable_source() to
au0828_release_active_link() so that both paths share it; the disconnect
path must not be skipped by the dev->users > 1 check, which only applies
to a video owner. Nothing has to be released when no link was activated,
so the new call runs only when dev->active_link_owner is set.

Fixes: c94903f1f7cc ("[media] media: au0828 add enable, disable source handlers")
Cc: stable@xxxxxxxxxxxxxxx
Co-developed-by: Bin Yu <byu@xxxxxxxxxxxxx>
Signed-off-by: Bin Yu <byu@xxxxxxxxxxxxx>
Co-developed-by: Cheng Wen <wencheng@xxxxxxxxxxxxx>
Signed-off-by: Cheng Wen <wencheng@xxxxxxxxxxxxx>
Signed-off-by: YuanZhe Liu <25031212351@xxxxxxxxxxxxxxxxx>
---
drivers/media/usb/au0828/au0828-core.c | 53 +++++++++++++++++++-------
1 file changed, 39 insertions(+), 14 deletions(-)

diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c
index 445cbeb7abae..57a163415d1c 100644
--- a/drivers/media/usb/au0828/au0828-core.c
+++ b/drivers/media/usb/au0828/au0828-core.c
@@ -120,6 +120,7 @@ static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value,
#ifdef CONFIG_MEDIA_CONTROLLER
static void au0828_media_graph_notify(struct media_entity *new,
void *notify_data);
+static void au0828_release_active_link(struct au0828_dev *dev);
#endif

static void au0828_unregister_media_device(struct au0828_dev *dev)
@@ -175,6 +176,18 @@ static void au0828_usb_disconnect(struct usb_interface *interface)
set_bit(DEV_DISCONNECTED, &dev->dev_state);

au0828_rc_unregister(dev);
+#ifdef CONFIG_MEDIA_CONTROLLER
+ /*
+ * The frontend entity can own the active link. Its entity is released
+ * by au0828_dvb_unregister() below, so stop the pipeline and deactivate
+ * the link while the entity is still registered.
+ */
+ if (dev->active_link_owner) {
+ mutex_lock(&dev->media_dev->graph_mutex);
+ au0828_release_active_link(dev);
+ mutex_unlock(&dev->media_dev->graph_mutex);
+ }
+#endif
/* Digital TV */
au0828_dvb_unregister(dev);

@@ -438,6 +451,31 @@ static int au0828_enable_source(struct media_entity *entity,
return ret;
}

+/* Callers should hold graph_mutex */
+static void au0828_release_active_link(struct au0828_dev *dev)
+{
+ int ret;
+
+ __media_pipeline_stop(dev->active_link_owner->pads);
+ pr_debug("Pipeline stop for %s\n", dev->active_link_owner->name);
+
+ ret = __media_entity_setup_link(dev->active_link, 0);
+ if (ret)
+ pr_err("Deactivate link Error %d\n", ret);
+
+ pr_info("Disabled Source: %s->%s->%s Ret %d\n",
+ dev->active_source->name, dev->active_sink->name,
+ dev->active_link_owner->name, ret);
+
+ dev->active_link = NULL;
+ dev->active_link_owner = NULL;
+ dev->active_source = NULL;
+ dev->active_sink = NULL;
+ dev->active_link_shared = false;
+ dev->active_link_user = NULL;
+ dev->active_link_user_pipe = NULL;
+}
+
/* Callers should hold graph_mutex */
static void au0828_disable_source(struct media_entity *entity)
{
@@ -537,20 +575,7 @@ static void au0828_disable_source(struct media_entity *entity)
dev->active_link_owner->name);

deactivate_link:
- ret = __media_entity_setup_link(dev->active_link, 0);
- if (ret)
- pr_err("Deactivate link Error %d\n", ret);
-
- pr_info("Disabled Source: %s->%s->%s Ret %d\n",
- dev->active_source->name, dev->active_sink->name,
- dev->active_link_owner->name, ret);
-
- dev->active_link = NULL;
- dev->active_link_owner = NULL;
- dev->active_source = NULL;
- dev->active_sink = NULL;
- dev->active_link_shared = false;
- dev->active_link_user = NULL;
+ au0828_release_active_link(dev);
}
}
#endif
--
2.34.1