The branch main has been updated by bapt:

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

commit 12b4a02bd88117b07f3e142af71b70bade4fd57e
Author:     Baptiste Daroussin <[email protected]>
AuthorDate: 2026-07-22 08:18:23 +0000
Commit:     Baptiste Daroussin <[email protected]>
CommitDate: 2026-07-22 15:10:58 +0000

    uvideo: fix close/detach race on streaming teardown
    
    detach() stopped streaming and called uvideo_vs_close() before
    destroy_dev(), so a concurrent close() could race the teardown and call
    uvideo_vs_close() a second time (double usbd_transfer_unsetup), and
    mtx_destroy() could race a close still holding sc_mtx.  sc_streaming
    was also read without the lock in both paths.
    
    Reorder detach() to call destroy_dev() first so all in-flight cdev
    methods drain before any teardown.  Read sc_streaming under sc_mtx in
    both detach() and the last-close safety net.
---
 sys/dev/usb/video/uvideo.c | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/sys/dev/usb/video/uvideo.c b/sys/dev/usb/video/uvideo.c
index bfcd6696a8e0..57d31d2b5286 100644
--- a/sys/dev/usb/video/uvideo.c
+++ b/sys/dev/usb/video/uvideo.c
@@ -960,21 +960,20 @@ uvideo_detach(device_t dev)
 
        sc->sc_dying = 1;
 
-       /* Stop any active streaming */
-       if (sc->sc_streaming) {
-               mtx_lock(&sc->sc_mtx);
-               sc->sc_streaming = 0;
-               mtx_unlock(&sc->sc_mtx);
-               uvideo_vs_close(sc);
-       }
-
        /* Destroy character device */
        if (sc->sc_cdev != NULL) {
                destroy_dev(sc->sc_cdev);
                sc->sc_cdev = NULL;
        }
 
-       /* Unit number is implicitly freed when the cdev is destroyed */
+       /* Stop streaming if still active (e.g. detached while idle). */
+       mtx_lock(&sc->sc_mtx);
+       if (sc->sc_streaming) {
+               sc->sc_streaming = 0;
+               mtx_unlock(&sc->sc_mtx);
+               uvideo_vs_close(sc);
+       } else
+               mtx_unlock(&sc->sc_mtx);
 
        /* Free frame buffers */
        uvideo_vs_free_frame(sc);
@@ -2911,12 +2910,13 @@ uvideo_cdev_close(struct cdev *dev, int flags, int fmt, 
struct thread *td)
        mtx_unlock(&sc->sc_mtx);
 
        /* Last close: stop streaming if still active (safety net) */
+       mtx_lock(&sc->sc_mtx);
        if (sc->sc_streaming) {
-               mtx_lock(&sc->sc_mtx);
                sc->sc_streaming = 0;
                mtx_unlock(&sc->sc_mtx);
                uvideo_vs_close(sc);
-       }
+       } else
+               mtx_unlock(&sc->sc_mtx);
 
        uvideo_vs_free_frame(sc);
 

Reply via email to