[PATCH] media: dvb-usb-v2: fix double-free on URB allocation failure

From: Fan Wu

Date: Sat Jul 18 2026 - 21:43:30 EST


usb_urb_alloc_bulk_urbs() and usb_urb_alloc_isoc_urbs() allocate URBs in
a loop. When usb_alloc_urb() fails at index i, the error path frees the
already-allocated URBs:

for (j = 0; j < i; j++)
usb_free_urb(stream->urb_list[j]);
return -ENOMEM;

but it leaves stream->urb_list[j] pointing at the freed URBs. At that
point stream->urbs_initialized equals i, since it is incremented once
per successful usb_alloc_urb() before the fill/setup of the next URB.

The -ENOMEM propagates up through usb_urb_initv2() and
dvb_usbv2_adapter_stream_init(). dvb_usbv2_adapter_init() returns the
error, dvb_usbv2_init() returns it, and dvb_usbv2_probe() then takes its
err_free_all path, which calls dvb_usbv2_exit() ->
dvb_usbv2_adapter_exit() -> dvb_usbv2_adapter_stream_exit() ->
usb_urb_exitv2() -> usb_urb_free_urbs().

usb_urb_free_urbs() walks urb_list[urbs_initialized-1 .. 0] and, for
each non-NULL slot, calls usb_free_urb(). urb_list[0..i-1] are still the
non-NULL dangling pointers from the alloc error path, so they are freed a
second time. URBs allocated here start with a refcount of one (usb_get_urb
is never taken in this path), so the first free releases the memory and
the second is a use-after-free / double-free.

struct usb_data_stream is embedded in the kzalloc'd dvb_usb_device, so
urb_list[] starts zeroed; the double-free requires at least one prior
usb_alloc_urb() to succeed before the failing one. That is the normal
multi-URB streaming configuration (e.g. ce6230 .count = 6).

Null each slot immediately after freeing it, so usb_urb_free_urbs() skips
the already-freed entries via its existing non-NULL check.

This issue was found by an in-house static analysis tool.

Fixes: eb28dc39d3e8 ("[media] drivers/media/dvb/dvb-usb/usb-urb.c: adjust array index")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: Codex:gpt-5.6
Signed-off-by: Fan Wu <fanwu01@xxxxxxxxxx>
---
drivers/media/usb/dvb-usb-v2/usb_urb.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/media/usb/dvb-usb-v2/usb_urb.c b/drivers/media/usb/dvb-usb-v2/usb_urb.c
index 2ad2ddeaff51..a6edec097fb3 100644
--- a/drivers/media/usb/dvb-usb-v2/usb_urb.c
+++ b/drivers/media/usb/dvb-usb-v2/usb_urb.c
@@ -143,8 +143,10 @@ static int usb_urb_alloc_bulk_urbs(struct usb_data_stream *stream)
stream->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC);
if (!stream->urb_list[i]) {
dev_dbg(&stream->udev->dev, "%s: failed\n", __func__);
- for (j = 0; j < i; j++)
+ for (j = 0; j < i; j++) {
usb_free_urb(stream->urb_list[j]);
+ stream->urb_list[j] = NULL;
+ }
return -ENOMEM;
}
usb_fill_bulk_urb(stream->urb_list[i],
@@ -173,8 +175,10 @@ static int usb_urb_alloc_isoc_urbs(struct usb_data_stream *stream)
stream->props.u.isoc.framesperurb, GFP_ATOMIC);
if (!stream->urb_list[i]) {
dev_dbg(&stream->udev->dev, "%s: failed\n", __func__);
- for (j = 0; j < i; j++)
+ for (j = 0; j < i; j++) {
usb_free_urb(stream->urb_list[j]);
+ stream->urb_list[j] = NULL;
+ }
return -ENOMEM;
}

--
2.34.1