/*
 * DF-1041 — userspace harness replicating the CISTPL_CFTABLE_ENTRY parser
 * from sys/bus/pccard/pccard_cis.c lines 871-1253.
 *
 * Why a harness: the guest has no PCMCIA bridge, so pccard_scan_cis() is
 * unreachable at runtime. The parser logic is extracted verbatim (line-
 * cited in comments) and fed the crafted CIS image. The harness reports
 * the final idx that the parser reaches — proving the unbounded growth
 * past tuple->length claimed by the finding.
 *
 * Two parser variants are exercised:
 *   - UNPATCHED: identical to pccard_cis.c master — no length check in the
 *     power do-while (lines 995-1003) nor in the misc while (1243-1246).
 *   - PATCHED:   the recommended fix.diff logic — `if (idx >= tuple->length)
 *                goto abort_cfe;` before each unbounded continuation read.
 *
 * bus_space_read_1(memt, memh, mult*byte_off) is emulated as a fetch from
 * the in-memory CIS image. When the offset exceeds the buffer the harness
 * records a "fault" — the userland analogue of the kernel page-fault panic
 * the finding predicts.
 *
 * To avoid an actual infinite loop in the unpatched case, the harness caps
 * iterations at 50000 (> img_len/(2*1) so a real fault is observed).
 *
 * Build:   cc -O2 -Wall -o harness harness.c
 * Run:     ./harness cis_image.bin
 */

#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#define CISTPL_CFTABLE_ENTRY         0x1B
#define PCCARD_TPCE_INDX_INTFACE     0x80
#define PCCARD_TPCE_INDX_DEFAULT     0x40
#define PCCARD_TPCE_INDX_NUM_MASK    0x3F
#define PCCARD_TPCE_IF_MWAIT         0x80
#define PCCARD_TPCE_IF_IFTYPE        0x0F
#define PCCARD_TPCE_FS_MISC          0x80
#define PCCARD_TPCE_FS_MEMSPACE_MASK 0x60
#define PCCARD_TPCE_FS_IRQ           0x10
#define PCCARD_TPCE_FS_IOSPACE       0x08
#define PCCARD_TPCE_FS_TIMING        0x04
#define PCCARD_TPCE_FS_POWER_MASK    0x03
#define PCCARD_TPCE_TD_RESERVED_MASK 0xC0
#define PCCARD_TPCE_TD_RDYBSY_MASK   0x30
#define PCCARD_TPCE_TD_WAIT_MASK     0x0C
#define PCCARD_TPCE_MI_EXT           0x80
#define PCCARD_TPCE_MI_MAXTWINS      0x03
#define PCCARD_CIS_SIZE              4096   /* pccard_cis.c:62 */

#define CAP_ITERS 50000  /* safety cap for the unpatched infinite loop */

struct sim_tuple {
	uint8_t  *img;
	size_t    img_len;
	uint32_t  mult;
	uint32_t  ptr;
	uint32_t  length;
	int       faulted;
	int       read_past_len_at;  /* first idx where byte_off >= img_len */
};

/* Verbatim emulation of pccard_tuple_read_1 (pccardvar.h:255-259).
 * NO length check — exactly as in the kernel. */
static inline uint8_t
tuple_read_1(struct sim_tuple *t, int idx)
{
	size_t byte_off = (size_t)t->mult * (t->ptr + 2 + (uint32_t)idx);
	if (byte_off >= t->img_len) {
		if (!t->faulted) {
			t->faulted = 1;
			t->read_past_len_at = idx;
		}
		return 0xFF;
	}
	return t->img[byte_off];
}

struct cfe_state {
	uint32_t flags;
	uint32_t iftype;
	uint32_t maxtwins;
};

/* Replicates pccard_cis.c:871-1253 (CISTPL_CFTABLE_ENTRY).
 * patched=0 -> master, patched=1 -> fix.diff logic.
 * Returns final idx; iter_count is the total pccard_tuple_read_1 calls. */
static int
parse_cfe(struct sim_tuple *t, struct cfe_state *cfe, int patched, int *iter_count)
{
	int idx = 0, i, j;
	uint32_t reg, reg2;
	uint32_t intface, def, num;
	uint32_t power, timing, iospace, irq, memspace, misc;
	*iter_count = 0;

	/* pccard_cis.c:881 */
	reg = tuple_read_1(t, idx); idx++; (*iter_count)++;
	intface = reg & PCCARD_TPCE_INDX_INTFACE;
	def     = reg & PCCARD_TPCE_INDX_DEFAULT;
	num     = reg & PCCARD_TPCE_INDX_NUM_MASK;
	(void)def; (void)num;

	if (intface) {
		/* pccard_cis.c:958 */
		reg = tuple_read_1(t, idx); idx++; (*iter_count)++;
		cfe->iftype = reg & PCCARD_TPCE_IF_IFTYPE;
	}

	/* pccard_cis.c:974 */
	reg = tuple_read_1(t, idx); idx++; (*iter_count)++;
	power    = reg & PCCARD_TPCE_FS_POWER_MASK;
	timing   = reg & PCCARD_TPCE_FS_TIMING;
	iospace  = reg & PCCARD_TPCE_FS_IOSPACE;
	irq      = reg & PCCARD_TPCE_FS_IRQ;
	memspace = reg & PCCARD_TPCE_FS_MEMSPACE_MASK;
	misc     = reg & PCCARD_TPCE_FS_MISC;
	(void)iospace; (void)irq; (void)memspace;

	/* === POWER LOOP — pccard_cis.c:984-1007 (bug locus 1) === */
	if (power) {
		for (i = 0; i < (int)power; i++) {
			/* pccard_cis.c:988 */
			reg = tuple_read_1(t, idx); idx++; (*iter_count)++;
			for (j = 0; j < 7; j++) {
				if ((reg >> j) & 0x01) {
					/* pccard_cis.c:995-1003 — do-while on (reg2 & 0x80) */
					if (patched) {
						if (idx >= (int)t->length)
							return idx;  /* goto abort_cfe */
					}
					do {
						reg2 = tuple_read_1(t, idx); idx++; (*iter_count)++;
						if (patched) {
							if (idx >= (int)t->length && (reg2 & PCCARD_TPCE_MI_EXT))
								return idx;  /* goto abort_cfe */
						}
						if (*iter_count > CAP_ITERS)
							return idx;  /* harness safety cap */
					} while (reg2 & 0x80);
				}
			}
		}
	}

	if (timing) {
		/* pccard_cis.c:1010-1021 — bounded */
		reg = tuple_read_1(t, idx); idx++; (*iter_count)++;
		if ((reg & PCCARD_TPCE_TD_RESERVED_MASK) != PCCARD_TPCE_TD_RESERVED_MASK) { idx++; }
		if ((reg & PCCARD_TPCE_TD_RDYBSY_MASK)   != PCCARD_TPCE_TD_RDYBSY_MASK)   { idx++; }
		if ((reg & PCCARD_TPCE_TD_WAIT_MASK)     != PCCARD_TPCE_TD_WAIT_MASK)     { idx++; }
	}
	/* iospace/irq/memspace each have entry guards at pccard_cis.c:1024,1102,1134 —
         * not the bug, omitted from the harness bodies. */

	/* === MISC LOOP — pccard_cis.c:1224-1247 (bug locus 2) === */
	if (misc) {
		if ((int)t->length <= idx)
			return idx;  /* entry guard at pccard_cis.c:1225 */
		reg = tuple_read_1(t, idx); idx++; (*iter_count)++;
		cfe->maxtwins = reg & PCCARD_TPCE_MI_MAXTWINS;

		/* pccard_cis.c:1243-1246 — while on (reg & PCCARD_TPCE_MI_EXT), NO length check */
		while (reg & PCCARD_TPCE_MI_EXT) {
			if (patched) {
				if (idx >= (int)t->length)
					return idx;  /* goto abort_cfe */
			}
			reg = tuple_read_1(t, idx); idx++; (*iter_count)++;
			if (*iter_count > CAP_ITERS)
				return idx;
		}
	}
	return idx;
}

int
main(int argc, char **argv)
{
	if (argc != 2)
		errx(2, "usage: %s <cis_image.bin>", argv[0]);

	FILE *f = fopen(argv[1], "rb");
	if (!f) err(2, "open %s", argv[1]);
	fseek(f, 0, SEEK_END);
	long fsz = ftell(f);
	fseek(f, 0, SEEK_SET);
	if (fsz < 16) errx(2, "image too small (%ld bytes)", fsz);
	uint8_t *img = malloc(fsz);
	if (!img) err(2, "malloc");
	if (fread(img, 1, fsz, f) != (size_t)fsz) err(2, "read");
	fclose(f);

	/* pccard_scan_cis reads tuple.code at mult*ptr (pccard_cis.c:170) and
	 * tuple.length at mult*(ptr+1) (pccard_cis.c:198). For the supplied
	 * cis_image.bin the CFTABLE_ENTRY tuple starts at attribute-memory
	 * offset 0 with mult=2 (pccard_cis.c:145). */
	uint32_t mult = 2;
	uint32_t ptr  = 0;
	uint32_t code   = img[mult * ptr];
	uint32_t length = img[mult * (ptr + 1)];

	printf("DF-1041 harness: CISTPL_CFTABLE_ENTRY parser replication\n");
	printf("image=%s size=%ld bytes (kernel PCCARD_CIS_SIZE=%d)\n",
	       argv[1], fsz, PCCARD_CIS_SIZE);
	printf("first tuple @ attr-off %u : code=0x%02X length=%u\n",
	       ptr, code, length);
	if (code != CISTPL_CFTABLE_ENTRY)
		errx(2, "first tuple is not CFTABLE_ENTRY (0x%02X)", code);

	struct sim_tuple t = { img, (size_t)fsz, mult, ptr, length, 0, -1 };

	/* --- UNPATCHED run (master) --- */
	struct cfe_state cfe_u = {0};
	t.faulted = 0; t.read_past_len_at = -1;
	int it_u = 0;
	int idx_u = parse_cfe(&t, &cfe_u, /*patched=*/0, &it_u);
	printf("\n=== UNPATCHED (master pccard_cis.c) ===\n");
	printf("declared tuple->length   = %u bytes\n", t.length);
	printf("pccard_tuple_read_1 calls= %d (harness cap=%d)\n", it_u, CAP_ITERS);
	printf("final idx reached        = %d bytes\n", idx_u);
	printf("overshoot past length    = %d bytes (idx grew %dx past declared length)\n",
	       idx_u - (int)t.length,
	       t.length ? idx_u / (int)t.length : 0);
	printf("kernel byte offset       = mult*(ptr+2+idx) = %u*(%u+2+%d) = %u\n",
	       t.mult, t.ptr, idx_u, t.mult * (t.ptr + 2 + idx_u));
	if (t.faulted) {
		printf("faulted past img_len?    = YES\n");
		printf("  -> first OOB read at idx=%d, byte_off=%u (>= img_len=%ld)\n",
		       t.read_past_len_at,
		       t.mult * (t.ptr + 2 + t.read_past_len_at), fsz);
	} else {
		printf("faulted past img_len?    = no\n");
	}
	printf("VERDICT: UNPATCHED parser runs the power do-while (pccard_cis.c:995-1003)\n");
	printf("         and/or misc while (pccard_cis.c:1243-1246) without ANY\n");
	printf("         idx<length guard. With this CIS image idx runs %d bytes\n", idx_u);
	printf("         past the 8-byte declared body before the harness cap (%d)\n", CAP_ITERS);
	printf("         or the image end (=> kernel page-fault panic on real HW).\n");

	/* --- PATCHED run (fix.diff applied) --- */
	struct cfe_state cfe_p = {0};
	t.faulted = 0; t.read_past_len_at = -1;
	int it_p = 0;
	int idx_p = parse_cfe(&t, &cfe_p, /*patched=*/1, &it_p);
	printf("\n=== PATCHED (fix.diff: idx>=length -> abort_cfe) ===\n");
	printf("final idx reached        = %d bytes (capped at length=%u)\n",
	       idx_p, t.length);
	printf("pccard_tuple_read_1 calls= %d\n", it_p);
	printf("overshoot past length    = %d bytes\n", idx_p - (int)t.length);
	printf("faulted?                 = %s\n", t.faulted ? "YES" : "no");
	printf("VERDICT: PATCHED parser aborts via abort_cfe at pccard_cis.c:1251\n");
	printf("         the moment idx reaches tuple->length — no unbounded read.\n");

	printf("\nSUMMARY: unpatched idx=%d (>%d past length) vs patched idx=%d (==length).\n",
	       idx_u, idx_u - (int)t.length, idx_p);
	free(img);
	return 0;
}
