DF-5307 / fix.diff
--- 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) { |