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.
This commit is contained in:
Baptiste Daroussin committed 2026-07-22 17:10:58 +02:00
1 parent 2120f3e5ec
commit 12b4a02bd8
1 file changed
+11 -11
+11 -11
View File
@@ -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);