On Tue, Feb 15, 2022 at 02:35:33PM +1000, David Gwynne wrote: > if an open tun (or tap) device is destroyed via ifconfig destroy, > there is a window while the open device is being revoked on the vfs > side that a third thread can come and open it again. this in turn > triggers a kassert in the ifconfig destroy path where it expects the > device to be closed. > > i think this diff fixes it by having the open code refuse to work with a > device that's in the process of being destroyed. > > this was found by syzkaller. the detail is at > https://syzkaller.appspot.com/bug?id=1d1ea7860c36e5066edea1145cf2d56715d5042b. > > i was able to reproduce the problem with this code. it was pretty hard > to hit, but ive had no luck since adding the TUN_DEAD handling to > tun_dev_open.
The diff looks good to me. OK visa@ > Index: if_tun.c > =================================================================== > RCS file: /cvs/src/sys/net/if_tun.c,v > retrieving revision 1.233 > diff -u -p -r1.233 if_tun.c > --- if_tun.c 15 Feb 2022 04:19:52 -0000 1.233 > +++ if_tun.c 15 Feb 2022 04:21:53 -0000 > @@ -218,6 +218,8 @@ tun_create(struct if_clone *ifc, int uni > KERNEL_ASSERT_LOCKED(); > > sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO); > + refcnt_init(&sc->sc_refs); > + > ifp = &sc->sc_if; > snprintf(ifp->if_xname, sizeof(ifp->if_xname), > "%s%d", ifc->ifc_name, unit); > @@ -267,7 +269,6 @@ tun_create(struct if_clone *ifc, int uni > } > > sigio_init(&sc->sc_sigio); > - refcnt_init(&sc->sc_refs); > > /* tell tun_dev_open we're initialised */ > > @@ -381,7 +382,7 @@ tun_dev_open(dev_t dev, const struct if_ > rdomain = rtable_l2(p->p_p->ps_rtableid); > > /* let's find or make an interface to work with */ > - while ((ifp = if_unit(name)) == NULL) { > + while ((sc = tun_name_lookup(name)) == NULL) { > error = if_clone_create(name, rdomain); > switch (error) { > case 0: /* it's probably ours */ > @@ -394,34 +395,45 @@ tun_dev_open(dev_t dev, const struct if_ > } > } > > - sc = ifp->if_softc; > + refcnt_take(&sc->sc_refs); > + > /* wait for it to be fully constructed before we use it */ > - while (!ISSET(sc->sc_flags, TUN_INITED)) { > + for (;;) { > + if (ISSET(sc->sc_flags, TUN_DEAD)) { > + error = ENXIO; > + goto done; > + } > + > + if (ISSET(sc->sc_flags, TUN_INITED)) > + break; > + > error = tsleep_nsec(sc, PCATCH, "tuninit", INFSLP); > if (error != 0) { > /* XXX if_clone_destroy if stayup? */ > - if_put(ifp); > - return (error); > + goto done; > } > } > > if (sc->sc_dev != 0) { > /* aww, we lost */ > - if_put(ifp); > - return (EBUSY); > + error = EBUSY; > + goto done; > } > /* it's ours now */ > sc->sc_dev = dev; > CLR(sc->sc_flags, stayup); > > /* automatically mark the interface running on open */ > + ifp = &sc->sc_if; > NET_LOCK(); > SET(ifp->if_flags, IFF_UP | IFF_RUNNING); > NET_UNLOCK(); > tun_link_state(ifp, LINK_STATE_FULL_DUPLEX); > - if_put(ifp); > + error = 0; > > - return (0); > +done: > + tun_put(sc); > + return (error); > } > > /* >