⬢ DragonFlyBSD Kernel Audit
DF-5493 / fix.diff
← back to finding ↓ download raw
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];
 };