⬢ DragonFlyBSD Kernel Audit
DF-4551 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/sound/pci/als4000.c
+++ b/sys/dev/sound/pci/als4000.c
@@ -780,11 +780,6 @@ als_resource_grab(device_t dev, struct sc_info *sc)
 		goto bad;
 	}
 
-	if (snd_setup_intr(dev, sc->irq, INTR_MPSAFE, als_intr,
-			   sc, &sc->ih)) {
-		device_printf(dev, "unable to setup interrupt\n");
-		goto bad;
-	}
-
 	sc->bufsz = pcm_getbuffersize(dev, 4096, ALS_DEFAULT_BUFSZ, 65536);
 
 	if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev),
@@ -838,6 +833,15 @@ als_pci_attach(device_t dev)
 	pcm_addchan(dev, PCMDIR_PLAY, &alspchan_class, sc);
 	pcm_addchan(dev, PCMDIR_REC,  &alsrchan_class, sc);
 
+	/* Hook the interrupt only after the channels it dispatches to exist
+	 * (pcm_addchan -> alschan_init sets ch->channel).  Registering it
+	 * earlier leaves a window where als_intr() runs chn_intr(NULL) and
+	 * panics on any shared-line IRQ. */
+	if (snd_setup_intr(dev, sc->irq, INTR_MPSAFE, als_intr,
+			   sc, &sc->ih)) {
+		device_printf(dev, "unable to setup interrupt\n");
+		goto bad_attach;
+	}
+
 	ksnprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s",
 		 rman_get_start(sc->reg), rman_get_start(sc->irq),PCM_KLDSTRING(snd_als4000));
 	pcm_setstatus(dev, status);
@@ -850,6 +854,14 @@ als_resource_free(device_t dev, struct sc_info *sc)
 {
+	/* Tear the interrupt down BEFORE releasing the I/O region: a racing
+	 * handler must not bus_space_read/write a released mapping. */
+	if (sc->ih) {
+		bus_teardown_intr(dev, sc->irq, sc->ih);
+		sc->ih = 0;
+	}
 	if (sc->reg) {
 		bus_release_resource(dev, SYS_RES_IOPORT, sc->regid, sc->reg);
 		sc->reg = 0;
 	}
-	if (sc->ih) {
-		bus_teardown_intr(dev, sc->irq, sc->ih);
-		sc->ih = 0;
-	}
 	if (sc->irq) {
 		bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq);
 		sc->irq = 0;