git: 6ca0ca7b4cb5 - main - IPv4 multicast: fix LOR in shutdown path

From: Mike Karels <karels_at_FreeBSD.org>
Date: Mon, 11 Apr 2022 19:52:13 UTC
The branch main has been updated by karels:

URL: https://cgit.FreeBSD.org/src/commit/?id=6ca0ca7b4cb527dc17c289f1ae177ec267fd1add

commit 6ca0ca7b4cb527dc17c289f1ae177ec267fd1add
Author:     Mike Karels <karels@FreeBSD.org>
AuthorDate: 2022-04-08 12:37:15 +0000
Commit:     Mike Karels <karels@FreeBSD.org>
CommitDate: 2022-04-11 19:51:16 +0000

    IPv4 multicast: fix LOR in shutdown path
    
    X_ip_mrouter_done() was calling the interface ioctl routines via
    if_allmulti() while holding a write lock.  However, some interface
    ioctl routines, including em/iflib and tap, use sxlocks, which are
    not permitted while holding a non-sleepable lock, and this elicits
    a warning from WITNESS.  Fix the locking issue by recording the
    affected interface pointers in a malloc'ed array, and call
    if_allmulti() on each after dropping the rwlock.
    
    Reviewed by:    bz
    Differential Revision: https://reviews.freebsd.org/D34845
---
 sys/netinet/ip_mroute.c | 26 ++++++++++++++++++++------
 1 file changed, 20 insertions(+), 6 deletions(-)

diff --git a/sys/netinet/ip_mroute.c b/sys/netinet/ip_mroute.c
index cc356e3678f3..f77898fb3aa4 100644
--- a/sys/netinet/ip_mroute.c
+++ b/sys/netinet/ip_mroute.c
@@ -747,7 +747,8 @@ ip_mrouter_init(struct socket *so, int version)
 static int
 X_ip_mrouter_done(void)
 {
-    struct ifnet *ifp;
+    struct ifnet **ifps;
+    int nifp;
     u_long i;
     vifi_t vifi;
     struct bw_upcall *bu;
@@ -774,6 +775,8 @@ X_ip_mrouter_done(void)
     	taskqueue_drain(V_task_queue, &V_task);
     }
 
+    ifps = malloc(MAXVIFS * sizeof(*ifps), M_TEMP, M_WAITOK);
+
     MRW_WLOCK();
     taskqueue_cancel(V_task_queue, &V_task, NULL);
 
@@ -785,14 +788,17 @@ X_ip_mrouter_done(void)
     mtx_destroy(&V_bw_upcalls_ring_mtx);
 
     /*
-     * For each phyint in use, disable promiscuous reception of all IP
-     * multicasts.
+     * For each phyint in use, prepare to disable promiscuous reception
+     * of all IP multicasts.  Defer the actual call until the lock is released;
+     * just record the list of interfaces while locked.  Some interfaces use
+     * sx locks in their ioctl routines, which is not allowed while holding
+     * a non-sleepable lock.
      */
-    for (vifi = 0; vifi < V_numvifs; vifi++) {
+    KASSERT(V_numvifs <= MAXVIFS, ("More vifs than possible"));
+    for (vifi = 0, nifp = 0; vifi < V_numvifs; vifi++) {
 	if (!in_nullhost(V_viftable[vifi].v_lcl_addr) &&
 		!(V_viftable[vifi].v_flags & (VIFF_TUNNEL | VIFF_REGISTER))) {
-	    ifp = V_viftable[vifi].v_ifp;
-	    if_allmulti(ifp, 0);
+	    ifps[nifp++] = V_viftable[vifi].v_ifp;
 	}
     }
     bzero((caddr_t)V_viftable, sizeof(*V_viftable) * MAXVIFS);
@@ -824,6 +830,14 @@ X_ip_mrouter_done(void)
 
     MRW_WUNLOCK();
 
+    /*
+     * Now drop our claim on promiscuous multicast on the interfaces recorded
+     * above.  This is safe to do now because ALLMULTI is reference counted.
+     */
+    for (vifi = 0; vifi < nifp; vifi++)
+	    if_allmulti(ifps[vifi], 0);
+    free(ifps, M_TEMP);
+
     CTR1(KTR_IPMF, "%s: done", __func__);
 
     return 0;