DF-4996 / df4996.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 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | /* * DF-4996 / DF-4997 mechanism prover (DragonFly 6.5-DEVELOPMENT x86_64). * * DF-4996: ral_pci_attach() calls ral_pci_detach() when the child attach * (rt2560/rt2661/rt2860) fails. The child has already unwound its * own state at that point and never called ieee80211_ifattach(). * The second-pass child detach then reaches * ieee80211_ifdetach() (netproto/802_11/wlan/ieee80211.c:410) whose * LIST_REMOVE(ic, ic_next) writes through the zeroed le_prev == * NULL: kernel NULL-pointer write -> fatal trap 12 panic. * This module reproduces the exact sink on the audit QEMU guest * (which has no Ralink PCI hardware): a freshly-zeroed ieee80211com * is exactly the state the ral softc is in on that path (newbus * zeroes device softcs at allocation). * * DF-4997: if_ral_pci.c writes sc_invalid through the rt2560 union alias. * rt2860var.h drifted from the "same in both up to here" prefix * invariant (sc_snd moved before sc_mtx, sc_tx_timer dropped from * the prefix), so for rt2860-family devices the write lands on * rt2860_softc.sc_debug and rt2860_softc.sc_invalid stays 0. * This module prints the true compiled offsets. * * MOD_LOAD is safe (offsets only). The DF-4996 sink is triggered by * writing to the kern.df4996 sysctl, so the offset evidence survives even * if the panic capture is botched. */ #include <sys/param.h> #include <sys/systm.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/malloc.h> #include <sys/lock.h> #include <sys/mbuf.h> #include <sys/socket.h> #include <sys/sysctl.h> #include <sys/bus.h> #include <net/ethernet.h> #include <net/if.h> #include <net/if_var.h> #include <net/if_media.h> #include <net/route.h> #include <netproto/802_11/ieee80211_var.h> #include <dev/netif/ral/rt2560var.h> #include <dev/netif/ral/rt2661var.h> #include <dev/netif/ral/rt2860var.h> #define OFF(s, f) __offsetof(s, f) static void df4997_report_offsets(void) { kprintf("DF4997: sizeof(struct lock)=%zu sizeof(struct mbufq)=%zu\n", sizeof(struct lock), sizeof(struct mbufq)); kprintf("DF4997: rt2560_softc size=%zu: sc_st=%zu sc_sh=%zu " "watchdog_ch=%zu sc_tx_timer=%zu sc_invalid=%zu sc_debug=%zu\n", sizeof(struct rt2560_softc), OFF(struct rt2560_softc, sc_st), OFF(struct rt2560_softc, sc_sh), OFF(struct rt2560_softc, watchdog_ch), OFF(struct rt2560_softc, sc_tx_timer), OFF(struct rt2560_softc, sc_invalid), OFF(struct rt2560_softc, sc_debug)); kprintf("DF4997: rt2661_softc size=%zu: sc_invalid=%zu sc_debug=%zu\n", sizeof(struct rt2661_softc), OFF(struct rt2661_softc, sc_invalid), OFF(struct rt2661_softc, sc_debug)); kprintf("DF4997: rt2860_softc size=%zu: sc_st=%zu sc_sh=%zu " "watchdog_ch=%zu sc_invalid=%zu sc_debug=%zu sc_tx_timer=%zu\n", sizeof(struct rt2860_softc), OFF(struct rt2860_softc, sc_st), OFF(struct rt2860_softc, sc_sh), OFF(struct rt2860_softc, watchdog_ch), OFF(struct rt2860_softc, sc_invalid), OFF(struct rt2860_softc, sc_debug), OFF(struct rt2860_softc, sc_tx_timer)); if (OFF(struct rt2560_softc, sc_invalid) == OFF(struct rt2860_softc, sc_invalid)) kprintf("DF4997: VERDICT prefixes agree (claim FALSE)\n"); else if (OFF(struct rt2560_softc, sc_invalid) == OFF(struct rt2860_softc, sc_debug)) kprintf("DF4997: VERDICT rt2560.sc_invalid alias == " "rt2860.sc_debug: glue writes MISS sc_invalid and hit " "sc_debug on rt2860 (claim TRUE)\n"); else kprintf("DF4997: VERDICT misaligned onto some other field " "(delta %zd)\n", (ssize_t)OFF(struct rt2560_softc, sc_invalid) - (ssize_t)OFF(struct rt2860_softc, sc_invalid)); if (OFF(struct rt2560_softc, sc_st) == OFF(struct rt2860_softc, sc_st) && OFF(struct rt2560_softc, sc_sh) == OFF(struct rt2860_softc, sc_sh)) kprintf("DF4997: sc_st/sc_sh offsets DO coincide (bus tag/" "handle writes land correctly - impact limited to " "sc_invalid/sc_debug)\n"); else kprintf("DF4997: sc_st/sc_sh offsets ALSO diverge (worse than " "claimed!)\n"); } static void df4996_trigger_sink(void) { struct ieee80211com *ic; /* newbus softc state after failed child attach: all-zero ic */ ic = kmalloc(sizeof(*ic), M_DEVBUF, M_WAITOK | M_ZERO); kprintf("DF4996: zeroed ic=%p (== ral child softc after failed attach:" " ieee80211_ifattach() never ran)\n", ic); kprintf("DF4996: invoking ieee80211_ifdetach(ic) ...\n"); ieee80211_ifdetach(ic); kprintf("DF4996: SURVIVED ieee80211_ifdetach - mechanism NOT " "reproduced (unexpected)\n"); kfree(ic, M_DEVBUF); } static int df4996_sysctl(SYSCTL_HANDLER_ARGS) { char buf[16]; int error; memset(buf, 0, sizeof(buf)); error = sysctl_handle_string(oidp, buf, sizeof(buf), req); if (error != 0 || req->newptr == NULL) return (error); kprintf("DF4996: sysctl trigger '%s'\n", buf); df4996_trigger_sink(); return (0); } SYSCTL_PROC(_kern, OID_AUTO, df4996, CTLTYPE_STRING | CTLFLAG_RW, 0, 0, df4996_sysctl, "A", "write any string to trigger the DF-4996 ifdetach sink demo"); static int df4996_modevent(module_t mod, int type, void *data) { switch (type) { case MOD_LOAD: kprintf("DF4996/DF4997 mechanism prover loaded\n"); df4997_report_offsets(); return (0); case MOD_UNLOAD: return (0); default: return (0); } } static moduledata_t df4996_mod = { "df4996", df4996_modevent, NULL }; DECLARE_MODULE(df4996, df4996_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); |