Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 22 Jul 2026 15:12:48 +0000
From:      Baptiste Daroussin <bapt@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: cb26bda8ca36 - main - uvideo: bounds-check frame interval reads against bLength
Message-ID:  <6a60ddf0.3377f.5adea594@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by bapt:

URL: https://cgit.FreeBSD.org/src/commit/?id=cb26bda8ca36e0e421f75d82e1aa46df8f2ff814

commit cb26bda8ca36e0e421f75d82e1aa46df8f2ff814
Author:     Baptiste Daroussin <bapt@FreeBSD.org>
AuthorDate: 2026-07-22 07:42:29 +0000
Commit:     Baptiste Daroussin <bapt@FreeBSD.org>
CommitDate: 2026-07-22 15:10:57 +0000

    uvideo: bounds-check frame interval reads against bLength
    
    Frame interval data is read from device-supplied frame descriptors whose
    bLength may be shorter than the number of intervals declared by
    bFrameIntervalType.  The continuous branch of uvideo_enum_fivals() read
    three intervals unconditionally, and the discrete branch checked the
    pointer but not the four bytes that UGETDW() reads, so a short or
    malformed descriptor could read past bLength and leak adjacent kernel
    memory to userspace.  uvideo_vs_parse_desc_frame_max_rate() had the same
    class of off-by-up-to-three-bytes read.
    
    Compute the available bytes from bLength and validate before each read.
    
    Reported by:    emaste
---
 sys/dev/usb/video/uvideo.c | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/sys/dev/usb/video/uvideo.c b/sys/dev/usb/video/uvideo.c
index 708660adeafb..ac54eba19695 100644
--- a/sys/dev/usb/video/uvideo.c
+++ b/sys/dev/usb/video/uvideo.c
@@ -1791,7 +1791,7 @@ uvideo_vs_parse_desc_frame_max_rate(struct uvideo_softc *sc,
 	nivals = UVIDEO_FRAME_NUM_INTERVALS(fd);
 
 	for (i = 0; i < nivals; i++) {
-		if (length <= 0)
+		if (length < (int)sizeof(uDWord))
 			break;
 		next_frame_ival = UGETDW(p);
 		if (next_frame_ival > frame_ival)
@@ -3294,7 +3294,7 @@ uvideo_enum_fsizes(struct uvideo_softc *sc, struct v4l2_frmsizeenum *fsizes)
 static int
 uvideo_enum_fivals(struct uvideo_softc *sc, struct v4l2_frmivalenum *fivals)
 {
-	int idx;
+	int idx, ival_bytes;
 	struct uvideo_format_group *fmtgrp = NULL;
 	struct usb_video_frame_desc *frame = NULL;
 	uint8_t *p;
@@ -3327,6 +3327,9 @@ uvideo_enum_fivals(struct uvideo_softc *sc, struct v4l2_frmivalenum *fivals)
 		return (EINVAL);
 
 	p = (uint8_t *)frame + UVIDEO_FRAME_MIN_LEN(frame);
+	ival_bytes = (int)frame->bLength - (int)UVIDEO_FRAME_MIN_LEN(frame);
+	if (ival_bytes < 0)
+		return (EINVAL);
 
 	bzero(fivals, sizeof(*fivals));
 	fivals->index = fi_index;
@@ -3337,6 +3340,8 @@ uvideo_enum_fivals(struct uvideo_softc *sc, struct v4l2_frmivalenum *fivals)
 	if (UVIDEO_FRAME_NUM_INTERVALS(frame) == 0) {
 		if (fi_index != 0)
 			return (EINVAL);
+		if (ival_bytes < (int)(3 * sizeof(uDWord)))
+			return (EINVAL);
 		fivals->type = V4L2_FRMIVAL_TYPE_STEPWISE;
 		fivals->stepwise.min.numerator = UGETDW(p);
 		fivals->stepwise.min.denominator = 10000000;
@@ -3349,12 +3354,9 @@ uvideo_enum_fivals(struct uvideo_softc *sc, struct v4l2_frmivalenum *fivals)
 	} else {
 		if (fi_index >= (uint32_t)UVIDEO_FRAME_NUM_INTERVALS(frame))
 			return (EINVAL);
-		p += sizeof(uDWord) * fi_index;
-		if (p > frame->bLength + (uint8_t *)frame) {
-			device_printf(sc->sc_dev,
-			    "frame desc too short?\n");
+		if (ival_bytes < (int)((fi_index + 1) * sizeof(uDWord)))
 			return (EINVAL);
-		}
+		p += sizeof(uDWord) * fi_index;
 		fivals->type = V4L2_FRMIVAL_TYPE_DISCRETE;
 		fivals->discrete.numerator = UGETDW(p);
 		fivals->discrete.denominator = 10000000;


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a60ddf0.3377f.5adea594>