DF-5518 / gpo_race3.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 | /* * gpo_race3.c -- DF-5518 race trigger, fix-validation variant. * * Same defect demonstration as gpo_race2.c, but detach runs on its own * kernel thread and the provider lock is released 500ms later (well past * the ~250ms detach duration), so: * * stock kernel: detach frees softc/device_t/resources and returns; * the delayed LK_RELEASE wakes the blocked in-flight * gpio_acpi_space_handler which then dereferences the * freed objects -> Fatal trap (gpio_cherryview_read_pin). * * fixed kernel: gpio_acpi_detach sets sc->detaching, removes the space * handler and DRAINS in-flight dispatches; the detach * thread sleeps in the drain until this module releases * the provider lock; the blocked handler then completes * against still-valid state and detach returns cleanly * (no panic, guest stays up). */ #include <sys/param.h> #include <sys/module.h> #include <sys/kernel.h> #include <sys/systm.h> #include <sys/malloc.h> #include <sys/bus.h> #include <sys/rman.h> #include <sys/kthread.h> #include <machine/cpufunc.h> #include <vm/vm_page.h> struct gpio_intel_softc_hack { device_t dev; struct resource *mem_res; struct resource *irq_res; void *intrhand; struct lock lk; }; static device_t race_dev; static int race_error; static volatile uint32_t *dbgmark = (volatile uint32_t *)0x1; static void detach_thread(void *arg) { kprintf("gpo_race3: detaching with in-flight handler...\n"); *dbgmark = 0x600D0001; race_error = device_detach(race_dev); *dbgmark = 0x600D0000 + (uint32_t)(race_error & 0xff); kprintf("gpo_race3: detach returned %d\n", race_error); } static int gpo_race3_modevent(module_t mod, int type, void *arg) { devclass_t dc; struct gpio_intel_softc_hack *schk; switch (type) { case MOD_LOAD: dc = devclass_find("gpio_intel"); if (dc == NULL) { kprintf("gpo_race3: no gpio_intel devclass\n"); return (ENXIO); } race_dev = devclass_get_device(dc, 0); if (race_dev == NULL) { kprintf("gpo_race3: no gpio_intel0\n"); return (ENXIO); } schk = device_get_softc(race_dev); dbgmark = (volatile uint32_t *)PHYS_TO_DMAP(0xD6008); *dbgmark = 0; kprintf("gpo_race3: holding gpio_intel0 sc->lk; next GPIO " "dispatch blocks mid-handler\n"); lockmgr(&schk->lk, LK_EXCLUSIVE); /* let the hammer dispatch into the blocked handler */ tsleep(&race_error, 0, "gporace3", 100); kthread_create(detach_thread, NULL, NULL, "gpo_race3"); /* detach thread: _REG(200ms) + teardown + (fixed: drain) */ /* release after detach has passed the free phase */ tsleep(&race_error, 0, "gporace3b", 500); lockmgr(&schk->lk, LK_RELEASE); kprintf("gpo_race3: lock released; any in-flight handler " "has now resumed\n"); break; case MOD_UNLOAD: break; default: return (EOPNOTSUPP); } return (0); } static moduledata_t gpo_race3_mod = { "gpo_race3", gpo_race3_modevent, NULL }; DECLARE_MODULE(gpo_race3, gpo_race3_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); MODULE_VERSION(gpo_race3, 1); |