Date: Wed, 22 Jul 2026 15:12:53 +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: c141d75430c7 - main - uvideo: validate frame descriptors and fix integer overflows in size computation Message-ID: <6a60ddf5.344e8.13bc6cae@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=c141d75430c75d21aa8260287b9e2a05905335b3 commit c141d75430c75d21aa8260287b9e2a05905335b3 Author: Baptiste Daroussin <bapt@FreeBSD.org> AuthorDate: 2026-07-22 12:15:21 +0000 Commit: Baptiste Daroussin <bapt@FreeBSD.org> CommitDate: 2026-07-22 15:10:58 +0000 uvideo: validate frame descriptors and fix integer overflows in size computation --- sys/dev/usb/video/uvideo.c | 114 ++++++++++++++++++++++++++++----------------- 1 file changed, 70 insertions(+), 44 deletions(-) diff --git a/sys/dev/usb/video/uvideo.c b/sys/dev/usb/video/uvideo.c index b31b3fe294cd..a00fcc5a7395 100644 --- a/sys/dev/usb/video/uvideo.c +++ b/sys/dev/usb/video/uvideo.c @@ -233,7 +233,7 @@ struct uvideo_softc { int sc_streaming; int sc_max_ctrl_size; - int sc_max_fbuf_size; + uint32_t sc_max_fbuf_size; int sc_negotiated_flag; int sc_frame_rate; @@ -1308,6 +1308,7 @@ uvideo_vs_parse_desc(struct uvideo_softc *sc, return (error); /* Parse video stream frame descriptors */ + sc->sc_fmtgrp_idx = 0; error = uvideo_vs_parse_desc_frame(sc); if (error != USB_ERR_NORMAL_COMPLETION) return (error); @@ -1715,9 +1716,15 @@ uvideo_vs_parse_desc_frame_buffer_size(struct uvideo_softc *sc, struct usb_video_frame_desc *fd = __DECONST(struct usb_video_frame_desc *, desc); int fmtidx, frame_num; - uint32_t fbuf_size; + uint64_t fbuf_size; fmtidx = sc->sc_fmtgrp_idx; + if (fmtidx >= UVIDEO_MAX_FORMAT || + sc->sc_fmtgrp[fmtidx].format == NULL) { + device_printf(sc->sc_dev, + "frame descriptor without format!\n"); + return (USB_ERR_INVAL); + } frame_num = sc->sc_fmtgrp[fmtidx].frame_num; if (frame_num >= UVIDEO_MAX_FRAME) { device_printf(sc->sc_dev, @@ -1744,7 +1751,7 @@ uvideo_vs_parse_desc_frame_buffer_size(struct uvideo_softc *sc, fbuf_size = UGETDW(fd->u.uc.dwMaxVideoFrameBufferSize); if (fbuf_size > sc->sc_max_fbuf_size) - sc->sc_max_fbuf_size = fbuf_size; + sc->sc_max_fbuf_size = (uint32_t)fbuf_size; if (++sc->sc_fmtgrp[fmtidx].frame_num == sc->sc_fmtgrp[fmtidx].format->bNumFrameDescriptors) @@ -1761,9 +1768,16 @@ uvideo_vs_parse_desc_frame_max_rate(struct uvideo_softc *sc, __DECONST(struct usb_video_frame_desc *, desc); uint8_t *p; int i, fmtidx, frame_num, length, nivals; - uint32_t fbuf_size, frame_ival, next_frame_ival; + uint64_t fbuf_size; + uint32_t frame_ival, next_frame_ival; fmtidx = sc->sc_fmtgrp_idx; + if (fmtidx >= UVIDEO_MAX_FORMAT || + sc->sc_fmtgrp[fmtidx].format == NULL) { + device_printf(sc->sc_dev, + "frame descriptor without format!\n"); + return (USB_ERR_INVAL); + } frame_num = sc->sc_fmtgrp[fmtidx].frame_num; if (frame_num >= UVIDEO_MAX_FRAME) { device_printf(sc->sc_dev, @@ -1803,7 +1817,7 @@ uvideo_vs_parse_desc_frame_max_rate(struct uvideo_softc *sc, fbuf_size /= 8 * 10000000; if (fbuf_size > sc->sc_max_fbuf_size) - sc->sc_max_fbuf_size = fbuf_size; + sc->sc_max_fbuf_size = (uint32_t)fbuf_size; if (++sc->sc_fmtgrp[fmtidx].frame_num == sc->sc_fmtgrp[fmtidx].format->bNumFrameDescriptors) @@ -2240,7 +2254,7 @@ uvideo_vs_alloc_frame(struct uvideo_softc *sc) fb->buf_size = UGETDW(sc->sc_desc_probe.dwMaxVideoFrameSize); - if (sc->sc_max_fbuf_size < fb->buf_size && sc->sc_mmap_flag == 0) { + if (sc->sc_max_fbuf_size < fb->buf_size) { device_printf(sc->sc_dev, "software video buffer too small!\n"); return (USB_ERR_NOMEM); @@ -2948,47 +2962,58 @@ uvideo_cdev_read(struct cdev *dev, struct uio *uio, int ioflag) return (EIO); /* Start streaming in read mode if not already running */ - if (sc->sc_vidmode == VIDMODE_NONE) { - if (devfs_get_cdevpriv((void **)&priv) != 0 || priv == NULL) - return (EINVAL); - sc->sc_mmap_flag = 0; - sc->sc_vidmode = VIDMODE_READ; + mtx_lock(&sc->sc_mtx); + if (sc->sc_vidmode != VIDMODE_NONE) { + mtx_unlock(&sc->sc_mtx); + if (sc->sc_vidmode != VIDMODE_READ) + return (EBUSY); + goto read_wait; + } + if (devfs_get_cdevpriv((void **)&priv) != 0 || priv == NULL) { + mtx_unlock(&sc->sc_mtx); + return (EINVAL); + } + sc->sc_mmap_flag = 0; + sc->sc_vidmode = VIDMODE_READ; + mtx_unlock(&sc->sc_mtx); - error = uvideo_vs_init(sc); - if (error != USB_ERR_NORMAL_COMPLETION) { - sc->sc_vidmode = VIDMODE_NONE; - return (EIO); - } + error = uvideo_vs_init(sc); + if (error != USB_ERR_NORMAL_COMPLETION) { + mtx_lock(&sc->sc_mtx); + sc->sc_vidmode = VIDMODE_NONE; + mtx_unlock(&sc->sc_mtx); + return (EIO); + } - /* Allocate a separate read buffer for frame delivery */ - sc->sc_fbufferlen = sc->sc_max_fbuf_size; - if (sc->sc_fbufferlen == 0) - sc->sc_fbufferlen = - UGETDW(sc->sc_desc_probe.dwMaxVideoFrameSize); + /* Allocate a separate read buffer for frame delivery */ + sc->sc_fbufferlen = sc->sc_max_fbuf_size; + if (sc->sc_fbufferlen == 0) + sc->sc_fbufferlen = + UGETDW(sc->sc_desc_probe.dwMaxVideoFrameSize); + if (sc->sc_fbuffer == NULL) { + sc->sc_fbuffer = malloc(sc->sc_fbufferlen, M_USBDEV, + M_WAITOK | M_ZERO); if (sc->sc_fbuffer == NULL) { - sc->sc_fbuffer = malloc(sc->sc_fbufferlen, M_USBDEV, - M_WAITOK | M_ZERO); - if (sc->sc_fbuffer == NULL) { - sc->sc_vidmode = VIDMODE_NONE; - return (ENOMEM); - } + mtx_lock(&sc->sc_mtx); + sc->sc_vidmode = VIDMODE_NONE; + mtx_unlock(&sc->sc_mtx); + return (ENOMEM); } + } - mtx_lock(&sc->sc_mtx); - sc->sc_streaming = 1; - priv->streaming = 1; - if (sc->sc_vs_cur->bulk_endpoint) - usbd_transfer_start(sc->sc_xfer[0]); - else { - int i; - for (i = 0; i < UVIDEO_IXFERS; i++) - usbd_transfer_start(sc->sc_xfer[i]); - } - mtx_unlock(&sc->sc_mtx); + mtx_lock(&sc->sc_mtx); + sc->sc_streaming = 1; + priv->streaming = 1; + if (sc->sc_vs_cur->bulk_endpoint) + usbd_transfer_start(sc->sc_xfer[0]); + else { + int i; + for (i = 0; i < UVIDEO_IXFERS; i++) + usbd_transfer_start(sc->sc_xfer[i]); } + mtx_unlock(&sc->sc_mtx); - if (sc->sc_vidmode != VIDMODE_READ) - return (EBUSY); +read_wait: /* Wait for a frame */ mtx_lock(&sc->sc_mtx); @@ -3181,7 +3206,9 @@ uvideo_cdev_mmap_single(struct cdev *dev, vm_ooffset_t *offset, vm_object_reference(sc->sc_mmap_object); *object = sc->sc_mmap_object; + mtx_lock(&sc->sc_mtx); sc->sc_mmap_flag = 1; + mtx_unlock(&sc->sc_mtx); return (0); } @@ -3430,7 +3457,7 @@ uvideo_s_fmt(struct uvideo_softc *sc, struct v4l2_format *fmt) * and commit controls with the device would disrupt the active USB * transfers. V4L2 mandates EBUSY in this case. */ mtx_lock(&sc->sc_mtx); - if (sc->sc_streaming) { + if (sc->sc_streaming || sc->sc_mmap_count > 0) { mtx_unlock(&sc->sc_mtx); return (EBUSY); } @@ -3520,7 +3547,7 @@ uvideo_s_parm(struct uvideo_softc *sc, struct v4l2_streamparm *parm) /* Reject parameter changes while streaming for the same reason as * S_FMT: they re-negotiate with the device. */ mtx_lock(&sc->sc_mtx); - if (sc->sc_streaming) { + if (sc->sc_streaming || sc->sc_mmap_count > 0) { mtx_unlock(&sc->sc_mtx); return (EBUSY); } @@ -3632,8 +3659,7 @@ uvideo_reqbufs(struct uvideo_softc *sc, struct v4l2_requestbuffers *rb) sc->sc_mmap_count = rb->count; buf_size = UGETDW(sc->sc_desc_probe.dwMaxVideoFrameSize); - if (buf_size == 0 || sc->sc_max_fbuf_size <= 0 || - buf_size > (u_int)sc->sc_max_fbuf_size) + if (buf_size == 0 || buf_size > sc->sc_max_fbuf_size) return (EINVAL); if (SIZE_MAX / sc->sc_mmap_count < buf_size) return (EINVAL);home | help
Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a60ddf5.344e8.13bc6cae>
