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");