[PATCH 1/2] media: dvb-usb-v2: fix URB double free and use-after-free on allocation failure

Chen Changcheng <[email protected]> Mon, 3 Aug 2026 17:45:38 +0800
Newsgroups org.kernel.vger.linux-media,org.kernel.vger.linux-kernel,org.kernel.vger.stable
Message-ID <[email protected]>
usb_urb_alloc_bulk_urbs() and usb_urb_alloc_isoc_urbs() free the URBs
allocated so far when usb_alloc_urb() fails, but they do not reset
urbs_initialized.  The stream is then left with a stale non-zero
urbs_initialized count covering already freed, dangling urb_list[]
entries, while every other user of the stream state
(usb_urb_free_urbs(), usb_urb_submitv2()) relies on "urbs_initialized
valid entries" as the invariant.

Two failure scenarios follow:

1. Probe failure path: dvb_usbv2_adapter_stream_init() fails and the
   probe error path runs dvb_usbv2_adapter_exit(), which calls
   usb_urb_exitv2() -> usb_urb_free_urbs() on the same adapter (it is
   reached because adap->props was assigned before stream init).  The
   already freed URBs are freed a second time -> double free.

2. Runtime reconfig path: while the probe path above is hit whenever
   allocation fails during probe, this one additionally depends on the
   device's get_stream_config() returning a configuration different
   from the one used at probe; such devices exist (e.g. af9035 shrinks
   the buffer size on full-speed USB ports).  usb_urb_reconfig() then
   frees the old URBs, updates stream->props and calls
   usb_urb_alloc_*_urbs().  If the allocation fails, the next
   usb_urb_submitv2() call with the same parameters short-circuits in
   reconfig()'s "all fields are same" check and then submits the stale,
   already freed URBs -> use-after-free.

Reset urbs_initialized to 0 on the allocation error path, keeping the
invariant that a non-zero urbs_initialized only covers live URBs.

Fixes: c79b339f9292 ("[media] dvb_usb_v2: copy current dvb_usb as a starting point")
Cc: [email protected]
Signed-off-by: Chen Changcheng <[email protected]>
---
 drivers/media/usb/dvb-usb-v2/usb_urb.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/media/usb/dvb-usb-v2/usb_urb.c b/drivers/media/usb/dvb-usb-v2/usb_urb.c
index 2ad2ddeaff51..9ba9aceabb5b 100644
--- a/drivers/media/usb/dvb-usb-v2/usb_urb.c
+++ b/drivers/media/usb/dvb-usb-v2/usb_urb.c
@@ -145,6 +145,7 @@ static int usb_urb_alloc_bulk_urbs(struct usb_data_stream *stream)
 			dev_dbg(&stream->udev->dev, "%s: failed\n", __func__);
 			for (j = 0; j < i; j++)
 				usb_free_urb(stream->urb_list[j]);
+			stream->urbs_initialized = 0;
 			return -ENOMEM;
 		}
 		usb_fill_bulk_urb(stream->urb_list[i],
@@ -175,6 +176,7 @@ static int usb_urb_alloc_isoc_urbs(struct usb_data_stream *stream)
 			dev_dbg(&stream->udev->dev, "%s: failed\n", __func__);
 			for (j = 0; j < i; j++)
 				usb_free_urb(stream->urb_list[j]);
+			stream->urbs_initialized = 0;
 			return -ENOMEM;
 		}
 
-- 
2.25.1