On Tue, Aug 23, 2022 at 02:47:11PM +0200, Stefan Sperling wrote:
> ddb{2}> show struct ifaddr 0xffff8000004e9400
> struct ifaddr at 0xffff8000004e9400 (64 bytes) {ifa_addr = (struct sockaddr 
> *)0
> xdeaf0009deafbead, ifa_dstaddr = (struct sockaddr *)0x20ef1a8af1d895de, 
> ifa_net
> mask = (struct sockaddr *)0xdeafbeaddeafbead, ifa_ifp = (struct ifnet 
> *)0xdeafb
> eaddeafbead, ifa_list = {tqe_next = (struct ifaddr *)0xdeafbeaddeafbead, 
> tqe_pr
> ev = 0xdeafbeaddeafbead}, ifa_flags = 0xdeafbead, ifa_refcnt = 0xdeafbead, 
> ifa_
> metric = 0xdeafbead}

The ifaddr has been freed.  That should not happen, as ifafree()
uses reference counting.  But this refcount is a simple integer
increment, that is not MP safe.  In theory all ++ should be protected
by exclusive netlock or shared netlock combiined with kernel lock.
But maybe I missed a path.

What are you doing with the machine?  Forwarding, IPv6, Stress test?
Do you have network interfaces with multiple network queues?

Anyway, instead of adding kernel locks here and there, use a propper
struct refcnt.  The variable ifatrash is never read, it looks like
a ddb debugging feature.  But for debugging refcount leaks we have
dt(4) now.

enc and mpls are special, they have interface address as part of
their sc structure.  I use refcount anyway, the new panic prevents
use after free.

This has passed a full regres run.

ok?

bluhm

Index: dev/dt/dt_prov_static.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/dev/dt/dt_prov_static.c,v
retrieving revision 1.14
diff -u -p -r1.14 dt_prov_static.c
--- dev/dt/dt_prov_static.c     28 Jun 2022 09:32:27 -0000      1.14
+++ dev/dt/dt_prov_static.c     23 Aug 2022 17:16:55 -0000
@@ -88,9 +88,10 @@ DT_STATIC_PROBE0(smr, wakeup);
 DT_STATIC_PROBE2(smr, thread, "uint64_t", "uint64_t");
 
 /*
- * reference counting
+ * reference counting, keep in sync with sys/refcnt.h
  */
 DT_STATIC_PROBE0(refcnt, none);
+DT_STATIC_PROBE3(refcnt, ifaddr, "void *", "int", "int");
 DT_STATIC_PROBE3(refcnt, inpcb, "void *", "int", "int");
 DT_STATIC_PROBE3(refcnt, tdb, "void *", "int", "int");
 
@@ -135,6 +136,7 @@ struct dt_probe *const dtps_static[] = {
        &_DT_STATIC_P(smr, thread),
        /* refcnt */
        &_DT_STATIC_P(refcnt, none),
+       &_DT_STATIC_P(refcnt, ifaddr),
        &_DT_STATIC_P(refcnt, inpcb),
        &_DT_STATIC_P(refcnt, tdb),
 };
Index: net/if_enc.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/net/if_enc.c,v
retrieving revision 1.78
diff -u -p -r1.78 if_enc.c
--- net/if_enc.c        28 Dec 2020 14:28:50 -0000      1.78
+++ net/if_enc.c        24 Aug 2022 07:51:49 -0000
@@ -100,6 +100,7 @@ enc_clone_create(struct if_clone *ifc, i
         * and empty ifa of type AF_LINK for this purpose.
         */
        if_alloc_sadl(ifp);
+       refcnt_init_trace(&sc->sc_ifa.ifa_refcnt, DT_REFCNT_IDX_IFADDR);
        sc->sc_ifa.ifa_ifp = ifp;
        sc->sc_ifa.ifa_addr = sdltosa(ifp->if_sadl);
        sc->sc_ifa.ifa_netmask = NULL;
@@ -152,6 +153,10 @@ enc_clone_destroy(struct ifnet *ifp)
        NET_UNLOCK();
 
        if_detach(ifp);
+       if (refcnt_rele(&sc->sc_ifa.ifa_refcnt) == 0) {
+               panic("%s: ifa refcnt has %u refs", __func__,
+                   sc->sc_ifa.ifa_refcnt.r_refs);
+       }
        free(sc, M_DEVBUF, sizeof(*sc));
 
        return (0);
Index: net/if_mpe.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/net/if_mpe.c,v
retrieving revision 1.101
diff -u -p -r1.101 if_mpe.c
--- net/if_mpe.c        8 Nov 2021 04:50:54 -0000       1.101
+++ net/if_mpe.c        24 Aug 2022 07:52:22 -0000
@@ -128,6 +128,7 @@ mpe_clone_create(struct if_clone *ifc, i
        sc->sc_txhprio = 0;
        sc->sc_rxhprio = IF_HDRPRIO_PACKET;
        sc->sc_rdomain = 0;
+       refcnt_init_trace(&sc->sc_ifa.ifa_refcnt, DT_REFCNT_IDX_IFADDR);
        sc->sc_ifa.ifa_ifp = ifp;
        sc->sc_ifa.ifa_addr = sdltosa(ifp->if_sadl);
        sc->sc_smpls.smpls_len = sizeof(sc->sc_smpls);
@@ -154,6 +155,10 @@ mpe_clone_destroy(struct ifnet *ifp)
        ifq_barrier(&ifp->if_snd);
 
        if_detach(ifp);
+       if (refcnt_rele(&sc->sc_ifa.ifa_refcnt) == 0) {
+               panic("%s: ifa refcnt has %u refs", __func__,
+                   sc->sc_ifa.ifa_refcnt.r_refs);
+       }
        free(sc, M_DEVBUF, sizeof *sc);
        return (0);
 }
Index: net/if_mpip.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/net/if_mpip.c,v
retrieving revision 1.15
diff -u -p -r1.15 if_mpip.c
--- net/if_mpip.c       26 Mar 2021 19:00:21 -0000      1.15
+++ net/if_mpip.c       24 Aug 2022 07:52:26 -0000
@@ -128,6 +128,7 @@ mpip_clone_create(struct if_clone *ifc, 
        bpfattach(&ifp->if_bpf, ifp, DLT_LOOP, sizeof(uint32_t));
 #endif
 
+       refcnt_init_trace(&sc->sc_ifa.ifa_refcnt, DT_REFCNT_IDX_IFADDR);
        sc->sc_ifa.ifa_ifp = ifp;
        sc->sc_ifa.ifa_addr = sdltosa(ifp->if_sadl);
 
@@ -152,7 +153,10 @@ mpip_clone_destroy(struct ifnet *ifp)
        ifq_barrier(&ifp->if_snd);
 
        if_detach(ifp);
-
+       if (refcnt_rele(&sc->sc_ifa.ifa_refcnt) == 0) {
+               panic("%s: ifa refcnt has %u refs", __func__,
+                   sc->sc_ifa.ifa_refcnt.r_refs);
+       }
        free(sc->sc_neighbor, M_DEVBUF, sizeof(*sc->sc_neighbor));
        free(sc, M_DEVBUF, sizeof(*sc));
 
Index: net/if_mpw.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/net/if_mpw.c,v
retrieving revision 1.62
diff -u -p -r1.62 if_mpw.c
--- net/if_mpw.c        26 Mar 2021 19:00:21 -0000      1.62
+++ net/if_mpw.c        24 Aug 2022 07:52:00 -0000
@@ -122,6 +122,7 @@ mpw_clone_create(struct if_clone *ifc, i
        sc->sc_txhprio = 0;
        sc->sc_rxhprio = IF_HDRPRIO_PACKET;
        sc->sc_rdomain = 0;
+       refcnt_init_trace(&sc->sc_ifa.ifa_refcnt, DT_REFCNT_IDX_IFADDR);
        sc->sc_ifa.ifa_ifp = ifp;
        sc->sc_ifa.ifa_addr = sdltosa(ifp->if_sadl);
        sc->sc_smpls.smpls_len = sizeof(sc->sc_smpls);
@@ -149,7 +150,10 @@ mpw_clone_destroy(struct ifnet *ifp)
 
        ether_ifdetach(ifp);
        if_detach(ifp);
-
+       if (refcnt_rele(&sc->sc_ifa.ifa_refcnt) == 0) {
+               panic("%s: ifa refcnt has %u refs", __func__,
+                   sc->sc_ifa.ifa_refcnt.r_refs);
+       }
        free(sc->sc_neighbor, M_DEVBUF, sizeof(*sc->sc_neighbor));
        free(sc, M_DEVBUF, sizeof(*sc));
 
Index: net/if_pppx.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/net/if_pppx.c,v
retrieving revision 1.121
diff -u -p -r1.121 if_pppx.c
--- net/if_pppx.c       25 Jul 2022 08:29:26 -0000      1.121
+++ net/if_pppx.c       23 Aug 2022 17:34:20 -0000
@@ -659,6 +659,7 @@ pppx_add_session(struct pppx_dev *pxd, s
        ifaddr.sin_addr = req->pr_ip_srcaddr;
 
        ia = malloc(sizeof (*ia), M_IFADDR, M_WAITOK | M_ZERO);
+       refcnt_init_trace(&ia->ia_ifa.ifa_refcnt, DT_REFCNT_IDX_IFADDR);
 
        ia->ia_addr.sin_family = AF_INET;
        ia->ia_addr.sin_len = sizeof(struct sockaddr_in);
Index: net/if_var.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/net/if_var.h,v
retrieving revision 1.114
diff -u -p -r1.114 if_var.h
--- net/if_var.h        20 Feb 2021 04:55:52 -0000      1.114
+++ net/if_var.h        23 Aug 2022 16:25:32 -0000
@@ -242,7 +242,7 @@ struct ifaddr {
        struct  ifnet *ifa_ifp;         /* back-pointer to interface */
        TAILQ_ENTRY(ifaddr) ifa_list;   /* list of addresses for interface */
        u_int   ifa_flags;              /* interface flags, see below */
-       u_int   ifa_refcnt;             /* number of `rt_ifa` references */
+       struct  refcnt ifa_refcnt;      /* number of `rt_ifa` references */
        int     ifa_metric;             /* cost of going out this interface */
 };
 
@@ -333,6 +333,7 @@ int p2p_bpf_mtap(caddr_t, const struct m
 struct ifaddr *ifa_ifwithaddr(struct sockaddr *, u_int);
 struct ifaddr *ifa_ifwithdstaddr(struct sockaddr *, u_int);
 struct ifaddr *ifaof_ifpforaddr(struct sockaddr *, struct ifnet *);
+struct ifaddr *ifaref(struct ifaddr *);
 void   ifafree(struct ifaddr *);
 
 int    if_isconnected(const struct ifnet *, unsigned int);
Index: net/route.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/net/route.c,v
retrieving revision 1.413
diff -u -p -r1.413 route.c
--- net/route.c 28 Jul 2022 22:19:09 -0000      1.413
+++ net/route.c 23 Aug 2022 16:31:29 -0000
@@ -146,7 +146,6 @@ extern unsigned int rtmap_limit;
 
 struct cpumem *                rtcounters;
 int                    rttrash;        /* routes not in table but not freed */
-int                    ifatrash;       /* ifas not in ifp list but not free */
 
 struct pool    rtentry_pool;           /* pool for rtentry structures */
 struct pool    rttimer_pool;           /* pool for rttimer structures */
@@ -512,16 +511,19 @@ rtfree(struct rtentry *rt)
        pool_put(&rtentry_pool, rt);
 }
 
+struct ifaddr *
+ifaref(struct ifaddr *ifa)
+{
+       refcnt_take(&ifa->ifa_refcnt);
+       return ifa;
+}
+
 void
 ifafree(struct ifaddr *ifa)
 {
-       if (ifa == NULL)
-               panic("ifafree");
-       if (ifa->ifa_refcnt == 0) {
-               ifatrash--;
-               free(ifa, M_IFADDR, 0);
-       } else
-               ifa->ifa_refcnt--;
+       if (refcnt_rele(&ifa->ifa_refcnt) == 0)
+               return;
+       free(ifa, M_IFADDR, 0);
 }
 
 /*
@@ -901,8 +903,7 @@ rtrequest(int req, struct rt_addrinfo *i
                        rt_mpls_clear(rt);
 #endif
 
-               ifa->ifa_refcnt++;
-               rt->rt_ifa = ifa;
+               rt->rt_ifa = ifaref(ifa);
                rt->rt_ifidx = ifp->if_index;
                /*
                 * Copy metrics and a back pointer from the cloned
@@ -1857,8 +1858,8 @@ db_print_ifa(struct ifaddr *ifa)
        db_print_sa(ifa->ifa_dstaddr);
        db_printf("  ifa_mask=");
        db_print_sa(ifa->ifa_netmask);
-       db_printf("  flags=0x%x, refcnt=%d, metric=%d\n",
-           ifa->ifa_flags, ifa->ifa_refcnt, ifa->ifa_metric);
+       db_printf("  flags=0x%x, refcnt=%u, metric=%d\n",
+           ifa->ifa_flags, ifa->ifa_refcnt.r_refs, ifa->ifa_metric);
 }
 
 /*
Index: net/rtsock.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/net/rtsock.c,v
retrieving revision 1.341
diff -u -p -r1.341 rtsock.c
--- net/rtsock.c        22 Aug 2022 21:18:48 -0000      1.341
+++ net/rtsock.c        23 Aug 2022 16:27:03 -0000
@@ -1115,8 +1115,7 @@ rtm_output(struct rt_msghdr *rtm, struct
                                ifp->if_rtrequest(ifp, RTM_DELETE, rt);
                                ifafree(rt->rt_ifa);
 
-                               ifa->ifa_refcnt++;
-                               rt->rt_ifa = ifa;
+                               rt->rt_ifa = ifaref(ifa);
                                rt->rt_ifidx = ifa->ifa_ifp->if_index;
                                /* recheck link state after ifp change */
                                rt_if_linkstate_change(rt, ifa->ifa_ifp,
Index: netinet/in.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/in.c,v
retrieving revision 1.175
diff -u -p -r1.175 in.c
--- netinet/in.c        6 Aug 2022 15:57:59 -0000       1.175
+++ netinet/in.c        23 Aug 2022 16:59:09 -0000
@@ -379,6 +379,7 @@ in_ioctl_set_ifaddr(u_long cmd, caddr_t 
        }
        if (ia == NULL) {
                ia = malloc(sizeof *ia, M_IFADDR, M_WAITOK | M_ZERO);
+               refcnt_init_trace(&ia->ia_ifa.ifa_refcnt, DT_REFCNT_IDX_IFADDR);
                ia->ia_addr.sin_family = AF_INET;
                ia->ia_addr.sin_len = sizeof(ia->ia_addr);
                ia->ia_ifa.ifa_addr = sintosa(&ia->ia_addr);
@@ -475,6 +476,8 @@ in_ioctl_change_ifaddr(u_long cmd, caddr
 
                if (ia == NULL) {
                        ia = malloc(sizeof *ia, M_IFADDR, M_WAITOK | M_ZERO);
+                       refcnt_init_trace(&ia->ia_ifa.ifa_refcnt,
+                           DT_REFCNT_IDX_IFADDR);
                        ia->ia_addr.sin_family = AF_INET;
                        ia->ia_addr.sin_len = sizeof(ia->ia_addr);
                        ia->ia_ifa.ifa_addr = sintosa(&ia->ia_addr);
@@ -745,7 +748,6 @@ in_purgeaddr(struct ifaddr *ifa)
 {
        struct ifnet *ifp = ifa->ifa_ifp;
        struct in_ifaddr *ia = ifatoia(ifa);
-       extern int ifatrash;
 
        NET_ASSERT_LOCKED();
 
@@ -760,7 +762,6 @@ in_purgeaddr(struct ifaddr *ifa)
                ia->ia_allhosts = NULL;
        }
 
-       ifatrash++;
        ia->ia_ifp = NULL;
        ifafree(&ia->ia_ifa);
 }
Index: netinet6/in6.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/in6.c,v
retrieving revision 1.247
diff -u -p -r1.247 in6.c
--- netinet6/in6.c      6 Aug 2022 15:57:59 -0000       1.247
+++ netinet6/in6.c      23 Aug 2022 16:55:17 -0000
@@ -647,6 +647,8 @@ in6_update_ifa(struct ifnet *ifp, struct
        if (ia6 == NULL) {
                hostIsNew = 1;
                ia6 = malloc(sizeof(*ia6), M_IFADDR, M_WAITOK | M_ZERO);
+               refcnt_init_trace(&ia6->ia_ifa.ifa_refcnt,
+                   DT_REFCNT_IDX_IFADDR);
                LIST_INIT(&ia6->ia6_memberships);
                /* Initialize the address and masks, and put time stamp */
                ia6->ia_ifa.ifa_addr = sin6tosa(&ia6->ia_addr);
@@ -938,7 +940,6 @@ void
 in6_unlink_ifa(struct in6_ifaddr *ia6, struct ifnet *ifp)
 {
        struct ifaddr *ifa = &ia6->ia_ifa;
-       extern int ifatrash;
        int plen;
 
        NET_ASSERT_LOCKED();
@@ -953,7 +954,6 @@ in6_unlink_ifa(struct in6_ifaddr *ia6, s
        rt_ifa_purge(ifa);
        ifa_del(ifp, ifa);
 
-       ifatrash++;
        ia6->ia_ifp = NULL;
        ifafree(&ia6->ia_ifa);
 }
Index: netinet6/nd6_nbr.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/nd6_nbr.c,v
retrieving revision 1.132
diff -u -p -r1.132 nd6_nbr.c
--- netinet6/nd6_nbr.c  8 Aug 2022 17:47:59 -0000       1.132
+++ netinet6/nd6_nbr.c  23 Aug 2022 16:27:11 -0000
@@ -1124,8 +1124,7 @@ nd6_dad_start(struct ifaddr *ifa)
         * first packet to be sent from the interface after interface
         * (re)initialization.
         */
-       dp->dad_ifa = ifa;
-       ifa->ifa_refcnt++;      /* just for safety */
+       dp->dad_ifa = ifaref(ifa);
        dp->dad_count = ip6_dad_count;
        dp->dad_ns_icount = dp->dad_na_icount = 0;
        dp->dad_ns_ocount = dp->dad_ns_tcount = 0;
Index: sys/refcnt.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/sys/refcnt.h,v
retrieving revision 1.7
diff -u -p -r1.7 refcnt.h
--- sys/refcnt.h        28 Jun 2022 09:32:28 -0000      1.7
+++ sys/refcnt.h        23 Aug 2022 16:54:02 -0000
@@ -43,8 +43,10 @@ void refcnt_finalize(struct refcnt *, co
 int    refcnt_shared(struct refcnt *);
 unsigned int   refcnt_read(struct refcnt *);
 
-#define DT_REFCNT_IDX_INPCB    1
-#define DT_REFCNT_IDX_TDB      2
+/* sorted alphabetically, keep in sync with dev/dt/dt_prov_static.c */
+#define DT_REFCNT_IDX_IFADDR   1
+#define DT_REFCNT_IDX_INPCB    2
+#define DT_REFCNT_IDX_TDB      3
 
 #endif /* _KERNEL */
 

Reply via email to