⬢ DragonFlyBSD Kernel Audit
DF-4504 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/sound/pci/t4dwave.c
+++ b/sys/dev/sound/pci/t4dwave.c
@@ -830,7 +830,7 @@
 	struct tr_info *tr;
 	struct ac97_info *codec = 0;
 	bus_addr_t	lowaddr;
-	int		i, dacn;
+	int		i, dacn, mixinit = 0;
 	char 		status[SND_STATUSLEN];
 
 	tr = kmalloc(sizeof(*tr), M_DEVBUF, M_WAITOK | M_ZERO);
@@ -880,6 +880,7 @@
 	codec = AC97_CREATE(dev, tr, tr_ac97);
 	if (codec == NULL) goto bad;
 	if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto bad;
+	mixinit = 1;
 
 	tr->irqid = 0;
 	tr->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &tr->irqid,
@@ -935,7 +936,17 @@
 	return 0;
 
 bad:
-	if (codec) ac97_destroy(codec);
+	/*
+	 * If mixer_init() succeeded, the 0666 /dev/mixerN cdev it created
+	 * (mixer.c:730-733) still dispatches kobj methods through this
+	 * codec.  mixer_uninit() destroys the cdev, the snd_mixer and the
+	 * codec itself (ac97mix_uninit -> ac97_destroy), mirroring the
+	 * pcm_unregister() unwind order (sound.c:1205).
+	 */
+	if (mixinit)
+		mixer_uninit(dev);
+	else if (codec)
+		ac97_destroy(codec);
 	if (tr->reg) bus_release_resource(dev, tr->regtype, tr->regid, tr->reg);
 	if (tr->ih) bus_teardown_intr(dev, tr->irq, tr->ih);
 	if (tr->irq) bus_release_resource(dev, SYS_RES_IRQ, tr->irqid, tr->irq);