⬢ DragonFlyBSD Kernel Audit
DF-2352 / fix.diff
← back to finding ↓ download raw
diff -ruN a/sys/bus/u4b/uvc/uvc_drv.c b/sys/bus/u4b/uvc/uvc_drv.c
--- a/sys/bus/u4b/uvc/uvc_drv.c	2026-10-14 18:40:54.648294861 +0000
+++ b/sys/bus/u4b/uvc/uvc_drv.c	2026-10-14 18:41:11.748074091 +0000
@@ -499,11 +499,15 @@
 	} else {
 		const uint32_t min = frame->interval[0].val;
 		const uint32_t max = frame->interval[1].val;
-		const uint32_t step = frame->interval[2].val;
+		uint32_t step = frame->interval[2].val;
 
+		if (step == 0)
+			step = 1;
 		interval = min + (interval - min + step/2) / step * step;
 		if (interval > max)
 			interval = max;
+		if (interval < min)
+			interval = min;
 	}
 
 	return interval;
@@ -1460,6 +1464,10 @@
 			break;
 		}
 	}
+	if (fmt == NULL) {
+		kprintf("No format desc found.\n");
+		return EINVAL;
+	}
 	if (fmt->nfrm == 0) {
 		kprintf("No frame desc found for default format.\n");
 		return EINVAL;
@@ -1532,6 +1540,10 @@
 			}
 		}
 
+		if (fmt == NULL) {
+			DPRINTF("No format desc found.\n");
+			goto done;
+		}
 		if (fmt->nfrm == 0) {
 			DPRINTF("No frame desc found for default format.\n");
 			goto done;
@@ -1919,8 +1931,28 @@
 				itv->val = interval ? interval : 1;
 				itv++;
 			}
-			if (frm->interval_type == 0)
+			if (frm->interval_type == 0) {
+				/*
+				 * Continuous frame: dFrameInterval[] carries
+				 * min/max/step; read them instead of leaving
+				 * the (M_ZERO) slots at 0 -- step==0 later
+				 * divides by zero in
+				 * uvc_drv_try_frame_interval().
+				 */
+				if (frmd->bLength >= 26 + 12) {
+					interval = UGETDW(frmd->dFrameInterval[0]);
+					itv->val = interval ? interval : 1;
+					interval = UGETDW(frmd->dFrameInterval[1]);
+					(itv + 1)->val = interval ? interval : 1;
+					interval = UGETDW(frmd->dFrameInterval[2]);
+					(itv + 2)->val = interval ? interval : 1;
+				} else {
+					itv->val = 1;
+					(itv + 1)->val = 1;
+					(itv + 2)->val = 1;
+				}
 				itv += 3;
+			}
 			fmt->nfrm++;
 			frm++;
 			break;
@@ -1950,8 +1982,22 @@
 				itv->val = interval ? interval : 1;
 				itv++;
 			}
-			if (frm->interval_type == 0)
+			if (frm->interval_type == 0) {
+				/* same as above: read min/max/step */
+				if (fbrmd->bLength >= 26 + 12) {
+					interval = UGETDW(fbrmd->dFrameInterval[0]);
+					itv->val = interval ? interval : 1;
+					interval = UGETDW(fbrmd->dFrameInterval[1]);
+					(itv + 1)->val = interval ? interval : 1;
+					interval = UGETDW(fbrmd->dFrameInterval[2]);
+					(itv + 2)->val = interval ? interval : 1;
+				} else {
+					itv->val = 1;
+					(itv + 1)->val = 1;
+					(itv + 2)->val = 1;
+				}
 				itv += 3;
+			}
 			fmt->nfrm++;
 			frm++;
 			break;
@@ -2494,11 +2540,32 @@
 uvc_drv_detach(device_t self)
 {
 	struct uvc_softc *sc = device_get_softc(self);
+	int i;
 
 	DPRINTF("%s-%d-%p\n", __func__, __LINE__, self);
 	//delete sysfs
 	//sysfs_video_del_device(sc->udev, sc->iface);
 
+	/*
+	 * Destroy the /dev/videoX cdev FIRST and let outstanding users
+	 * drain before freeing any memory.  destroy_dev() is
+	 * asynchronous (devfs_destroy_dev -> devfs_msg_send_dev) and
+	 * open file descriptors keep dispatching ioctls into the driver
+	 * until the devfs core processes the destroy; freeing
+	 * sc->data/sc->video before that turns concurrent V4L2 ioctls
+	 * (VIDIOC_ENUM_FMT etc., which read video->data->fmt without
+	 * holding the mutex) into use-after-free reads.
+	 */
+	if (sc->video) {
+		/* entry close */
+		uvc_v4l2_unreg(sc->video);
+		for (i = 0; i < 1000; i++) {
+			if (atomic_load_64(&sc->video->users) == 0)
+				break;
+			tsleep(sc->video, 0, "uvcdt", 1);
+		}
+	}
+
 	/* destroy video hard code only one */
 	uvc_drv_destroy_video(sc->video);
 
@@ -2507,12 +2574,13 @@
 	uvc_drv_destroy_ctrl(sc->ctrl);
 
 	if (sc->video) {
-		/* entry close */
-		uvc_v4l2_unreg(sc->video);
 		/* data free */
 		kfree(sc->video, M_UVC);
 		sc->video = NULL;
 	}
+	sc->data = NULL;
+	sc->ctrl = NULL;
+
 	DPRINTF("%s-%d-%p\n", __func__, __LINE__, self);
 	return 0;
 }
diff -ruN a/sys/bus/u4b/uvc/uvc_drv.h b/sys/bus/u4b/uvc/uvc_drv.h
--- a/sys/bus/u4b/uvc/uvc_drv.h	2026-10-14 18:40:54.648294861 +0000
+++ b/sys/bus/u4b/uvc/uvc_drv.h	2026-10-14 18:41:11.752074039 +0000
@@ -659,10 +659,9 @@
 	struct usb_xfer	*xfer[UVC_N_BULKTRANSFER];
 
 /* data format */
-	uint8_t nfmt;
-	uint8_t nfrm;
-	uint8_t nitv;
-	uint8_t unuse[3];
+	uint16_t nfmt;
+	uint16_t nfrm;
+	uint16_t nitv;
 	struct uvc_data_format *fmt;
 };