⬢ DragonFlyBSD Kernel Audit
DF-5253 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/bus/u4b/audio/uaudio.c b/sys/bus/u4b/audio/uaudio.c
index 6fd409bfb9f..11111111111 100644
--- a/sys/bus/u4b/audio/uaudio.c
+++ b/sys/bus/u4b/audio/uaudio.c
@@ -328,6 +328,7 @@ struct uaudio_softc {
 	struct uaudio_search_result sc_mixer_clocks;
 	struct uaudio_mixer_node sc_mixer_node;
 	struct uaudio_configure_msg sc_config_msg[2];
+	struct sysctl_ctx_list sc_mixer_sysctl_ctx;

 	struct lock *sc_mixer_lock;
 	struct snd_mixer *sc_mixer_dev;
@@ -916,6 +917,10 @@ uaudio_attach(device_t dev)
 	usb_error_t err;
 	device_t child;

+	/* owns the mixer control sysctl OIDs (torn down in uaudio_detach,
+	 * before the PCM/mixer teardown that frees sc_mixer_lock) */
+	sysctl_ctx_init(&sc->sc_mixer_sysctl_ctx);
+
 	sc->sc_play_chan.priv_sc = sc;
 	sc->sc_rec_chan.priv_sc = sc;
 	sc->sc_udev = uaa->device;
@@ -1209,6 +1214,17 @@ uaudio_detach(device_t dev)
 	usbd_transfer_unsetup(sc->sc_play_chan.xfer, UAUDIO_NCHANBUFS + 1);
 	usbd_transfer_unsetup(sc->sc_rec_chan.xfer, UAUDIO_NCHANBUFS + 1);

+	/*
+	 * Remove the mixer control sysctls BEFORE the PCM/mixer teardown
+	 * (bus_generic_detach -> pcm_unregister -> mixer_uninit), which
+	 * frees sc->sc_mixer_lock via snd_mtxfree() while the sysctl OIDs
+	 * would still be registered.  sysctl_ctx_free() drains in-flight
+	 * handlers (handlers run under the shared sysctllock, teardown
+	 * takes it exclusively), so after this returns no handler can be
+	 * running and no new one can start.
+	 */
+	sysctl_ctx_free(&sc->sc_mixer_sysctl_ctx);
+
 	uaudio_hid_detach(sc);

 	if (bus_generic_detach(dev) != 0) {
@@ -2766,6 +2782,15 @@ found:
 	if (error != 0 || req->newptr == NULL)
 		return (error);

+	/*
+	 * Defense in depth: a racing detach may have torn the mixer lock
+	 * down (uaudio_mixer_uninit_sub sets sc_mixer_lock to NULL and
+	 * mixer_uninit then frees it) while we were crossing user memory
+	 * in sysctl_handle_int() above.
+	 */
+	if (sc->sc_mixer_lock == NULL)
+		return (ENXIO);
+
 	/* update mixer value */

 	lockmgr(sc->sc_mixer_lock, LK_EXCLUSIVE);
@@ -2806,10 +2831,9 @@ uaudio_mixer_register_sysctl(struct uaudio_softc *sc, device_t dev)
 	int chan;
 	int n;

-	mixer_tree = SYSCTL_ADD_NODE(device_get_sysctl_ctx(dev),
+	mixer_tree = SYSCTL_ADD_NODE(&sc->sc_mixer_sysctl_ctx,
 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "mixer",
 	    CTLFLAG_RD, NULL, "");
-
 	if (mixer_tree == NULL)
 		return;

@@ -2828,8 +2852,8 @@ uaudio_mixer_register_sysctl(struct uaudio_softc *sc, device_t dev)
 				ksnprintf(buf, sizeof(buf), "%s_%d",
 				    pmc->name, n);
 			}
-
-			control_tree = SYSCTL_ADD_NODE(device_get_sysctl_ctx(dev),
+			/* the nodes below live in sc->sc_mixer_sysctl_ctx */
+			control_tree = SYSCTL_ADD_NODE(&sc->sc_mixer_sysctl_ctx,
 			    SYSCTL_CHILDREN(mixer_tree), OID_AUTO, buf,
 			    CTLFLAG_RD, NULL, "Mixer control nodes");

@@ -2837,22 +2861,22 @@ uaudio_mixer_register_sysctl(struct uaudio_softc *sc, device_t dev)
 			if (control_tree == NULL)
 				continue;

-			SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+			SYSCTL_ADD_PROC(&sc->sc_mixer_sysctl_ctx,
 			    SYSCTL_CHILDREN(control_tree),
 			    OID_AUTO, "val", CTLTYPE_INT | CTLFLAG_RW, sc,
 			    pmc->wValue[chan],
 			    uaudio_mixer_sysctl_handler, "I", "Current value");

-			SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
+			SYSCTL_ADD_INT(&sc->sc_mixer_sysctl_ctx,
 			    SYSCTL_CHILDREN(control_tree),
 			    OID_AUTO, "min", CTLFLAG_RD, 0, pmc->minval,
 			    "Minimum value");

-			SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
+			SYSCTL_ADD_INT(&sc->sc_mixer_sysctl_ctx,
 			    SYSCTL_CHILDREN(control_tree),
 			    OID_AUTO, "max", CTLFLAG_RD, 0, pmc->maxval,
 			    "Maximum value");

-			SYSCTL_ADD_STRING(device_get_sysctl_ctx(dev),
+			SYSCTL_ADD_STRING(&sc->sc_mixer_sysctl_ctx,
 			    SYSCTL_CHILDREN(control_tree),
 			    OID_AUTO, "desc", CTLFLAG_RD, pmc->desc, 0,
 			    "Description");