⬢ DragonFlyBSD Kernel Audit
DF-5305 / fix.diff
← back to finding ↓ download raw
--- sys/bus/u4b/uvc/uvc_drv.c.orig	2026-10-02 12:15:02.217368540 +0000
+++ sys/bus/u4b/uvc/uvc_drv.c	2026-10-02 12:17:44.607311079 +0000
@@ -1495,6 +1495,12 @@
 	struct uvc_data_request *req;
 	int ret, i;
 
+	/* A NULL streaming-interface state (VC header listed none, or no
+	 * VC header at all) must not reach the data->num_altsetting
+	 * dereference below. */
+	if (data == NULL)
+		return NULL;
+
 	v = kmalloc(sizeof(*v), M_UVC, M_ZERO | M_WAITOK);
 	if (v == NULL)
 		return v;
@@ -1841,6 +1847,8 @@
 		switch (desc->bDescriptorSubtype) {
 		case UDESCSUB_VS_COLORFORMAT:
 			cld = (struct uvc_vs_color_desc *)desc;
+			if (desc->bLength < 4)
+				break;
 			fmt->colorspace
 				= uvc_drv_get_colorspace(cld->bColorPris);
 			break;
@@ -1885,9 +1893,13 @@
 					sizeof(fmt->name));
 				fmt->fcc = fmtdes->fcc;
 			} else {
-				strlcpy(fmt->name,
-					(const char *)(fbraw->bGuidFmt),
-					sizeof(fmt->name));
+				/* bGuidFmt is 16 raw descriptor bytes and
+				 * is NOT NUL-terminated: never strlcpy()
+				 * it, the walk runs past the descriptor
+				 * and past the kmalloc()ed cdesc buffer. */
+				memcpy(fmt->name, fbraw->bGuidFmt,
+				    sizeof(fbraw->bGuidFmt));
+				fmt->name[sizeof(fbraw->bGuidFmt)] = 0;
 				fmt->fcc = 0;
 			}
 			fmt->bpp = fbraw->bBitsPerpixel;
@@ -2114,6 +2126,10 @@
 			if (vsnum > 1)
 				vsnum = 1;
 		}
+		/* The header must list at least one streaming interface
+		 * and must actually contain bIfaceList[0]. */
+		if (vsnum < 1 || hdesc->bLength < 12 + vsnum)
+			return EINVAL;
 
 		ctrl->revision = UGETW(hdesc->wRevision);
 
@@ -2206,6 +2222,10 @@
 		}
 
 		node_info_size = sizeof(struct uvc_ct_node_info);
+		/* bControlSize lives at offset 14: only read it when the
+		 * descriptor is long enough to contain it. */
+		if (it_desc->bLength < 15)
+			return EINVAL;
 		memcpy((void *)&ctrls_mask_size, (uint8_t *)it_desc + 14, 1);
 		src_ids_num = 0;
 
@@ -2329,7 +2349,15 @@
 		xu_desc = (struct uvc_vc_extension_unit_desc *)desc;
 
 		node_info_size = sizeof(struct uvc_xu_node_info);
-		src_ids_num = xu_desc->bLength >= 22 ? xu_desc->bNrInPins : 0;
+		/* bNrInPins is unbounded; the bControlSize byte it indexes
+		 * (offset 22 + bNrInPins) must stay inside bLength. */
+		if (xu_desc->bLength >= 23) {
+			src_ids_num = xu_desc->bNrInPins;
+			if (src_ids_num > xu_desc->bLength - 23)
+				src_ids_num = xu_desc->bLength - 23;
+		} else {
+			src_ids_num = 0;
+		}
 		ctrls_mask_size = xu_desc->bLength >= 24 ?
 			*(&xu_desc->bControlSize + src_ids_num) : 0;
 
@@ -2727,6 +2755,14 @@
 	}
 	uvc_ctrl_init_dev(sc, sc->ctrl);
 
+	/* uvc_drv_init_video() dereferences sc->data unconditionally; a
+	 * device whose VC header lists no streaming interface (or that
+	 * has no VC header at all) leaves sc->data NULL -> panic. */
+	if (sc->data == NULL) {
+		ret = EINVAL;
+		goto detach;
+	}
+
 	//hard code here, maybe not only one
 	sc->video = uvc_drv_init_video(sc, sc->ctrl, sc->data);
 	if (!sc->video) {