diff --git a/sys/bus/u4b/net/usb_ethernet.c b/sys/bus/u4b/net/usb_ethernet.c --- a/sys/bus/u4b/net/usb_ethernet.c +++ b/sys/bus/u4b/net/usb_ethernet.c @@ -177,6 +177,10 @@ goto error; } + /* record that attach got past taskqueue creation: uether_ifdetach() + * may only tear down ue's that got this far (DF-5493) */ + ue->ue_attaching = 1; + /* fork rest of the attach code */ UE_LOCK(ue); ue_queue_command(ue, ue_attach_post_task, @@ -254,11 +258,16 @@ "%parent", CTLTYPE_STRING | CTLFLAG_RD, ue, 0, ue_sysctl_parent, "A", "parent device"); + /* the ifnet is published and fully set up */ + ue->ue_attached = 1; + KKASSERT(!lockowned(ue->ue_lock)); UE_LOCK(ue); return; fail: + /* NOTE: the unit was already returned to the bitmap above; nothing + * else was published, so uether_ifdetach() must skip the teardown. */ devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(ue), ue->ue_unit); UE_LOCK(ue); return; @@ -269,13 +278,33 @@ { struct ifnet *ifp; + /* + * Class-wide guard (DF-5493): eight drivers (cdce/aue/axge/urndis/ + * mos/udav/kue/cue, DF-5334 class) call their detach() -- and thus + * uether_ifdetach() -- on attach() failure paths where the usb_ether + * was never attached: + * + * - ue_lock == NULL (ue fields assigned only after the failing + * call): UE_LOCK() below would lockmgr() through NULL -> panic. + * - uether_ifattach() failed at usb_proc_create() (ue_tq all-zero, + * ue_attach_post_task never ran): the embedded ifnet was never + * if_attach()ed, so ether_ifdetach() below would tear down a + * zeroed ifnet (if_down/if_detach panic, ifindex2ifnet[0] and + * ifnet-list corruption). + * + * ue_attaching is only set once usb_proc_create() succeeded, and + * ue_attached only once ue_attach_post_task() published the ifnet. + */ + if (ue->ue_lock == NULL || !ue->ue_attaching) + return; + /* wait for any post attach or other command to complete */ usb_proc_drain(&ue->ue_tq); /* read "ifnet" pointer after taskqueue drain */ ifp = uether_getifp(ue); - if (ifp != NULL) { + if (ue->ue_attached) { /* we are not running any more */ UE_LOCK(ue); diff --git a/sys/bus/u4b/net/usb_ethernet.h b/sys/bus/u4b/net/usb_ethernet.h --- a/sys/bus/u4b/net/usb_ethernet.h +++ b/sys/bus/u4b/net/usb_ethernet.h @@ -103,6 +103,12 @@ int ue_unit; + /* attach-state guard: see uether_ifdetach() (DF-5493) */ + uint8_t ue_attaching; /* uether_ifattach() got past + * usb_proc_create() */ + uint8_t ue_attached; /* ue_attach_post_task() + * published the ifnet */ + /* ethernet address from eeprom */ uint8_t ue_eaddr[ETHER_ADDR_LEN]; };