if_detach() should wait until concurrent (*if_qstart)() interface start
authormvs <mvs@openbsd.org>
Sun, 10 Jul 2022 21:26:55 +0000 (21:26 +0000)
committermvs <mvs@openbsd.org>
Sun, 10 Jul 2022 21:26:55 +0000 (21:26 +0000)
routines finished.

Call ifq_barrier(9) just after we unlinked dying interface from the stack.
From this point it is not accessible by if_get(9) and if_unit(9), and all
concurrent threads owning interface pointer finished. It also detached
from pseudo drivers like bridge(4). We only could have concurrent
(*if_qstart)() handlers running, so wait them and then continue
destruction.

Reported and tested by Hrvoje Popovski.

ok bluhm@

sys/net/if.c

index 19b2f38..d54c7e6 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: if.c,v 1.657 2022/06/29 09:08:07 mvs Exp $    */
+/*     $OpenBSD: if.c,v 1.658 2022/07/10 21:26:55 mvs Exp $    */
 /*     $NetBSD: if.c,v 1.35 1996/05/07 05:26:04 thorpej Exp $  */
 
 /*
@@ -1029,6 +1029,10 @@ if_detach(struct ifnet *ifp)
        /* Other CPUs must not have a reference before we start destroying. */
        if_remove(ifp);
 
+       ifp->if_qstart = if_detached_qstart;
+
+       /* Wait until the start routines finished. */
+       ifq_barrier(&ifp->if_snd);
        ifq_clr_oactive(&ifp->if_snd);
 
 #if NBPFILTER > 0
@@ -1037,7 +1041,6 @@ if_detach(struct ifnet *ifp)
 
        NET_LOCK();
        s = splnet();
-       ifp->if_qstart = if_detached_qstart;
        ifp->if_ioctl = if_detached_ioctl;
        ifp->if_watchdog = NULL;