[PATCH RFC] media: dvb-core: fix memory leaks in dvb_dmxdev_filter_start()
"syzbot" <[email protected]> Wed, 29 Jul 2026 15:15:27 +0000 (UTC)
| Newsgroups | dev.linux.lists.syzbot |
|---|---|
| Message-ID | <[email protected]> |
When starting a PES filter with multiple PIDs, dvb_dmxdev_filter_start()
iterates over all feeds and calls dvb_dmxdev_start_feed() for each of them.
If dvb_dmxdev_start_feed() succeeds for the first feed but fails for a
subsequent feed (e.g., due to running out of hardware feeds), it attempts
to clean up by calling dvb_dmxdev_filter_stop().
However, dvb_dmxdev_filter_stop() checks if the filter state is at least
DMXDEV_STATE_GO. Since dvb_dmxdev_filter_start() only sets the state to
DMXDEV_STATE_GO at the end of the function, the state is still
DMXDEV_STATE_SET during the error path. As a result,
dvb_dmxdev_filter_stop() immediately returns 0 and does not stop the feeds
that were already successfully started. This leaves the successfully
started feeds running indefinitely, leading to memory leaks of structures
like vidtv_mux and its associated channels, services, and PSI descriptors.
BUG: memory leak
unreferenced object 0xffff888184ec5120 (size 32):
hex dump (first 32 bytes):
00 00 00 00 00 00 00 00 48 19 02 0c 10 34 56 85 ........H....4V.
81 88 ff ff 0a 70 3f 56 85 81 88 ff ff 00 00 00 .....p?V........
backtrace (crc b14a33fd):
kmemleak_alloc_recursive include/linux/kmemleak.h:44 [inline]
slab_post_alloc_hook mm/slub.c:4597 [inline]
slab_alloc_node mm/slub.c:4917 [inline]
__kmalloc_cache_noprof+0x1be/0x3f0 mm/slub.c:5485
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
vidtv_psi_service_desc_init+0x7a/0x1b0
drivers/media/test-drivers/vidtv/vidtv_psi.c:233
vidtv_channel_s302m_init+0xd3/0x2c0
drivers/media/test-drivers/vidtv/vidtv_channel.c:83
vidtv_channels_init+0x20/0x40
drivers/media/test-drivers/vidtv/vidtv_channel.c:528
vidtv_mux_init+0x117/0x830
drivers/media/test-drivers/vidtv/vidtv_mux.c:521
vidtv_start_streaming
drivers/media/test-drivers/vidtv/vidtv_bridge.c:194 [inline]
vidtv_start_feed+0x1d3/0x260
drivers/media/test-drivers/vidtv/vidtv_bridge.c:239
dmx_ts_feed_start_filtering+0xaf/0x110
drivers/media/dvb-core/dvb_demux.c:747
dvb_dmxdev_start_feed drivers/media/dvb-core/dmxdev.c:658 [inline]
dvb_dmxdev_filter_start+0x1d4/0x650 drivers/media/dvb-core/dmxdev.c:769
To fix this, temporarily set the filter state to DMXDEV_STATE_GO before
calling dvb_dmxdev_filter_stop() on the error path in
dvb_dmxdev_filter_start(). This ensures that the cleanup function actually
stops the successfully started feeds.
Additionally, this patch addresses several other related issues in the
error handling paths:
- In dvb_dmxdev_start_feed(), explicitly set feed->ts = NULL if an error
occurs and the feed is released to prevent dangling pointers.
- In dvb_dmxdev_add_pid(), if dvb_dmxdev_start_feed() fails, remove the
feed from the list and free it.
- In dvb_dmxdev_remove_pid(), always remove and free the feed, even if
feed->ts is NULL.
- In dvb_dmxdev_filter_start() for DMXDEV_TYPE_SEC, if (*secfeed)->set()
fails, set *secfeed = NULL. If start_filtering() fails, explicitly release
the filter, restart the feed, and set the pointers to NULL before returning
the error.
- Update dvb_dmxdev_feed_stop(), dvb_dmxdev_filter_stop(), and
dvb_dmxdev_remove_pid() to check if feed->ts != NULL before dereferencing
it.
Fixes: 1cb662a31449 ("V4L/DVB (12275): Add two new ioctls: DMX_ADD_PID and DMX_REMOVE_PID")
Assisted-by: Gemini:gemini-3.5-flash Gemini:gemini-3.1-pro-preview syzbot
Reported-by: [email protected]
Closes: https://syzkaller.appspot.com/bug?extid=e180aa47a45c517e59f5
Link: https://syzkaller.appspot.com/ai_job?id=408080dd-bb67-4039-8125-ce147d3d3315
To: <[email protected]>
To: "Mauro Carvalho Chehab" <[email protected]>
To: "Andreas Oberritter" <[email protected]>
Cc: "Hans Verkuil" <[email protected]>
Cc: <[email protected]>
---
diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c
index 3c8bc75e4..99d438411 100644
--- a/drivers/media/dvb-core/dmxdev.c
+++ b/drivers/media/dvb-core/dmxdev.c
@@ -490,8 +490,10 @@ static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter)
dmxdevfilter->feed.sec->stop_filtering(dmxdevfilter->feed.sec);
break;
case DMXDEV_TYPE_PES:
- list_for_each_entry(feed, &dmxdevfilter->feed.ts, next)
- feed->ts->stop_filtering(feed->ts);
+ list_for_each_entry(feed, &dmxdevfilter->feed.ts, next) {
+ if (feed->ts)
+ feed->ts->stop_filtering(feed->ts);
+ }
break;
default:
return -EINVAL;
@@ -572,8 +574,10 @@ static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter)
dvb_dmxdev_feed_stop(dmxdevfilter);
demux = dmxdevfilter->dev->demux;
list_for_each_entry(feed, &dmxdevfilter->feed.ts, next) {
- demux->release_ts_feed(demux, feed->ts);
- feed->ts = NULL;
+ if (feed->ts) {
+ demux->release_ts_feed(demux, feed->ts);
+ feed->ts = NULL;
+ }
}
break;
default:
@@ -652,12 +656,14 @@ static int dvb_dmxdev_start_feed(struct dmxdev *dmxdev,
ret = tsfeed->set(tsfeed, feed->pid, ts_type, ts_pes, timeout);
if (ret < 0) {
dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed);
+ feed->ts = NULL;
return ret;
}
ret = tsfeed->start_filtering(tsfeed);
if (ret < 0) {
dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed);
+ feed->ts = NULL;
return ret;
}
@@ -726,6 +732,7 @@ static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter)
pr_err("DVB (%s): could not set feed\n",
__func__);
dvb_dmxdev_feed_restart(filter);
+ *secfeed = NULL;
return ret;
}
} else {
@@ -758,8 +765,13 @@ static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter)
filter->todo = 0;
ret = filter->feed.sec->start_filtering(filter->feed.sec);
- if (ret < 0)
+ if (ret < 0) {
+ filter->feed.sec->release_filter(filter->feed.sec, filter->filter.sec);
+ dvb_dmxdev_feed_restart(filter);
+ filter->feed.sec = NULL;
+ filter->filter.sec = NULL;
return ret;
+ }
dvb_dmxdev_filter_timer(filter);
break;
@@ -768,6 +780,7 @@ static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter)
list_for_each_entry(feed, &filter->feed.ts, next) {
ret = dvb_dmxdev_start_feed(dmxdev, filter, feed);
if (ret < 0) {
+ dvb_dmxdev_filter_state_set(filter, DMXDEV_STATE_GO);
dvb_dmxdev_filter_stop(filter);
return ret;
}
@@ -884,6 +897,7 @@ static int dvb_dmxdev_add_pid(struct dmxdev *dmxdev,
struct dmxdev_filter *filter, u16 pid)
{
struct dmxdev_feed *feed;
+ int ret;
if ((filter->type != DMXDEV_TYPE_PES) ||
(filter->state < DMXDEV_STATE_SET))
@@ -901,8 +915,14 @@ static int dvb_dmxdev_add_pid(struct dmxdev *dmxdev,
feed->pid = pid;
list_add(&feed->next, &filter->feed.ts);
- if (filter->state >= DMXDEV_STATE_GO)
- return dvb_dmxdev_start_feed(dmxdev, filter, feed);
+ if (filter->state >= DMXDEV_STATE_GO) {
+ ret = dvb_dmxdev_start_feed(dmxdev, filter, feed);
+ if (ret < 0) {
+ list_del(&feed->next);
+ kfree(feed);
+ return ret;
+ }
+ }
return 0;
}
@@ -917,10 +937,12 @@ static int dvb_dmxdev_remove_pid(struct dmxdev *dmxdev,
return -EINVAL;
list_for_each_entry_safe(feed, tmp, &filter->feed.ts, next) {
- if ((feed->pid == pid) && (feed->ts != NULL)) {
- feed->ts->stop_filtering(feed->ts);
- filter->dev->demux->release_ts_feed(filter->dev->demux,
- feed->ts);
+ if (feed->pid == pid) {
+ if (feed->ts != NULL) {
+ feed->ts->stop_filtering(feed->ts);
+ filter->dev->demux->release_ts_feed(filter->dev->demux,
+ feed->ts);
+ }
list_del(&feed->next);
kfree(feed);
}
base-commit: f5098b6bae761e346ebcd9da7f95622c04733cff
--
This is an AI-generated patch subject to moderation.
Reply with '#syz upstream' to Sign-off the patch as a human author
and send it to the upstream kernel mailing lists.
Reply with '#syz reject' to reject it ('#syz unreject' to undo).
See https://goo.gle/syzbot-ai-patches for information about AI-generated patches.
You can comment on the patch as usual, syzbot will try to address
the comments and send a new version of the patch if necessary.
syzbot engineers can be reached at [email protected].