⬢ DragonFlyBSD Kernel Audit
DF-3866 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/sound/pci/emu10kx.c b/sys/dev/sound/pci/emu10kx.c
--- a/sys/dev/sound/pci/emu10kx.c
+++ b/sys/dev/sound/pci/emu10kx.c
@@ -945,6 +945,11 @@ emu_intr_register(struct emu_sc_info *sc, uint32_t inte_mask, uint32_t intr_mask
 int
 emu_intr_unregister(struct emu_sc_info *sc, int hnumber)
 {
 	uint32_t x;
 	int i;
 
+	/* hnumber is an ihandler[] slot index.  emu_intr_register() is
+	 * documented (by use) to return -1 on exhaustion and callers in
+	 * emu10kx-pcm.c store that value unchecked in ch->ihandle. */
+	if (hnumber < 0 || hnumber >= EMU_MAX_IRQ_CONSUMERS)
+		return (-1);
+
 	lockmgr(&sc->lock, LK_EXCLUSIVE);
 
 	if (sc->ihandler[hnumber].inte_mask == 0) {
diff --git a/sys/dev/sound/pci/emu10kx-pcm.c b/sys/dev/sound/pci/emu10kx-pcm.c
--- a/sys/dev/sound/pci/emu10kx-pcm.c
+++ b/sys/dev/sound/pci/emu10kx-pcm.c
@@ -980,6 +980,12 @@ emurchan_trigger(kobj_t obj __unused, void *c_devinfo, int go)
 		emu_wrptr(sc->card, 0, ch->setupreg, val);
 		ch->ihandle = emu_intr_register(sc->card, ch->irqmask, ch->iprmask, &emu_pcm_intr, sc);
+		if (ch->ihandle < 0) {
+			/* no free interrupt slot: fail the start cleanly */
+			ch->run = 0;
+			ch->ihandle = 0;
+			emu_wrptr(sc->card, 0, ch->sizereg, 0);
+			snd_mtxunlock(sc->lock);
+			return (ENXIO);
+		}
 		break;
 	case PCMTRIG_STOP:
 		/* FALLTHROUGH */
@@ -1134,6 +1140,12 @@ emufxrchan_trigger(kobj_t obj __unused, void *c_devinfo, int go)
 	case PCMTRIG_START:
 		ch->run = 1;
 		emu_wrptr(sc->card, 0, ch->sizereg, sz);
 		ch->ihandle = emu_intr_register(sc->card, ch->irqmask, ch->iprmask, &emu_pcm_intr, sc);
+		if (ch->ihandle < 0) {
+			ch->run = 0;
+			ch->ihandle = 0;
+			emu_wrptr(sc->card, 0, ch->sizereg, 0);
+			snd_mtxunlock(sc->lock);
+			return (ENXIO);
+		}
 		/*
 		 * SB Live! is limited to 32 mono channels.  Audigy
 		 * has 64 mono channels.  Channels are enabled
@@ -1290,7 +1302,11 @@ emu_pcm_init(struct emu_pcm_info *sc)
 static int
 emu_pcm_uninit(struct emu_pcm_info *sc)
 {
+	/* release the card-level interrupt handler registered in
+	 * emu_pcm_attach(); leaking it exhausts the 32-slot pool.
+	 * (attach sets sc->ihandle = -1 before the registration) */
+	if (sc->ihandle >= 0)
+		emu_intr_unregister(sc->card, sc->ihandle);
 	return (0);
 }