diff options
author | Jason A. Donenfeld <Jason@zx2c4.com> | 2021-04-14 14:42:50 -0600 |
---|---|---|
committer | Jason A. Donenfeld <Jason@zx2c4.com> | 2021-04-15 18:22:10 -0600 |
commit | 65c2211c619f779bcdef50a485970ad50545fe01 (patch) | |
tree | 748240cc0c66807508e5a242ad07177dcbaf9dd4 | |
parent | if_wg: don't check return value of WAITOK (diff) | |
download | wireguard-freebsd-65c2211c619f779bcdef50a485970ad50545fe01.tar.xz wireguard-freebsd-65c2211c619f779bcdef50a485970ad50545fe01.zip |
if_wg: do not allow ioctl to race with clone_destroy
This fixes the crash from:
bash -c 'while true; do ifconfig wg0 create; ifconfig wg0 destroy; done&
while true; do wg show wg0 > /dev/null 2>&1; done& wait'
Since we're setting ifp to NULL here, we also have to account for
multicast v6 packets being transmitted during destroy, which can be
triggered by:
ifconfig wg0 create
ifconfig wg0 inet6 fe80::1234/120
ifconfig wg0 up
route add -inet6 ff02::1:0/120 -iface wg0
ifconfig wg0 destroy
These are unfixed upstream bug that we're working around.
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
-rw-r--r-- | src/if_wg.c | 26 |
1 files changed, 16 insertions, 10 deletions
diff --git a/src/if_wg.c b/src/if_wg.c index 544884f..14b742e 100644 --- a/src/if_wg.c +++ b/src/if_wg.c @@ -2441,22 +2441,18 @@ free: static int wg_transmit(struct ifnet *ifp, struct mbuf *m) { - struct wg_softc *sc; + struct wg_softc *sc = ifp->if_softc; sa_family_t family; struct epoch_tracker et; struct wg_peer *peer; struct wg_tag *t; uint32_t af; - int rc; + int rc = 0; - /* - * Work around lifetime issue in the ipv6 mld code. - */ - if (__predict_false(ifp->if_flags & IFF_DYING)) + /* Work around lifetime issue in the ipv6 mld code. */ + if (__predict_false((ifp->if_flags & IFF_DYING) || !sc)) return (ENXIO); - rc = 0; - sc = ifp->if_softc; if ((t = wg_tag_get(m)) == NULL) { rc = ENOBUFS; goto early_out; @@ -2888,9 +2884,16 @@ wg_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct wg_data_io *wgd = (struct wg_data_io *)data; struct ifreq *ifr = (struct ifreq *)data; - struct wg_softc *sc = ifp->if_softc; + struct wg_softc *sc; int ret = 0; + sx_slock(&wg_sx); + sc = ifp->if_softc; + if (!sc) { + ret = ENXIO; + goto out; + } + switch (cmd) { case SIOCSWG: ret = priv_check(curthread, PRIV_NET_WG); @@ -2908,7 +2911,7 @@ wg_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) */ break; case SIOCSIFFLAGS: - if ((ifp->if_flags & IFF_UP) != 0) + if (ifp->if_flags & IFF_UP) ret = wg_up(sc); else wg_down(sc); @@ -2940,6 +2943,8 @@ wg_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) ret = ENOTTY; } +out: + sx_sunlock(&wg_sx); return ret; } @@ -3118,6 +3123,7 @@ wg_clone_destroy(struct ifnet *ifp) struct ucred *cred; sx_xlock(&wg_sx); + ifp->if_softc = NULL; sx_xlock(&sc->sc_lock); sc->sc_flags |= WGF_DYING; cred = sc->sc_ucred; |