⬢ DragonFlyBSD Kernel Audit
DF-5194 / snake_probe.c
← back to finding ↓ download raw
/*
 * snake_probe.c -- DF-5194 guest harness (run as root)
 *
 * 1. positive control: force the snake saver to run on /dev/ttyv0 and
 *    observe its writes in VGA text memory (phys 0xB8000) via /dev/mem;
 *    two samples 1s apart must differ (the snake moved).
 * 2. guard check: while the snake is blanked, issue CONS_SET (mode change).
 *    sc_set_text_mode -> sc_clean_up -> wait_scrn_saver_stop must unblank
 *    the saver first (snake_saver(adp,0) resets `blanked`), so the ioctl
 *    must return 0 after a brief wait and the console must be unblanked;
 *    the next blank cycle must reinitialize the snake (fresh single spot).
 * 3. degenerate geometry attempt: there is no ioctl that can install a
 *    1-row text screen (only CONS_SET with a mode number; kernel clamps
 *    to full mode dims) -- demonstrated by trying bogus values.
 */
#include <sys/ioctl.h>
#include <sys/consio.h>
#include <sys/fbio.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#define VRAM_PHYS 0xB8000ULL
#define VRAM_LEN  8192		/* text plane window we sample */

static int read_vram(unsigned char *buf)
{
	int fd = open("/dev/mem", O_RDONLY);
	ssize_t n;
	if (fd < 0) { perror("open /dev/mem"); return -1; }
	n = pread(fd, buf, VRAM_LEN, (off_t)VRAM_PHYS);
	close(fd);
	if (n != VRAM_LEN) { perror("pread vram"); return -1; }
	return 0;
}

/* count cells carrying the snake attribute (0x07) with a letter char */
static int snake_cells(const unsigned char *b)
{
	int i, c = 0;
	for (i = 0; i < VRAM_LEN; i += 2)
		if (b[i+1] == 0x07 && b[i] != 0x20 && b[i] != 0)
			c++;
	return c;
}

static double now_sec(void)
{
	struct timespec ts;
	clock_gettime(CLOCK_MONOTONIC, &ts);
	return ts.tv_sec + ts.tv_nsec / 1e9;
}

int main(void)
{
	unsigned char a[VRAM_LEN], b[VRAM_LEN];
	int fd, v = 1, t0, t1;
	double t_start, t_end;

	fd = open("/dev/ttyv0", O_WRONLY);
	if (fd < 0) { perror("open /dev/ttyv0"); return 1; }

	/* 1s blank timeout + force saver start */
	if (ioctl(fd, CONS_BLANKTIME, &v)) { perror("CONS_BLANKTIME"); return 1; }
	if (ioctl(fd, CONS_SAVERSTART, &v)) { perror("CONS_SAVERSTART"); return 1; }

	printf("[*] waiting 3s for snake to draw...\n");
	sleep(3);
	if (read_vram(a)) return 1;
	printf("[*] sample1: %d non-blank attr-0x07 cells\n", snake_cells(a));

	sleep(1);
	if (read_vram(b)) return 1;
	printf("[*] sample2: %d non-blank attr-0x07 cells\n", snake_cells(b));
	if (memcmp(a, b, VRAM_LEN) != 0)
		printf("[+] POSITIVE CONTROL: VRAM changed between samples -- "
		       "snake_saver is executing and moving\n");
	else
		printf("[-] VRAM static -- snake not running?\n");

	/* --- guard check: mode change while blanked --- */
	printf("[*] issuing CONS_SET(M_VGA_C80x30) while blanked...\n");
	t0 = 32;	/* M_VGA_C80x30 */
	t_start = now_sec();
	errno = 0;
	v = ioctl(fd, CONS_SET, &t0);
	t_end = now_sec();
	printf("[*] CONS_SET -> rc=%d errno=%d after %.3fs\n",
	    v, errno, t_end - t_start);
	if (v == 0)
		printf("[+] GUARD: mode change completed cleanly "
		       "(saver was stopped/restarted by sc_clean_up)\n");

	/* verify console unblanked and sane, then restore 80x25 */
	t1 = 24;	/* M_VGA_C80x25 */
	v = ioctl(fd, CONS_SET, &t1);
	printf("[*] restore CONS_SET(80x25) -> rc=%d\n", v);

	/* force blank again: snake must reinitialize (fresh draw) */
	v = 1; ioctl(fd, CONS_SAVERSTART, &v);
	sleep(2);
	if (read_vram(b)) return 1;
	printf("[*] post-reblank sample: %d attr-0x07 cells "
	       "(fresh snake re-init expected)\n", snake_cells(b));

	/* stop saver, restore no-blank */
	v = 0;
	ioctl(fd, CONS_SAVERSTART, &v);
	ioctl(fd, CONS_BLANKTIME, &v);
	printf("[*] cleanup done; guest still alive: ");
	fflush(stdout);
	printf("yes\n");
	return 0;
}