⬢ DragonFlyBSD Kernel Audit
DF-4743 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/sound/pci/via82c686.c
+++ b/sys/dev/sound/pci/via82c686.c
@@ -560,7 +560,7 @@ via_attach(device_t dev)
 		/*maxsize*/via->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff,
 		/*flags*/0,
 		&via->parent_dmat) != 0) {
 		device_printf(dev, "unable to create dma tag\n");
-		goto bad;
+		goto bad_mixer;
 	}
 
 	/*
@@ -576,7 +576,7 @@ via_attach(device_t dev)
 		/*maxsize*/NSEGS * sizeof(struct via_dma_op),
 		/*nsegments*/1, /*maxsegz*/0x3ffff,
 		/*flags*/0,
 		&via->sgd_dmat) != 0) {
 		device_printf(dev, "unable to create dma tag\n");
-		goto bad;
+		goto bad_mixer;
 	}
 
 	if (bus_dmamem_alloc(via->sgd_dmat, (void **)&via->sgd_table,
@@ -584,10 +584,10 @@ via_attach(device_t dev)
 	    BUS_DMA_NOWAIT, &via->sgd_dmamap) != 0)
-		goto bad;
+		goto bad_mixer;
 	if (bus_dmamap_load(via->sgd_dmat, via->sgd_dmamap, via->sgd_table,
 	    NSEGS * sizeof(struct via_dma_op), dma_cb, via, 0) != 0)
-		goto bad;
+		goto bad_mixer;
 
 	ksnprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s",
 		 rman_get_start(via->reg), rman_get_start(via->irq),
 		 PCM_KLDSTRING(snd_via82c686));
 
 	/* Register */
-	if (pcm_register(dev, via, 1, 1)) goto bad;
+	if (pcm_register(dev, via, 1, 1)) goto bad_mixer;
 	pcm_addchan(dev, PCMDIR_PLAY, &viachan_class, via);
 	pcm_addchan(dev, PCMDIR_REC, &viachan_class, via);
 	pcm_setstatus(dev, status);
 	return 0;
+bad_mixer:
+	/*
+	 * mixer_init() has published a world-writable /dev/mixerN whose
+	 * mixer holds via->codec (and through it `via`) as devinfo. Retire
+	 * it before freeing anything, or the cdev dangles into freed memory
+	 * (DF-4743, DF-4156 mixer-UAF family). mixer_uninit() runs
+	 * ac97mix_uninit() -> ac97_destroy() on the codec.
+	 */
+	mixer_uninit(dev);
+	via->codec = NULL;	/* freed by ac97mix_uninit() above */
 bad:
 	if (via->codec) ac97_destroy(via->codec);
 	if (via->reg) bus_release_resource(dev, SYS_RES_IOPORT, via->regid, via->reg);