⬢ DragonFlyBSD Kernel Audit
DF-3405 / fix.diff
← back to finding ↓ download raw
--- a/sys/net/tap/if_tap.c	2026-09-12 06:54:42.343593350 +0000
+++ b/sys/net/tap/if_tap.c	2026-09-12 06:55:11.939211287 +0000
@@ -220,7 +220,6 @@
 	int unit = minor(dev);
 
 	sc = kmalloc(sizeof(*sc), M_TAP, M_WAITOK | M_ZERO);
-	dev->si_drv1 = sc;
 	sc->tap_dev = dev;
 	sc->tap_flags |= flags;
 
@@ -250,6 +249,12 @@
 	sc->tap_flags |= TAP_INITED;
 	sc->tap_devq.ifq_maxlen = ifqmaxlen;
 
+	/*
+	 * Publish the softc only once it is fully initialized;
+	 * tapopen() reads dev->si_drv1 with no other lock held.
+	 */
+	dev->si_drv1 = sc;
+
 	SLIST_INSERT_HEAD(&tap_listhead, sc, tap_link);
 
 	TAPDEBUG(ifp, "created, minor = %#x, flags = 0x%x\n",
@@ -330,9 +335,10 @@
 
 	dev = ap->a_head.a_dev;
 	sc = dev->si_drv1;
-	if (sc == NULL && (sc = tapcreate(dev, 0)) == NULL) {
+	if (sc == NULL) {
+		/* destroyed (or not yet born): do not resurrect it */
 		rel_mplock();
-		return (ENOMEM);
+		return (ENXIO);
 	}
 	if (sc->tap_flags & TAP_OPEN) {
 		rel_mplock();
@@ -503,17 +509,29 @@
 tap_clone_destroy(struct ifnet *ifp)
 {
 	struct tap_softc *sc = ifp->if_softc;
+	int error;
 
-	if (sc->tap_flags & TAP_OPEN)
-		return (EBUSY);
-	if ((sc->tap_flags & TAP_CLONE) == 0)
-		return (EINVAL);
-
-	TAPDEBUG(ifp, "clone destroyed, minor = %#x, flags = 0x%x\n",
-		 minor(sc->tap_dev), sc->tap_flags);
-	tapdestroy(sc);
+	/*
+	 * Serialize against tapopen()/tapclose(), which use the softc
+	 * under the mplock.  Without this, a tapopen() that fetched
+	 * dev->si_drv1 but has not yet set TAP_OPEN races this check,
+	 * tapdestroy() kfrees the softc, and tapopen() keeps writing to
+	 * freed memory (use-after-free).
+	 */
+	get_mplock();
+	if (sc->tap_flags & TAP_OPEN) {
+		error = EBUSY;
+	} else if ((sc->tap_flags & TAP_CLONE) == 0) {
+		error = EINVAL;
+	} else {
+		TAPDEBUG(ifp, "clone destroyed, minor = %#x, flags = 0x%x\n",
+			 minor(sc->tap_dev), sc->tap_flags);
+		tapdestroy(sc);
+		error = 0;
+	}
+	rel_mplock();
 
-	return (0);
+	return (error);
 }