DF-5688 / df5688_mod.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 | /* * DF-5688 — in-kernel proof module. Loads with kldload(8), runs the * 4-op duplicate-id sequence (Part B of df5688_minimal.h) against the * REAL kernel idr implementation (sys/libkern/linux_idr.c, compiled * into X86_64_GENERIC), prints the outcome to the console/dmesg, and * unloads cleanly (no crash — the sequence never drives a counter * negative). * * Sequence (identical to the userland rig's minimal_dup): * 1. idr_alloc(&idr, A, 526, 0, 0) -> 526 (bumps nodes[525]) * 2. idr_remove(&idr, 525) -> NULL (525 NEVER allocated; * the `allocated != 0' guard passes because nodes[525] is a * cumulative counter -> PHANTOM DECREMENT) * 3. idr_alloc(&idr, B, 525, 532, 0) -> 525 * 4. idr_alloc(&idr, C, 525, 0, 0) -> 525 AGAIN (still live!) * * Build: make -m /usr/src/share/mk KERNCONF=X86_64_GENERIC */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/systm.h> #include <sys/module.h> #include <sys/idr.h> static void df5688_show(struct idr *idp, const char *tag) { kprintf("df5688[%s]: count=%d nodes[525].allocated=%d " "nodes[524].allocated=%d nodes[511].allocated=%d\n", tag, idp->idr_count, (525 < idp->idr_count) ? idp->idr_nodes[525].allocated : -999, (524 < idp->idr_count) ? idp->idr_nodes[524].allocated : -999, (511 < idp->idr_count) ? idp->idr_nodes[511].allocated : -999); } static void df5688_run(void) { struct idr idr; void *a = (void *)0xdead0001UL, *b = (void *)0xdead0002UL; void *c = (void *)0xdead0003UL; void *got; int id1, id2, id3, bad = 0; idr_init(&idr); df5688_show(&idr, "init"); id1 = idr_alloc(&idr, a, 526, 0, 0); kprintf("df5688[1]: idr_alloc(A, 526, 0, 0) -> %d\n", id1); df5688_show(&idr, "after-alloc-526"); got = idr_remove(&idr, 525); kprintf("df5688[2]: idr_remove(525) [NEVER allocated] -> %p " "(Linux semantics: NULL no-op; DF: PHANTOM DECREMENT)\n", got); df5688_show(&idr, "after-phantom-remove-525"); id2 = idr_alloc(&idr, b, 525, 532, 0); kprintf("df5688[3]: idr_alloc(B, 525, 532, 0) -> %d\n", id2); kprintf("df5688[3]: idr_find(%d) -> %p\n", id2, idr_find(&idr, id2)); id3 = idr_alloc(&idr, c, 525, 0, 0); kprintf("df5688[4]: idr_alloc(C, 525, 0, 0) -> %d\n", id3); got = idr_find(&idr, id2); kprintf("df5688[4]: idr_find(%d) -> %p (object B was %p)\n", id2, got, b); if (id3 == id2) { bad = 1; kprintf("df5688: *** DUPLICATE ID %d CONFIRMED IN-KERNEL: " "two idr_alloc() calls returned the same id while " "live; the first object pointer (%p) was silently " "overwritten\n", id2, b); } if (got == c && got != b) kprintf("df5688: *** object handle aliasing confirmed: " "handle %d now maps to C (%p), B (%p) leaked\n", id2, c, b); idr_destroy(&idr); kprintf("df5688: %s\n", bad ? "KERNEL MEMORY CORRUPTION (duplicate id) CONFIRMED" : "no corruption observed"); } static int df5688_handler(module_t mod, int type, void *data) { switch (type) { case MOD_LOAD: df5688_run(); return (0); default: return (0); } } static moduledata_t df5688_mod = { "df5688", df5688_handler, NULL }; DECLARE_MODULE(df5688, df5688_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); MODULE_VERSION(df5688, 1); |