git: 5760c22a0522 - main - sound: Let pcm_unregister() tear down a partially initialized device

From: Christos Margiolis <christos_at_FreeBSD.org>
Date: Tue, 27 Oct 2026 11:28:09 UTC
The branch main has been updated by christos:

URL: https://cgit.FreeBSD.org/src/commit/?id=5760c22a05220cfb6b6cbf623f2cda5047b28706

commit 5760c22a05220cfb6b6cbf623f2cda5047b28706
Author:     Christos Margiolis <christos@FreeBSD.org>
AuthorDate: 2026-10-27 11:26:29 +0000
Commit:     Christos Margiolis <christos@FreeBSD.org>
CommitDate: 2026-10-27 11:26:29 +0000

    sound: Let pcm_unregister() tear down a partially initialized device
    
    Since 3107b952f534 ("sound: Merge PCM_ALIVE() with PCM_REGISTERED()"),
    pcm_unregister() is gated on PCM_REGISTERED(), so it cannot run unless
    pcm_register() has already succeeded, which means drivers will leak
    resources if a failure happened prior to this.
    
    Reintroduce PCM_ALIVE(), but test dev rather than the lock. pcm_init()
    assigns dev before anything else, so it can tell us whether pcm_init()
    has run or not. Make sure to clear it in pcm_unregister() so that a
    second call to pcm_unregister() cannot double-free.
    
    While here, publish to sndstat(4) only once pcm_register() has
    succeeded.
    
    Reported by:    jah
    Fixes:          3107b952f534 ("sound: Merge PCM_ALIVE() with PCM_REGISTERED()")
    MFC after:      1 week
    Sponsored by:   The FreeBSD Foundation
    Reviewed by:    jah, markj
    Differential Revision:  https://reviews.freebsd.org/D60801
---
 sys/dev/sound/pcm/dsp.c   |  9 ++++++++-
 sys/dev/sound/pcm/sound.c | 10 ++++++----
 sys/dev/sound/pcm/sound.h |  4 ++--
 3 files changed, 16 insertions(+), 7 deletions(-)

diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c
index e1242ec4deb4..9277a1f337ff 100644
--- a/sys/dev/sound/pcm/dsp.c
+++ b/sys/dev/sound/pcm/dsp.c
@@ -160,7 +160,14 @@ dsp_destroy_dev(device_t dev)
 	struct snddev_info *d;
 
 	d = device_get_softc(dev);
-	destroy_dev(d->dsp_dev);
+	/*
+	 * The cdev may not exist if the driver failed before or during
+	 * pcm_register().
+	 */
+	if (d->dsp_dev != NULL) {
+		destroy_dev(d->dsp_dev);
+		d->dsp_dev = NULL;
+	}
 }
 
 static int
diff --git a/sys/dev/sound/pcm/sound.c b/sys/dev/sound/pcm/sound.c
index 92aedb552d27..f0db8554c950 100644
--- a/sys/dev/sound/pcm/sound.c
+++ b/sys/dev/sound/pcm/sound.c
@@ -428,8 +428,6 @@ pcm_register(device_t dev, char *str)
 	vchan_initsys(dev);
 	feeder_eq_initsys(dev);
 
-	sndstat_register(dev, SNDST_TYPE_PCM, d->status);
-
 	err = dsp_make_dev(dev);
 	if (err)
 		return (err);
@@ -441,6 +439,9 @@ pcm_register(device_t dev, char *str)
 		}
 	}
 
+	/* Publish only once registration really has finished properly. */
+	sndstat_register(dev, SNDST_TYPE_PCM, d->status);
+
 	bus_topo_lock();
 	if (snd_unit_auto < 0)
 		snd_unit_auto = (snd_unit < 0) ? 1 : 0;
@@ -463,8 +464,8 @@ pcm_unregister(device_t dev)
 
 	d = device_get_softc(dev);
 
-	if (!PCM_REGISTERED(d)) {
-		device_printf(dev, "unregister: device not configured\n");
+	if (!PCM_ALIVE(d)) {
+		device_printf(dev, "unregister: device not initialized\n");
 		return (0);
 	}
 
@@ -496,6 +497,7 @@ pcm_unregister(device_t dev)
 
 	cv_destroy(&d->cv);
 	mtx_destroy(&d->lock);
+	d->dev = NULL;
 
 	bus_topo_lock();
 	if (snd_unit == device_get_unit(dev)) {
diff --git a/sys/dev/sound/pcm/sound.h b/sys/dev/sound/pcm/sound.h
index ecd4dec5a170..16110e46a152 100644
--- a/sys/dev/sound/pcm/sound.h
+++ b/sys/dev/sound/pcm/sound.h
@@ -122,8 +122,8 @@ struct snd_mixer;
 				"\015PVCHANS"				\
 				"\016RVCHANS"
 
-#define PCM_REGISTERED(x)	\
-	((x) != NULL && ((x)->flags & SD_F_REGISTERED))
+#define PCM_ALIVE(x)		((x) != NULL && (x)->dev != NULL)
+#define PCM_REGISTERED(x)	(PCM_ALIVE(x) && ((x)->flags & SD_F_REGISTERED))
 
 #define	PCM_MAXCHANS		10000
 #define	PCM_CHANCOUNT(d)	\