DF-3405 / fix.diff
--- 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); } |