DF-3866 / fix.diff
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); } |