DF-2679 / dfrace.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 | /* * DF-2679 / DF-2680 PoC (kernel side churn): adds N children to root_bus * driven by a real driver so they attach (devadded -> devctl events -> * ksignal(async_proc)) and detach (devremoved) like ordinary devices. * On MOD_UNLOAD the driver is deleted and the children are deleted * (device_delete_child -> kobj_delete -> kfree). * * DF-2679: the deletes race unprivileged sysctl_devices() walkers. * DF-2680: each attach/detach fires devctl_queue_data() -> ksignal(). */ #include <sys/param.h> #include <sys/module.h> #include <sys/kernel.h> #include <sys/systm.h> #include <sys/bus.h> #include <sys/bus_private.h> #include <sys/malloc.h> /* root_bus is declared in <sys/bus.h>; root_devclass is a global kernel * symbol without a public prototype */ extern devclass_t root_devclass; #define NDEV 512 MALLOC_DEFINE(M_DFRACE, "dfrace", "DF-2679 poison chunks"); static device_t dfrace_devs[NDEV]; static void *dfrace_junk[NDEV]; static int dfrace_probe(device_t dev); static int dfrace_attach(device_t dev); static int dfrace_detach(device_t dev); static device_method_t dfrace_methods[] = { DEVMETHOD(device_probe, dfrace_probe), DEVMETHOD(device_attach, dfrace_attach), DEVMETHOD(device_detach, dfrace_detach), DEVMETHOD_END }; static driver_t dfrace_driver = { "dfrace", /* must equal the child devclass name */ dfrace_methods, 1, /* no softc */ }; static int dfrace_probe(device_t dev) { return (0); } static int dfrace_attach(device_t dev) { return (0); } static int dfrace_detach(device_t dev) { return (0); } static int dfrace_modev(module_t mod, int what, void *arg) { int i, n; switch (what) { case MOD_LOAD: for (i = 0; i < NDEV; i++) { if (dfrace_junk[i]) { kfree(dfrace_junk[i], M_DFRACE); dfrace_junk[i] = NULL; } } n = 0; for (i = 0; i < NDEV; i++) { dfrace_devs[i] = device_add_child(root_bus, "dfrace", i); if (dfrace_devs[i]) n++; } /* register driver on the root bus devclass -> probe+attach */ devclass_add_driver(root_devclass, &dfrace_driver); kprintf("dfrace: added+attached %d children to root_bus\n", n); return (0); case MOD_UNLOAD: devclass_delete_driver(root_devclass, &dfrace_driver); n = 0; for (i = 0; i < NDEV; i++) { if (dfrace_devs[i]) { if (device_delete_child(root_bus, dfrace_devs[i]) == 0) { dfrace_devs[i] = NULL; n++; } } /* * Back-fill the just-freed chunk with a poisoned * same-bucket allocation (models any racing kernel * allocation; makes the UAF fault loudly). */ dfrace_junk[i] = kmalloc(sizeof(struct bsd_device), M_DFRACE, M_WAITOK | M_ZERO); if (dfrace_junk[i]) memset(dfrace_junk[i], 0xAA, sizeof(struct bsd_device)); } kprintf("dfrace: detached+deleted %d children\n", n); for (i = 0; i < NDEV; i++) { if (dfrace_junk[i]) { kfree(dfrace_junk[i], M_DFRACE); dfrace_junk[i] = NULL; } } return (0); default: return (0); } } static moduledata_t dfrace_mod = { "dfrace", dfrace_modev, NULL }; DECLARE_MODULE(dfrace, dfrace_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); MODULE_VERSION(dfrace, 1); |