DF-5341 / pap_trust.c
/* * DF-5341 โ mptable_map() trusts firmware table pointers with no * signature / checksum / physical-range validation (mptable.c:501-539). * * The floating-pointer scan matches ONLY the 4-byte "_MP_" signature * (mptable.c:375). mptable_map() then: * - maps 16 bytes at the hit address and reads fps->pap, checking * ONLY pap != 0 (mptable.c:511-512), * - maps 44 bytes at that arbitrary 32-bit PA and adopts * base_table_length from it (mptable.c:517-518), * - re-maps base_table_length (up to 65535) bytes there and the * whole table is parsed (mptable.c:531). * * NEVER checked, anywhere in the file: * - MPFPS checksum byte (fps->checksum, struct mptable.c:93) * - MPFPS spec_rev / length sanity * - MPCTH "PCMP" signature (struct mptable.c:103) โ grep: never read * - MPCTH checksum (mptable.c:106) โ grep: never read * - that pap / pap+base_table_length lie in mapped RAM * * pmap_mapdev_attr() (pmap.c:6192-6218) installs PTEs for ANY physical * address with no bounds check, so every one of these firmware values * becomes a kernel read of an arbitrary physical address at early boot, * and the parsed garbage drives CPU/IOAPIC enumeration. * * This harness is a decision-table reproducer: it enumerates pap values * the current code accepts and where mapdev lands for them. */ #include <stdio.h> #include <stdint.h> #include <string.h> typedef uint32_t u_int32_t; typedef uint16_t u_short; /* the ONLY pap validation in mptable_map (mptable.c:512) */ static int pap_accepted(u_int32_t pap) { return pap != 0; } /* pmap_mapdev_attr (pmap.c:6192) accepts any pa โ modeled, plus the * span computation the caller relies on */ static void show_map(const char *what, u_int32_t pap, u_short base_table_length) { uint64_t lo = pap, hi = (uint64_t)pap + base_table_length; printf(" %-28s pap=0x%08x len=%-6u map span [0x%09llx..0x%09llx)%s\n", what, pap, base_table_length, (unsigned long long)lo, (unsigned long long)hi, (hi > 0x100000000ULL) ? " ** CROSSES THE 4GB WRAP **" : ""); } int main(void) { struct { u_int32_t pap; const char *what; } cases[] = { { 0x0009FC00, "sane (EBDA table)" }, { 0x000F0000, "BIOS ROM area" }, { 0xFEBC0000, "MMIO hole" }, { 0xA0000000, "PCI MMIO" }, { 0xFFFFFF00, "top of 4GB - 0x100" }, { 0xFFFFFFFF, "last byte of 4G" }, { 0xDEADBEEF, "total garbage" }, }; unsigned i; printf("pap values accepted by mptable_map() as-is (only check: pap != 0, " "mptable.c:512):\n"); for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { printf(" pap=0x%08x (%-20s) -> %s\n", cases[i].pap, cases[i].what, pap_accepted(cases[i].pap) ? "ACCEPTED, mapped + parsed as MP config table" : "rejected"); } printf("\nresulting pmap_mapdev spans (pmap.c:6192 accepts ANY pa):\n"); show_map("sane", 0x0009FC00, 0x2A); show_map("garbage pap", 0xDEADBEEF, 0xFFFF); show_map("4GB-crossing", 0xFFFFFF00, 0x1000); printf("\nvalidation the file NEVER performs (audited by grep of " "sys/platform/pc64/x86_64/mptable.c):\n"); printf(" MPFPS checksum byte: 0 reads of fps->checksum\n"); printf(" MPCTH 'PCMP' signature: 0 reads of cth->signature\n"); printf(" MPCTH checksum: 0 reads of cth->checksum\n"); printf(" pap/pap+len in RAM: no range check exists\n"); return 0; } |