lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <20251122072558.2604753-1-dengjie03@kylinos.cn>
Date: Sat, 22 Nov 2025 15:25:58 +0800
From: Jie Deng <dengjie03@...inos.cn>
To: laurent.pinchart@...asonboard.com
Cc: hansg@...nel.org,
	mchehab@...nel.org,
	kieran.bingham@...asonboard.com,
	linux-media@...r.kernel.org,
	linux-kernel@...r.kernel.org,
	Jie Deng <dengjie03@...inos.cn>
Subject: [PATCH v2] media: usb: uvc: Fix NULL pointer dereference during USB device hot-unplug

Add safety checks to prevent kernel panic during the race window in
USB device disconnection.

The issue occurs in a specific timing window during hot-unplug:
- usb_disconnect() calls usb_disable_device() which sets
  dev->actconfig->interface[i] to NULL
- But dev->actconfig is not yet set to NULL
- During this window, uvc_video_stop_streaming() calls usb_set_interface()
- This eventually calls usb_ifnum_to_if() which accesses the already
  NULL interface[i]->altsetting, causing a kernel panic
logs:
[ 9518.891254] Call trace:
[ 9518.894817]  usb_ifnum_to_if+0x34/0x50
[ 9518.899681]  usb_set_interface+0x108/0x3c8
[ 9518.904898]  uvc_video_stop_streaming+0x3c/0x90 [uvcvideo]
[ 9518.911500]  uvc_stop_streaming+0x24/0x90 [uvcvideo]
[ 9518.917583]  __vb2_queue_cancel+0x44/0x458 [videobuf2_common]
[ 9518.924444]  vb2_core_streamoff+0x20/0xb8 [videobuf2_common]
[ 9518.931221]  vb2_streamoff+0x18/0x60 [videobuf2_v4l2]
[ 9518.937390]  uvc_queue_streamoff+0x30/0x50 [uvcvideo]
[ 9518.943557]  uvc_ioctl_streamoff+0x40/0x68 [uvcvideo]
[ 9518.949724]  v4l_streamoff+0x20/0x28
[ 9518.954415]  __video_do_ioctl+0x17c/0x3e0
[ 9518.959540]  video_usercopy+0x1d8/0x558
[ 9518.964490]  video_ioctl2+0x14/0x1c
[ 9518.969094]  v4l2_ioctl+0x3c/0x58
[ 9518.973526]  do_vfs_ioctl+0x374/0x7b0
[ 9518.978304]  ksys_ioctl+0x78/0xa8
[ 9518.982734]  sys_ioctl+0xc/0x18
[ 9518.986991]  __sys_trace_return+0x0/0x4
[ 9518.991943] Code: eb04005f 54000100 f9400040 91002042 (f9400003)
[ 9518.999153] ---[ end trace f7c7d3236806d9a4 ]---

The fix adds comprehensive NULL pointer validation:
- Check stream, stream->dev, stream->dev->udev, and stream->intf early
- Safely iterate through interfaces with proper NULL checks.
- Ensure config->interface[i] and its altsetting are valid before
  access.
This prevents the crash by ensuring we don't access partially freed
USB interface structures during device removal.

Fixes: 571e70dbd421 ("media: uvcvideo: Split uvc_video_enable into two")
Signed-off-by: Jie Deng <dengjie03@...inos.cn>
---
v2:
	* Before the uvc driver calls usb_set_interface(), it first
	  checks whether the interface exists and is valid.
v1:
	* Add a null pointer check in the usb_ifnum_to_if() function.
	  This plan cannot eliminate the root cause.
	  Link:https://lore.kernel.org/all/20251113114411.1410343-1-dengjie03@kylinos.cn/
---
 drivers/media/usb/uvc/uvc_video.c | 20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index 2094e059d7d3..30dd8ea8980e 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -2317,10 +2317,25 @@ int uvc_video_start_streaming(struct uvc_streaming *stream)
 
 void uvc_video_stop_streaming(struct uvc_streaming *stream)
 {
+	struct usb_host_config *config;
+	int i;
+
+	if (!stream || !stream->dev || !stream->dev->udev || !stream->intf)
+		goto cleanup_clock;
+
 	uvc_video_stop_transfer(stream, 1);
 
-	if (stream->intf->num_altsetting > 1) {
-		usb_set_interface(stream->dev->udev, stream->intfnum, 0);
+	config = stream->dev->udev->actconfig;
+	if (stream->intf->num_altsetting > 1 && config) {
+		/* Security Check: Check if the interface exists and is valid */
+		for (i = 0; i < config->desc.bNumInterfaces; i++) {
+			if (config->interface[i] &&
+			    config->interface[i]->altsetting[0]
+				.desc.bInterfaceNumber == stream->intfnum) {
+				usb_set_interface(stream->dev->udev, stream->intfnum, 0);
+				break;
+			}
+		}
 	} else {
 		/*
 		 * UVC doesn't specify how to inform a bulk-based device
@@ -2338,5 +2353,6 @@ void uvc_video_stop_streaming(struct uvc_streaming *stream)
 		usb_clear_halt(stream->dev->udev, pipe);
 	}
 
+cleanup_clock:
 	uvc_video_clock_cleanup(&stream->clock);
 }
-- 
2.25.1


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ