DF-5465 / icu_legacy_probe.c
/* * DF-5465 evidence: icu_abi_legacy_intr_config() (icu_abi.c:366-397) is * the driver-facing ABI behind bus_config_intr() / * machintr_legacy_intr_config() (sys/sys/machintr.h:103). Its only * 'irq' validation is * KKASSERT(irq >= 0 && irq < IDT_HWI_VECTORS); [icu_abi.c:373] * KKASSERT compiles to nothing on non-INVARIANTS (production) kernels * (sys/sys/systm.h:94-122); stock GENERIC ships without INVARIANTS. * The function returns void -- there is no error path. * * Step 1 (silent OOB write): legacy_intr_config(1000, EDGE, HIGH): * map = &icu_irqmaps[0][1000] [icu_abi.c:374] -- 16000 bytes past * the 192-entry row: still inside the [MAXCPU][192] 2-D array (lands * in cpu5's map row), so nothing faults; icu_abi.c:379 * 'map->im_flags |= ICU_IMF_CONF' (RMW write) and :389 * 'map->im_trig = trig' silently corrupt another CPU's free-MSI map, * then elcr_write_trigger(1000, ...) runs with a wild irq * (elcr.c:70-92, '1 << irq' UB shift, outb to the ELCR port). * * Step 2 (wild index): legacy_intr_config(-100000000, EDGE, HIGH): * &icu_irqmaps[0][-100000000] is ~1.5 GB below the array -- below * KERNBASE, unmapped -> the RMW at icu_abi.c:379 takes a kernel-mode * page fault -> panic. * * On an INVARIANTS kernel step 1 already panics at the KKASSERT * (icu_abi.c:373) -- either way a driver bug is promoted to a kernel * panic; on production kernels it is a silent OOB write primitive at a * driver-chosen 16-byte-strided offset plus, for wild values, a fault. */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/systm.h> #include <sys/machintr.h> /* enum intr_trigger/polarity values from sys/bus.h */ #define INTR_TRIGGER_CONFORM 0 #define INTR_TRIGGER_EDGE 1 #define INTR_TRIGGER_LEVEL 2 #define INTR_POLARITY_CONFORM 0 #define INTR_POLARITY_HIGH 1 #define INTR_POLARITY_LOW 2 extern struct machintr_abi MachIntrABI_ICU; static int icu_legacy_modevent(module_t mod __unused, int type, void *unused __unused) { switch (type) { case MOD_LOAD: kprintf("icu_legacy_probe: step1 legacy_intr_config(1000, " "EDGE, HIGH): silent OOB write at icu_irqmaps[0][1000] " "(+16000 bytes, inside the 2-D array -- no fault)\n"); MachIntrABI_ICU.legacy_intr_config(1000, INTR_TRIGGER_EDGE, INTR_POLARITY_HIGH); kprintf("icu_legacy_probe: step1 RETURNED WITHOUT ERROR " "(void) -- OOB write done, guest still alive\n"); kprintf("icu_legacy_probe: step2 legacy_intr_config" "(-100000000, EDGE, HIGH): wild RMW ~1.5GB below " "icu_irqmaps -> expect kernel page fault panic at " "icu_abi.c:379\n"); MachIntrABI_ICU.legacy_intr_config(-100000000, INTR_TRIGGER_EDGE, INTR_POLARITY_HIGH); kprintf("icu_legacy_probe: step2 RETURNED WITHOUT PANIC " "(unexpected on a production kernel)\n"); return 0; case MOD_UNLOAD: return 0; default: return EOPNOTSUPP; } } static moduledata_t icu_legacy_probe_mod = { "icu_legacy_probe", icu_legacy_modevent, NULL }; DECLARE_MODULE(icu_legacy_probe, icu_legacy_probe_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); MODULE_VERSION(icu_legacy_probe, 1); |