⬢ DragonFlyBSD Kernel Audit
DF-2340 / fix.diff
← back to finding ↓ download raw
--- sys/bus/u4b/net/if_kue.c.orig
+++ sys/bus/u4b/net/if_kue.c
@@ -439,6 +439,24 @@
 	kue_ctl(sc, KUE_CTL_READ, KUE_CMD_GET_ETHER_DESCRIPTOR,
 	    0, &sc->sc_desc, sizeof(sc->sc_desc));
 
+	/*
+	 * Only now is sc_desc.kue_mcastfilt valid, so only now can the
+	 * multicast filter buffer be sized.  This allocation used to
+	 * live in kue_attach(), where sc_desc was still all-zero:
+	 * kmalloc(0) returns ZERO_LENGTH_PTR ((void *)-8,
+	 * kern_slaballoc.c:193) which the NULL check cannot catch, and
+	 * kue_setmulti() later wrote filter entries through it once the
+	 * device-controlled descriptor had arrived -- deterministic
+	 * kernel page fault on any SIOCADDMULTI while a device reports
+	 * a non-zero filter count (DF-2340).  A count of zero keeps
+	 * sc_mcfilters NULL: kue_setmulti() then takes the allmulti
+	 * path without touching the buffer and kfree(NULL) is a no-op.
+	 */
+	if (KUE_MCFILTCNT(sc) != 0) {
+		sc->sc_mcfilters = kmalloc(KUE_MCFILTCNT(sc) * ETHER_ADDR_LEN,
+		    M_USBDEV, M_WAITOK | M_ZERO);
+	}
+
 	/* copy in ethernet address */
 	memcpy(ue->ue_eaddr, sc->sc_desc.kue_macaddr, sizeof(ue->ue_eaddr));
 }
@@ -485,13 +503,6 @@
 		goto detach;
 	}
 
-	sc->sc_mcfilters = kmalloc(KUE_MCFILTCNT(sc) * ETHER_ADDR_LEN,
-	    M_USBDEV, M_WAITOK);
-	if (sc->sc_mcfilters == NULL) {
-		device_printf(dev, "failed allocating USB memory\n");
-		goto detach;
-	}
-
 	ue->ue_sc = sc;
 	ue->ue_dev = dev;
 	ue->ue_udev = uaa->device;