⬢ DragonFlyBSD Kernel Audit
DF-5427 / fix.diff
← back to finding ↓ download raw
Fix DF-5427: signed/unsigned index confusion in
uvc_drv_enum_v4l2_frameintervals() (VIDIOC_ENUM_FRAMEINTERVALS).

The user-controlled v4l2_frmivalenum.index (__u32) was assigned to a signed
int (uvc_drv.c:750).  Any value >= 0x80000000 becomes negative, the
`index < nintervals` guard at uvc_drv.c:756 then passes trivially, and
frm->interval[index].val at uvc_drv.c:768 reads kernel heap at an
attacker-chosen negative word offset (down to -8GiB) relative to the
interval array, disclosing the (continued-fraction transformed) value to
unprivileged userspace through the _IOWR copyout -- and a wild offset
fatal-faults the kernel (confirmed: Fatal trap 12 at
uvc_drv_enum_v4l2_frameintervals+0x18b, triggered by uid=1001).

Fix: keep `index` unsigned and compare against the (positive) interval
count in unsigned arithmetic.  Positive indices behave exactly as before.

Apply with: git apply -p0   (from /usr/src)
or:           patch -p0 < fix.diff

--- sys/bus/u4b/uvc/uvc_drv.c
+++ sys/bus/u4b/uvc/uvc_drv.c
@@ -734,7 +734,8 @@
 {
 	struct uvc_data_format *loop, *fmt = NULL;
 	struct uvc_data_frame *frm = NULL;
-	int i, index, nintervals;
+	int i, nintervals;
+	uint32_t index;
 
 	for (i = 0; i < v->data->nfmt; i++) {
 		loop = v->data->fmt + i;
@@ -753,7 +754,7 @@
 		    fmt->frm[i].height == itv->height) {
 			frm = &fmt->frm[i];
 			nintervals = frm->interval_type ? frm->interval_type : 1;
-			if (index < nintervals)
+			if (index < (uint32_t)nintervals)
 				break;
 			index -= nintervals;
 		}