diff options
author | David Gwynne <dlg@cvs.openbsd.org> | 2022-02-16 02:22:40 +0000 |
---|---|---|
committer | David Gwynne <dlg@cvs.openbsd.org> | 2022-02-16 02:22:40 +0000 |
commit | 21b5307c4631b0755fc011f119c3d7cd86bdaa12 (patch) | |
tree | 1adafe9d3600991226e43cf3002684e0ccfcf873 /sys/net/if_tun.c | |
parent | c9e1588e5a30a2740be73092b6132abaf02e915f (diff) |
prevent (re)opening of tun/tap interfaces that are being destroyed.
if an open tun (or tap) device is destroyed via the clone destroy
ioctl (eg, like what ifconfig destroy does), 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.
fix this by having tun_dev_open check for the TUN_DEAD flag that
the destroy function sets. this still relies on the kernel lock for
serialisation.
Reported-by: syzbot+5df2ad232f5f8b671442@syzkaller.appspotmail.com
ok visa@
Diffstat (limited to 'sys/net/if_tun.c')
-rw-r--r-- | sys/net/if_tun.c | 34 |
1 files changed, 23 insertions, 11 deletions
diff --git a/sys/net/if_tun.c b/sys/net/if_tun.c index 2932a67c42e..c54f51b0d54 100644 --- a/sys/net/if_tun.c +++ b/sys/net/if_tun.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_tun.c,v 1.233 2022/02/15 04:19:52 dlg Exp $ */ +/* $OpenBSD: if_tun.c,v 1.234 2022/02/16 02:22:39 dlg Exp $ */ /* $NetBSD: if_tun.c,v 1.24 1996/05/07 02:40:48 thorpej Exp $ */ /* @@ -218,6 +218,8 @@ tun_create(struct if_clone *ifc, int unit, int flags) 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 unit, int flags) } 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_clone *ifc, int mode, struct proc *p) 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_clone *ifc, int mode, struct proc *p) } } - 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); } /* |