DF-5518 / gpo_race2.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 | /* * gpo_race2.c -- DF-5518 deterministic race trigger. * * The lottery variant (gpo_detach.c) races the ~us window between * AcpiRemoveAddressSpaceHandler returning and the (already in-flight) * gpio_acpi_space_handler completing its 59-pin read loop. This variant * pins the handler in-flight deterministically the way real contention * would (a slow provider, a preempted CPU between pins): * * 1. hold gpio_intel0's sc->lk exclusively from this kmod; * 2. the TZ0._TMP hammer's next GeneralPurposeIo field access * dispatches into gpio_acpi_space_handler and blocks inside * gpio_intel_alloc_io_pin's lockmgr(&sc->lk) -- an in-flight, * mid-dispatch space handler; * 3. device_detach(gpio_intel0) now runs without any quiesce: * - gpio_acpi0 detaches: AcpiRemoveAddressSpaceHandler frees the * HandlerObj (and the ContextMutex the blocked handler's dispatch * path still uses); * - gpio_intel0 teardown: bus_teardown_intr / release resources / * lockuninit(&sc->lk) -> KKASSERT on the waiter (panic), or * - device_set_driver(NULL) (subr_bus.c:2150) kfrees BOTH softcs * and kobj_delete kfrees the device_t -- all while the blocked * handler still holds dev/sc pointers; * 4. this kmod releases the lock: the blocked handler resumes INSIDE * lockmgr on freed memory and dereferences the freed softc. */ #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/systm.h> #include <machine/cpufunc.h> static int gpo_race2_modevent(module_t mod, int type, void *arg) { devclass_t dc; device_t dev; struct gpio_intel_softc_hack { device_t dev; struct resource *mem_res; struct resource *irq_res; void *intrhand; struct lock lk; }; struct gpio_intel_softc_hack *schk; void *p[256]; int error, i, j, n; static const int sizes[] = { 64, 128, 256, 512, 1024, 2048 }; switch (type) { case MOD_LOAD: dc = devclass_find("gpio_intel"); if (dc == NULL) { kprintf("gpo_race2: no gpio_intel devclass\n"); return (ENXIO); } dev = devclass_get_device(dc, 0); if (dev == NULL) { kprintf("gpo_race2: no gpio_intel0\n"); return (ENXIO); } schk = device_get_softc(dev); kprintf("gpo_race2: holding gpio_intel0 sc->lk; next GPIO " "dispatch will block mid-handler\n"); lockmgr(&schk->lk, LK_EXCLUSIVE); /* let the hammer dispatch into the blocked handler */ tsleep((const void *)&n, 0, "gporace", 200); kprintf("gpo_race2: detaching with in-flight handler...\n"); error = device_detach(dev); kprintf("gpo_race2: detach returned %d; freeing lock\n", error); lockmgr(&schk->lk, LK_RELEASE); /* poison whatever was freed */ n = 0; for (i = 0; i < (int)NELEM(sizes); i++) { for (j = 0; j < 32 && n < 256; j++, n++) { p[n] = kmalloc(sizes[i], M_DEVBUF, M_WAITOK); if (p[n] != NULL) memset(p[n], 0xDE, sizes[i]); } } kprintf("gpo_race2: done (%d poison allocs)\n", n); break; case MOD_UNLOAD: break; default: return (EOPNOTSUPP); } return (0); } static moduledata_t gpo_race2_mod = { "gpo_race2", gpo_race2_modevent, NULL }; DECLARE_MODULE(gpo_race2, gpo_race2_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); MODULE_VERSION(gpo_race2, 1); |