This diff converts a struct ifnet pointer in pfsync's softc into an
ifindex with corresponding if_get()/if_put() calls.

Seems to still work fine and fixes the following panic reported by double-p:

ifconfig pfsync0 syncdev vlan5 up
ifconfig vlan5 destroy
ifconfig pfsync0 destroy
-> crash because of stale pointer sc->sc_sync_if

ddb> trace
hook_disestablish() at hook_disestablish+0x25
pfsync_clone_destroy() at pfsync_clone_destroy+0x85
ifioctl() at ifioctl+0x232
sys_ioctl() at sys_ioctl+0x196
syscall() at syscall+0x19b
--- syscall (number 54) ---
end of kernel
end trace frame

Index: if_pfsync.c
===================================================================
RCS file: /cvs/src/sys/net/if_pfsync.c,v
retrieving revision 1.245
diff -u -p -r1.245 if_pfsync.c
--- if_pfsync.c 20 Feb 2017 06:30:39 -0000      1.245
+++ if_pfsync.c 9 Mar 2017 06:55:28 -0000
@@ -196,7 +196,7 @@ void        pfsync_out_tdb(struct tdb *, void *
 
 struct pfsync_softc {
        struct ifnet             sc_if;
-       struct ifnet            *sc_sync_if;
+       int                      sc_syncdev_ifidx;
 
        struct pool              sc_pool;
 
@@ -355,6 +355,7 @@ pfsync_clone_destroy(struct ifnet *ifp)
 {
        struct pfsync_softc *sc = ifp->if_softc;
        struct pfsync_deferral *pd;
+       struct ifnet *syncdev;
 
        timeout_del(&sc->sc_bulkfail_tmo);
        timeout_del(&sc->sc_bulk_tmo);
@@ -365,10 +366,13 @@ pfsync_clone_destroy(struct ifnet *ifp)
        if (sc->sc_link_demoted)
                carp_group_demote_adj(&sc->sc_if, -1, "pfsync destroy");
 #endif
-       if (sc->sc_sync_if)
+       syncdev = if_get(sc->sc_syncdev_ifidx);
+       if (syncdev) {
                hook_disestablish(
-                   sc->sc_sync_if->if_linkstatehooks,
+                   syncdev->if_linkstatehooks,
                    sc->sc_lhcookie);
+               if_put(syncdev);
+       }
        if_detach(ifp);
 
        pfsync_drop(sc);
@@ -401,11 +405,15 @@ void
 pfsync_syncdev_state(void *arg)
 {
        struct pfsync_softc *sc = arg;
+       struct ifnet *syncdev;
 
-       if (!sc->sc_sync_if || !(sc->sc_if.if_flags & IFF_UP))
+       syncdev = if_get(sc->sc_syncdev_ifidx);
+       if (!syncdev || !(sc->sc_if.if_flags & IFF_UP)) {
+               if_put(syncdev);
                return;
+       }
 
-       if (sc->sc_sync_if->if_link_state == LINK_STATE_DOWN) {
+       if (syncdev->if_link_state == LINK_STATE_DOWN) {
                sc->sc_if.if_flags &= ~IFF_RUNNING;
                if (!sc->sc_link_demoted) {
 #if NCARP > 0
@@ -425,6 +433,8 @@ pfsync_syncdev_state(void *arg)
 
                pfsync_request_full_update(sc);
        }
+
+       if_put(syncdev);
 }
 
 int
@@ -640,6 +650,7 @@ pfsync_input(struct mbuf **mp, int *offp
 {
        struct mbuf *n, *m = *mp;
        struct pfsync_softc *sc = pfsyncif;
+       struct ifnet *syncdev;
        struct ip *ip = mtod(m, struct ip *);
        struct pfsync_header *ph;
        struct pfsync_subheader subh;
@@ -649,11 +660,14 @@ pfsync_input(struct mbuf **mp, int *offp
 
        /* verify that we have a sync interface configured */
        if (sc == NULL || !ISSET(sc->sc_if.if_flags, IFF_RUNNING) ||
-           sc->sc_sync_if == NULL || !pf_status.running)
+           !pf_status.running)
+               goto done;
+       syncdev = if_get(sc->sc_syncdev_ifidx);
+       if (syncdev == NULL)
                goto done;
 
        /* verify that the packet came in on the right interface */
-       if (sc->sc_sync_if->if_index != m->m_pkthdr.ph_ifidx) {
+       if (sc->sc_syncdev_ifidx != m->m_pkthdr.ph_ifidx) {
                pfsyncstat_inc(pfsyncs_badif);
                goto done;
        }
@@ -729,6 +743,7 @@ pfsync_input(struct mbuf **mp, int *offp
 
 done:
        m_freem(m);
+       if_put(syncdev);
        return IPPROTO_DONE;
 }
 
@@ -1234,7 +1249,7 @@ pfsyncioctl(struct ifnet *ifp, u_long cm
        struct ifreq *ifr = (struct ifreq *)data;
        struct ip_moptions *imo = &sc->sc_imo;
        struct pfsyncreq pfsyncr;
-       struct ifnet    *sifp;
+       struct ifnet    *sifp, *syncdev = NULL;
        struct ip *ip;
        int s, error;
 
@@ -1269,10 +1284,13 @@ pfsyncioctl(struct ifnet *ifp, u_long cm
                splx(s);
                break;
        case SIOCSIFMTU:
-               if (!sc->sc_sync_if ||
+               syncdev = if_get(sc->sc_syncdev_ifidx);
+               if (!syncdev ||
                    ifr->ifr_mtu <= PFSYNC_MINPKT ||
-                   ifr->ifr_mtu > sc->sc_sync_if->if_mtu)
+                   ifr->ifr_mtu > syncdev->if_mtu) {
+                       if_put(syncdev);
                        return (EINVAL);
+               }
                s = splnet();
                if (ifr->ifr_mtu < ifp->if_mtu)
                        pfsync_sendout();
@@ -1281,9 +1299,11 @@ pfsyncioctl(struct ifnet *ifp, u_long cm
                break;
        case SIOCGETPFSYNC:
                bzero(&pfsyncr, sizeof(pfsyncr));
-               if (sc->sc_sync_if) {
-                       strlcpy(pfsyncr.pfsyncr_syncdev,
-                           sc->sc_sync_if->if_xname, IFNAMSIZ);
+               syncdev = if_get(sc->sc_syncdev_ifidx);
+               if (syncdev) {
+                       strlcpy(pfsyncr.pfsyncr_syncdev, syncdev->if_xname,
+                           IFNAMSIZ);
+                       if_put(syncdev);
                }
                pfsyncr.pfsyncr_syncpeer = sc->sc_sync_peer;
                pfsyncr.pfsyncr_maxupdates = sc->sc_maxupdates;
@@ -1313,11 +1333,15 @@ pfsyncioctl(struct ifnet *ifp, u_long cm
                sc->sc_defer = pfsyncr.pfsyncr_defer;
 
                if (pfsyncr.pfsyncr_syncdev[0] == 0) {
-                       if (sc->sc_sync_if)
+                       syncdev = if_get(sc->sc_syncdev_ifidx);
+                       if (syncdev) {
                                hook_disestablish(
-                                   sc->sc_sync_if->if_linkstatehooks,
+                                   syncdev->if_linkstatehooks,
                                    sc->sc_lhcookie);
-                       sc->sc_sync_if = NULL;
+                               if_put(syncdev);
+                       }
+                       sc->sc_syncdev_ifidx = 0;
+                       syncdev = NULL;
                        if (imo->imo_num_memberships > 0) {
                                in_delmulti(imo->imo_membership[
                                    --imo->imo_num_memberships]);
@@ -1332,29 +1356,33 @@ pfsyncioctl(struct ifnet *ifp, u_long cm
                        return (EINVAL);
                }
 
+               syncdev = if_get(sc->sc_syncdev_ifidx);
                if (sifp->if_mtu < sc->sc_if.if_mtu ||
-                   (sc->sc_sync_if != NULL &&
-                   sifp->if_mtu < sc->sc_sync_if->if_mtu) ||
+                   (syncdev != NULL &&
+                   sifp->if_mtu < syncdev->if_mtu) ||
                    sifp->if_mtu < MCLBYTES - sizeof(struct ip))
                        pfsync_sendout();
 
-               if (sc->sc_sync_if)
+               if (syncdev) {
                        hook_disestablish(
-                           sc->sc_sync_if->if_linkstatehooks,
+                           syncdev->if_linkstatehooks,
                            sc->sc_lhcookie);
-               sc->sc_sync_if = sifp;
+                       if_put(syncdev);
+               }
+               syncdev = sifp;
+               sc->sc_syncdev_ifidx = syncdev->if_index;
 
                if (imo->imo_num_memberships > 0) {
                        
in_delmulti(imo->imo_membership[--imo->imo_num_memberships]);
                        imo->imo_ifidx = 0;
                }
 
-               if (sc->sc_sync_if &&
+               if (syncdev &&
                    sc->sc_sync_peer.s_addr == INADDR_PFSYNC_GROUP) {
                        struct in_addr addr;
 
-                       if (!(sc->sc_sync_if->if_flags & IFF_MULTICAST)) {
-                               sc->sc_sync_if = NULL;
+                       if (!(syncdev->if_flags & IFF_MULTICAST)) {
+                               sc->sc_syncdev_ifidx = 0;
                                splx(s);
                                return (EADDRNOTAVAIL);
                        }
@@ -1362,13 +1390,13 @@ pfsyncioctl(struct ifnet *ifp, u_long cm
                        addr.s_addr = INADDR_PFSYNC_GROUP;
 
                        if ((imo->imo_membership[0] =
-                           in_addmulti(&addr, sc->sc_sync_if)) == NULL) {
-                               sc->sc_sync_if = NULL;
+                           in_addmulti(&addr, syncdev)) == NULL) {
+                               sc->sc_syncdev_ifidx = 0;
                                splx(s);
                                return (ENOBUFS);
                        }
                        imo->imo_num_memberships++;
-                       imo->imo_ifidx = sc->sc_sync_if->if_index;
+                       imo->imo_ifidx = syncdev->if_index;
                        imo->imo_ttl = PFSYNC_DFLTTL;
                        imo->imo_loop = 0;
                }
@@ -1386,7 +1414,7 @@ pfsyncioctl(struct ifnet *ifp, u_long cm
                ip->ip_dst.s_addr = sc->sc_sync_peer.s_addr;
 
                sc->sc_lhcookie =
-                   hook_establish(sc->sc_sync_if->if_linkstatehooks, 1,
+                   hook_establish(syncdev->if_linkstatehooks, 1,
                    pfsync_syncdev_state, sc);
 
                pfsync_request_full_update(sc);
@@ -1487,6 +1515,7 @@ pfsync_sendout(void)
 #if NBPFILTER > 0
        struct ifnet *ifp = &sc->sc_if;
 #endif
+       struct ifnet *syncdev;
        struct mbuf *m;
        struct ip *ip;
        struct pfsync_header *ph;
@@ -1501,12 +1530,14 @@ pfsync_sendout(void)
        if (sc == NULL || sc->sc_len == PFSYNC_MINPKT)
                return;
 
+       syncdev = if_get(sc->sc_syncdev_ifidx);
        if (!ISSET(sc->sc_if.if_flags, IFF_RUNNING) ||
 #if NBPFILTER > 0
-           (ifp->if_bpf == NULL && sc->sc_sync_if == NULL)) {
+           (ifp->if_bpf == NULL && syncdev == NULL)) {
 #else
-           sc->sc_sync_if == NULL) {
+           syncdev == NULL) {
 #endif
+               if_put(syncdev);
                pfsync_drop(sc);
                return;
        }
@@ -1515,6 +1546,7 @@ pfsync_sendout(void)
        if (m == NULL) {
                sc->sc_if.if_oerrors++;
                pfsyncstat_inc(pfsyncs_onomem);
+               if_put(syncdev);
                pfsync_drop(sc);
                return;
        }
@@ -1525,6 +1557,7 @@ pfsync_sendout(void)
                        m_free(m);
                        sc->sc_if.if_oerrors++;
                        pfsyncstat_inc(pfsyncs_onomem);
+                       if_put(syncdev);
                        pfsync_drop(sc);
                        return;
                }
@@ -1637,7 +1670,7 @@ pfsync_sendout(void)
                m->m_len = m->m_pkthdr.len = sc->sc_len;
        }
 
-       if (sc->sc_sync_if == NULL) {
+       if (syncdev == NULL) {
                sc->sc_len = PFSYNC_MINPKT;
                m_freem(m);
                return;
@@ -1656,6 +1689,8 @@ pfsync_sendout(void)
                pfsyncstat_inc(pfsyncs_opackets);
        else
                pfsyncstat_inc(pfsyncs_oerrors);
+
+       if_put(syncdev);
 }
 
 void
@@ -1891,7 +1926,8 @@ pfsync_cancel_full_update(struct pfsync_
 void
 pfsync_request_full_update(struct pfsync_softc *sc)
 {
-       if (sc->sc_sync_if && ISSET(sc->sc_if.if_flags, IFF_RUNNING)) {
+       struct ifnet *syncdev = if_get(sc->sc_syncdev_ifidx);
+       if (syncdev && ISSET(sc->sc_if.if_flags, IFF_RUNNING)) {
                /* Request a full state table update. */
                sc->sc_ureq_sent = time_uptime;
 #if NCARP > 0
@@ -1907,6 +1943,7 @@ pfsync_request_full_update(struct pfsync
                    sizeof(struct pfsync_state)));
                pfsync_request_update(0, 0);
        }
+       if_put(syncdev);
 }
 
 void

Reply via email to