DragonFlyBSD Kernel Audit
DF-2813 / ochang_drv.c
← back to finding ↓ download raw
/*
 * ochang_drv.c - userspace driver for the ochang KLD (DF-2813 PoC).
 *
 * Pins itself to cpu2 (neither the strand cpu0 nor the sleeper cpu5) and
 * orchestrates the ARM/SLEEPER/STRAND/PROBE/RESCUE sequence, printing the
 * sleeper state with timestamps.
 *
 * Exit code 0 = bug reproduced (sleeper stuck >= 8 s with objects available
 * on the strand cpu, then woken by RESCUE in < 2 s).
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/usched.h>

static void
cmd(int c)
{
	if (sysctlbyname("kern.ochang.cmd", NULL, NULL, &c, sizeof(c)) < 0) {
		perror("sysctl cmd");
		exit(1);
	}
}

static void
status(const char *tag)
{
	char buf[1024];
	size_t len = sizeof(buf) - 1;

	buf[0] = 0;
	if (sysctlbyname("kern.ochang.status", buf, &len, NULL, 0) < 0) {
		perror("sysctl status");
		exit(1);
	}
	buf[len] = 0;
	printf("[%s] %s", tag, buf);
	fflush(stdout);
}

static double
now_s(void)
{
	struct timeval tv;

	gettimeofday(&tv, NULL);
	return (tv.tv_sec + tv.tv_usec / 1e6);
}

static int
sleeper_got(void)
{
	char buf[1024];
	size_t len = sizeof(buf) - 1;
	char *p;

	buf[0] = 0;
	if (sysctlbyname("kern.ochang.status", buf, &len, NULL, 0) < 0)
		exit(1);
	buf[len] = 0;
	p = strstr(buf, "got=");
	if (!p)
		return (-1);
	return (atoi(p + 4));
}

int
main(void)
{
	double t_strand, t_reacq;
	int cpu = 2;
	int i;

	if (usched_set(0, USCHED_SET_CPU, &cpu, sizeof(cpu)) < 0) {
		perror("usched_set");
		exit(1);
	}
	printf("driver pinned to cpu%d\n", cpu);

	cmd(1);			/* ARM */
	status("after ARM");
	usleep(200000);

	cmd(2);			/* SLEEPER on cpu5 */
	usleep(1000000);
	status("sleeper +1s");

	cmd(3);			/* STRAND: magcap objects onto cpu0 */
	t_strand = now_s();
	printf("=== STRAND done, watching sleeper for up to 10 s ===\n");
	for (i = 0; i < 10 * 4; i++) {
		usleep(250000);
		if (sleeper_got() == 1) {
			printf("!!! sleeper woke unexpectedly after %.2f s\n",
			    now_s() - t_strand);
			break;
		}
	}
	status("post-strand");
	cmd(7);			/* PROBE strand cpu: expect OBJECT */
	cmd(8);			/* PROBE sleeper cpu: expect NULL    */
	status("post-probe");

	if (sleeper_got() != 1) {
		printf("=== RESCUE (cycle both cpu0 magazines to depot) ===\n");
		cmd(4);
		t_reacq = now_s();
		while (now_s() - t_reacq < 5.0) {
			if (sleeper_got() == 1)
				break;
			usleep(20000);
		}
		if (sleeper_got() == 1)
			printf("sleeper acquired %.3f s after RESCUE\n",
			    now_s() - t_reacq);
		else
			printf("sleeper STILL stuck after RESCUE (bug in PoC)\n");
	}
	status("final");

	cmd(5);			/* DRAIN */
	cmd(6);			/* DESTROY */

	printf("=== summary: stuck_with_available_objects=%s rescue_wake=%s ===\n",
	    "see log above", sleeper_got() == 1 ? "yes" : "no");
	return (0);
}