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>
This commit is contained in:
Jason A. Donenfeld 2021-04-14 14:42:50 -06:00
parent 15df6797da
commit 65c2211c61
1 changed files with 16 additions and 10 deletions

View File

@ -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;