⬢ DragonFlyBSD Kernel Audit
DF-4651 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/sound/pci/fm801.c
+++ b/sys/dev/sound/pci/fm801.c
@@ -614,11 +614,6 @@
 
 	fm801_init(fm801);
 
-	codec = AC97_CREATE(dev, fm801, fm801_ac97);
-	if (codec == NULL) goto oops;
-
-	if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto oops;
-
 	fm801->irqid = 0;
 	fm801->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &fm801->irqid,
 					    RF_ACTIVE | RF_SHAREABLE);
@@ -638,12 +633,33 @@
 		goto oops;
 	}
 
+#define FM801_MAXPLAYCH	1
+	if (pcm_register(dev, fm801, FM801_MAXPLAYCH, 1)) goto oops;
+
+	/*
+	 * Only publish the mixer once no failure path remains: mixer_init()
+	 * creates the 0666 /dev/mixerN cdev that dispatches into codec/fm801
+	 * (mixer.c make_dev + si_drv1), so a later goto oops would leave a
+	 * live cdev over ac97_destroy()ed/kfree()d objects (DF-4156 family).
+	 */
+	codec = AC97_CREATE(dev, fm801, fm801_ac97);
+	if (codec == NULL) goto oops;
+
+	if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) {
+		/*
+		 * mixer_init() failed before the mixer cdev was published;
+		 * destroy the codec inline so oops: never frees an object a
+		 * live /dev/mixerN still points at.
+		 */
+		ac97_destroy(codec);
+		codec = NULL;
+		goto oops;
+	}
+
 	ksnprintf(status, 64, "at %s 0x%lx irq %ld %s",
 		(fm801->regtype == SYS_RES_IOPORT)? "io" : "memory",
 		rman_get_start(fm801->reg), rman_get_start(fm801->irq),PCM_KLDSTRING(snd_fm801));
 
-#define FM801_MAXPLAYCH	1
-	if (pcm_register(dev, fm801, FM801_MAXPLAYCH, 1)) goto oops;
 	pcm_addchan(dev, PCMDIR_PLAY, &fm801ch_class, fm801);
 	pcm_addchan(dev, PCMDIR_REC, &fm801ch_class, fm801);
 	pcm_setstatus(dev, status);
@@ -655,9 +671,10 @@
 
 oops:
 	if (codec) ac97_destroy(codec);
-	if (fm801->reg) bus_release_resource(dev, fm801->regtype, fm801->regid, fm801->reg);
+	/* tear the handler down before unmapping its MMIO window */
 	if (fm801->ih) bus_teardown_intr(dev, fm801->irq, fm801->ih);
 	if (fm801->irq) bus_release_resource(dev, SYS_RES_IRQ, fm801->irqid, fm801->irq);
+	if (fm801->reg) bus_release_resource(dev, fm801->regtype, fm801->regid, fm801->reg);
 	if (fm801->parent_dmat) bus_dma_tag_destroy(fm801->parent_dmat);
 	kfree(fm801, M_DEVBUF);
 	return ENXIO;