From nobody Wed Jul 22 15:12:47 2026 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4h4yRh1YGWz6mFTm for ; Wed, 22 Jul 2026 15:12:48 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "YR1" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4h4yRg5P7Dz3kjJ for ; Wed, 22 Jul 2026 15:12:47 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1784733167; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=2lw+qz2+lkO1v0ALIH1b5NuogDTkJ7V+ipxNegipPvs=; b=ca6R7H6eGt908TrFJ8viAcEuuOn/9rapTeRKsSK4TuinW2Qfl5VVsOMf9hVB18LSmuiBb+ AM+GgvfRFeBtXJ56IQUgyvrXWlz1Khl2aVcX5lgOwYOwlP4BQIaBgPEPWNvDpCpOoKQ0St 86LdHOP3SEReIA0ch4awEgY6cZ9ifHFiMVYNpEWC+IbIlFJQwa1ex6Yz9P9DiX7zRNSJHz dvkKgN0tzfUj60uNMpKGtw5Pkdfaxvl7z4O9bkoOyKPJwih4LDIpK/0g5+NGwdswaN2LPK zt2VnwxiISC3ro5FWNuWnfyXOGaZ1BOLXtrpuNLva3h7TccB7Dgz59TJTIktuQ== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1784733167; a=rsa-sha256; cv=none; b=PYYN3XVlL3RX4LVnSuSbPcaNgRutgXSS5xBx3yNoaV0qC9n3uHYuGn13KQP7VvVPk484jO s2IIwtSTSQRTQkhj4g9AfFQvfuoTW/2nKAuaWz7wGDXvuZnBe3e42n0yHbukwsWn/s11Yh 8txBNynuCK1izcbfjDkcPmVaCXbOmGraMJAASeW3BujlIxVvYfA6j/8nFUIrtNHpa6UTwC yG6OBRgJJgsOZKnpdWlv7M2AC1STEHhWTnyoJlnN5usGqEEnV9cZxjtii1vU/DG8joU/2F U90FU7gIYm2c+Bb6WwCRtoEK9AW1kpopjT27bNZZxRSQwl8h2TWOjjwQtdjCSA== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1784733167; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=2lw+qz2+lkO1v0ALIH1b5NuogDTkJ7V+ipxNegipPvs=; b=xbeefoxKrdxxoU4cBuo/Yep5aDKemlWFkQzZZ4o2sxFEyzDgTzHSFSK13wPBd1/34yXdZ4 cfVC1MOPcq4OOwH5Hpn+rJ2xL/ggcfDqGiu/c0kb0mn4M/bo75Asi8YbhhvOXrzP8AiQCi axP3McaPhhwG+JbrCORUTgOi3ICQJUE2NT/rmLj+o6qiSeBmYHC9JET2P5KaOOo2LBZMT2 FfEdWE1kh2ei9bwOlVMRMVJbTnLsgezGgmvxJ2BnCG5EE5sPDeS9qtjBWtK5+5Sa5eJmFZ nqEbMqPz1M0lH+DYZrGnebV7gkOpxOHAWuW4tOrgTjLfQP3a6V6zvFq+Wylt5Q== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4h4yRg41n4z7sh for ; Wed, 22 Jul 2026 15:12:47 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 3377b by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Wed, 22 Jul 2026 15:12:47 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Baptiste Daroussin Subject: git: f12dd1d5f030 - main - uvideo: lock the mmap queue and read path List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: bapt X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: f12dd1d5f0303fe3bc5030293bda04b04995b72c Auto-Submitted: auto-generated Date: Wed, 22 Jul 2026 15:12:47 +0000 Message-Id: <6a60ddef.3377b.5517bd77@gitrepo.freebsd.org> The branch main has been updated by bapt: URL: https://cgit.FreeBSD.org/src/commit/?id=f12dd1d5f0303fe3bc5030293bda04b04995b72c commit f12dd1d5f0303fe3bc5030293bda04b04995b72c Author: Baptiste Daroussin AuthorDate: 2026-07-22 07:26:18 +0000 Commit: Baptiste Daroussin CommitDate: 2026-07-22 15:10:57 +0000 uvideo: lock the mmap queue and read path qbuf(), dqbuf() and read() manipulated sc_mmap_q / sc_mmap_cur / sc_frames_ready without sc_mtx, racing with the USB transfer callbacks (producer) that run under the mutex. This could corrupt the queue or trigger use-after-free. Take sc_mtx around qbuf(), use mtx_sleep() and protect the queue operations in dqbuf(), and use mtx_sleep() with a snapshot of sc_fsize in read(). Also reject S_FMT and S_PARM with EBUSY while streaming: both re-negotiate the probe/commit controls with the device, which disrupts the active USB transfers (a second client opening the device would otherwise freeze the first one's stream). --- sys/dev/usb/video/uvideo.c | 72 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 13 deletions(-) diff --git a/sys/dev/usb/video/uvideo.c b/sys/dev/usb/video/uvideo.c index 9c6076bc76d0..708660adeafb 100644 --- a/sys/dev/usb/video/uvideo.c +++ b/sys/dev/usb/video/uvideo.c @@ -2893,7 +2893,7 @@ uvideo_cdev_read(struct cdev *dev, struct uio *uio, int ioflag) { struct uvideo_softc *sc = dev->si_drv1; usb_error_t error; - int ret; + int ret, fsize; if (sc == NULL || sc->sc_dying) return (ENXIO); @@ -2942,23 +2942,31 @@ uvideo_cdev_read(struct cdev *dev, struct uio *uio, int ioflag) return (EBUSY); /* Wait for a frame */ + mtx_lock(&sc->sc_mtx); while (sc->sc_frames_ready == 0) { - if (ioflag & IO_NDELAY) + if (ioflag & IO_NDELAY) { + mtx_unlock(&sc->sc_mtx); return (EWOULDBLOCK); - ret = tsleep(sc, PCATCH, "uvread", hz * 10); - if (ret != 0) + } + ret = mtx_sleep(sc, &sc->sc_mtx, PCATCH, "uvread", hz * 10); + if (ret != 0) { + mtx_unlock(&sc->sc_mtx); return (ret); - if (sc->sc_dying) + } + if (sc->sc_dying) { + mtx_unlock(&sc->sc_mtx); return (ENXIO); + } } sc->sc_frames_ready--; + fsize = sc->sc_fsize; + mtx_unlock(&sc->sc_mtx); - if (sc->sc_fsize == 0) + if (fsize == 0) return (0); - return (uiomove(sc->sc_fbuffer, MIN(uio->uio_resid, sc->sc_fsize), - uio)); + return (uiomove(sc->sc_fbuffer, MIN(uio->uio_resid, fsize), uio)); } static int @@ -3367,6 +3375,16 @@ uvideo_s_fmt(struct uvideo_softc *sc, struct v4l2_format *fmt) if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return (EINVAL); + /* Reject format changes while streaming: re-negotiating the probe + * 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) { + mtx_unlock(&sc->sc_mtx); + return (EBUSY); + } + mtx_unlock(&sc->sc_mtx); + DPRINTFN(1, "s_fmt: requested %dx%d\n", fmt->fmt.pix.width, fmt->fmt.pix.height); @@ -3448,6 +3466,15 @@ uvideo_s_parm(struct uvideo_softc *sc, struct v4l2_streamparm *parm) { usb_error_t error; + /* 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) { + mtx_unlock(&sc->sc_mtx); + return (EBUSY); + } + mtx_unlock(&sc->sc_mtx); + if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { if (parm->parm.capture.timeperframe.numerator == 0 || parm->parm.capture.timeperframe.denominator == 0) @@ -3652,8 +3679,11 @@ uvideo_qbuf(struct uvideo_softc *sc, struct v4l2_buffer *qb) qb->index >= sc->sc_mmap_count) return (EINVAL); + /* Serialize with the USB transfer callbacks (producer). */ + mtx_lock(&sc->sc_mtx); sc->sc_mmap[qb->index].v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE; sc->sc_mmap[qb->index].v4l2_buf.flags |= V4L2_BUF_FLAG_QUEUED; + mtx_unlock(&sc->sc_mtx); DPRINTFN(2, "buffer %d ready for queueing\n", qb->index); @@ -3670,24 +3700,40 @@ uvideo_dqbuf(struct uvideo_softc *sc, struct v4l2_buffer *dqb) dqb->memory != V4L2_MEMORY_MMAP) return (EINVAL); - if (STAILQ_EMPTY(&sc->sc_mmap_q)) { - error = tsleep(sc, PCATCH, "uvdqbuf", hz * 10); - if (error) + /* + * Serialize with the USB transfer callbacks (producer) that insert + * completed buffers into sc_mmap_q under sc_mtx. Use mtx_sleep so + * the wait and the queue inspection are atomic. + */ + mtx_lock(&sc->sc_mtx); + while (STAILQ_EMPTY(&sc->sc_mmap_q)) { + error = mtx_sleep(sc, &sc->sc_mtx, PCATCH, "uvdqbuf", hz * 10); + if (error != 0) { + mtx_unlock(&sc->sc_mtx); return (EINVAL); + } + if (sc->sc_dying) { + mtx_unlock(&sc->sc_mtx); + return (ENXIO); + } } mmap = STAILQ_FIRST(&sc->sc_mmap_q); - if (mmap == NULL) + if (mmap == NULL) { + mtx_unlock(&sc->sc_mtx); return (EINVAL); + } bcopy(&mmap->v4l2_buf, dqb, sizeof(struct v4l2_buffer)); mmap->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE; mmap->v4l2_buf.flags &= ~V4L2_BUF_FLAG_QUEUED; + STAILQ_REMOVE_HEAD(&sc->sc_mmap_q, q_frames); + mtx_unlock(&sc->sc_mtx); + DPRINTFN(2, "frame dequeued from index %d\n", mmap->v4l2_buf.index); - STAILQ_REMOVE_HEAD(&sc->sc_mmap_q, q_frames); return (0); }