Author: rwatson
Date: Tue Jun 23 20:19:09 2009
New Revision: 194760
URL: http://svn.freebsd.org/changeset/base/194760

Log:
  Modify most routines returning 'struct ifaddr *' to return references
  rather than pointers, requiring callers to properly dispose of those
  references.  The following routines now return references:
  
    ifaddr_byindex
    ifa_ifwithaddr
    ifa_ifwithbroadaddr
    ifa_ifwithdstaddr
    ifa_ifwithnet
    ifaof_ifpforaddr
    ifa_ifwithroute
    ifa_ifwithroute_fib
    rt_getifa
    rt_getifa_fib
    IFP_TO_IA
    ip_rtaddr
    in6_ifawithifp
    in6ifa_ifpforlinklocal
    in6ifa_ifpwithaddr
    in6_ifadd
    carp_iamatch6
    ip6_getdstifaddr
  
  Remove unused macro which didn't have required referencing:
  
    IFP_TO_IA6
  
  This closes many small races in which changes to interface
  or address lists while an ifaddr was in use could lead to use of freed
  memory (etc).  In a few cases, add missing if_addr_list locking
  required to safely acquire references.
  
  Because of a lack of deep copying support, we accept a race in which
  an in6_ifaddr pointed to by mbuf tags and extracted with
  ip6_getdstifaddr() doesn't hold a reference while in transmit.  Once
  we have mbuf tag deep copy support, this can be fixed.
  
  Reviewed by:  bz
  Obtained from:        Apple, Inc. (portions)
  MFC after:    6 weeks (portions)

Modified:
  head/sys/contrib/rdma/rdma_addr.c
  head/sys/contrib/rdma/rdma_cma.c
  head/sys/net/if.c
  head/sys/net/route.c
  head/sys/net/rtsock.c
  head/sys/net80211/ieee80211.c
  head/sys/netinet/igmp.c
  head/sys/netinet/in.c
  head/sys/netinet/in_mcast.c
  head/sys/netinet/in_pcb.c
  head/sys/netinet/in_var.h
  head/sys/netinet/ip_carp.c
  head/sys/netinet/ip_divert.c
  head/sys/netinet/ip_icmp.c
  head/sys/netinet/ip_input.c
  head/sys/netinet/ip_mroute.c
  head/sys/netinet/ip_options.c
  head/sys/netinet/ip_output.c
  head/sys/netinet/tcp_input.c
  head/sys/netinet6/frag6.c
  head/sys/netinet6/icmp6.c
  head/sys/netinet6/in6.c
  head/sys/netinet6/in6_ifattach.c
  head/sys/netinet6/in6_pcb.c
  head/sys/netinet6/in6_src.c
  head/sys/netinet6/in6_var.h
  head/sys/netinet6/ip6_input.c
  head/sys/netinet6/ip6_output.c
  head/sys/netinet6/mld6.c
  head/sys/netinet6/nd6.c
  head/sys/netinet6/nd6_nbr.c
  head/sys/netinet6/nd6_rtr.c
  head/sys/netinet6/raw_ip6.c
  head/sys/netipx/ipx_pcb.c

Modified: head/sys/contrib/rdma/rdma_addr.c
==============================================================================
--- head/sys/contrib/rdma/rdma_addr.c   Tue Jun 23 20:19:02 2009        
(r194759)
+++ head/sys/contrib/rdma/rdma_addr.c   Tue Jun 23 20:19:09 2009        
(r194760)
@@ -129,13 +129,16 @@ int rdma_translate_ip(struct sockaddr *a
        struct ifaddr *ifa;
        struct sockaddr_in *sin = (struct sockaddr_in *)addr;
        uint16_t port = sin->sin_port;
+       int ret;
        
        sin->sin_port = 0;
        ifa = ifa_ifwithaddr(addr);
        sin->sin_port = port;
        if (!ifa)
                return (EADDRNOTAVAIL);
-       return rdma_copy_addr(dev_addr, ifa->ifa_ifp, NULL);
+       ret = rdma_copy_addr(dev_addr, ifa->ifa_ifp, NULL);
+       ifa_free(ifa);
+       return (ret);
 }
 
 static void queue_req(struct addr_req *req)

Modified: head/sys/contrib/rdma/rdma_cma.c
==============================================================================
--- head/sys/contrib/rdma/rdma_cma.c    Tue Jun 23 20:19:02 2009        
(r194759)
+++ head/sys/contrib/rdma/rdma_cma.c    Tue Jun 23 20:19:09 2009        
(r194760)
@@ -1337,6 +1337,7 @@ static int iw_conn_req_handler(struct iw
        }
        dev = ifa->ifa_ifp;
        ret = rdma_copy_addr(&conn_id->id.route.addr.dev_addr, dev, NULL);
+       ifa_free(ifa);
        if (ret) {
                cma_enable_remove(conn_id);
                rdma_destroy_id(new_cm_id);

Modified: head/sys/net/if.c
==============================================================================
--- head/sys/net/if.c   Tue Jun 23 20:19:02 2009        (r194759)
+++ head/sys/net/if.c   Tue Jun 23 20:19:09 2009        (r194760)
@@ -48,6 +48,7 @@
 #include <sys/socket.h>
 #include <sys/socketvar.h>
 #include <sys/protosw.h>
+#include <sys/kdb.h>
 #include <sys/kernel.h>
 #include <sys/lock.h>
 #include <sys/refcount.h>
@@ -261,6 +262,8 @@ ifaddr_byindex(u_short idx)
 
        IFNET_RLOCK();
        ifa = ifnet_byindex_locked(idx)->if_addr;
+       if (ifa != NULL)
+               ifa_ref(ifa);
        IFNET_RUNLOCK();
        return (ifa);
 }
@@ -1464,7 +1467,7 @@ ifa_free(struct ifaddr *ifa)
  */
 /*ARGSUSED*/
 static struct ifaddr *
-ifa_ifwithaddr_internal(struct sockaddr *addr)
+ifa_ifwithaddr_internal(struct sockaddr *addr, int getref)
 {
        INIT_VNET_NET(curvnet);
        struct ifnet *ifp;
@@ -1477,6 +1480,8 @@ ifa_ifwithaddr_internal(struct sockaddr 
                        if (ifa->ifa_addr->sa_family != addr->sa_family)
                                continue;
                        if (sa_equal(addr, ifa->ifa_addr)) {
+                               if (getref)
+                                       ifa_ref(ifa);
                                IF_ADDR_UNLOCK(ifp);
                                goto done;
                        }
@@ -1485,6 +1490,8 @@ ifa_ifwithaddr_internal(struct sockaddr 
                            ifa->ifa_broadaddr &&
                            ifa->ifa_broadaddr->sa_len != 0 &&
                            sa_equal(ifa->ifa_broadaddr, addr)) {
+                               if (getref)
+                                       ifa_ref(ifa);
                                IF_ADDR_UNLOCK(ifp);
                                goto done;
                        }
@@ -1501,14 +1508,14 @@ struct ifaddr *
 ifa_ifwithaddr(struct sockaddr *addr)
 {
 
-       return (ifa_ifwithaddr_internal(addr));
+       return (ifa_ifwithaddr_internal(addr, 1));
 }
 
 int
 ifa_ifwithaddr_check(struct sockaddr *addr)
 {
 
-       return (ifa_ifwithaddr_internal(addr) != NULL);
+       return (ifa_ifwithaddr_internal(addr, 0) != NULL);
 }
 
 /*
@@ -1532,6 +1539,7 @@ ifa_ifwithbroadaddr(struct sockaddr *add
                            ifa->ifa_broadaddr &&
                            ifa->ifa_broadaddr->sa_len != 0 &&
                            sa_equal(ifa->ifa_broadaddr, addr)) {
+                               ifa_ref(ifa);
                                IF_ADDR_UNLOCK(ifp);
                                goto done;
                        }
@@ -1565,6 +1573,7 @@ ifa_ifwithdstaddr(struct sockaddr *addr)
                                continue;
                        if (ifa->ifa_dstaddr != NULL &&
                            sa_equal(addr, ifa->ifa_dstaddr)) {
+                               ifa_ref(ifa);
                                IF_ADDR_UNLOCK(ifp);
                                goto done;
                        }
@@ -1587,7 +1596,7 @@ ifa_ifwithnet(struct sockaddr *addr)
        INIT_VNET_NET(curvnet);
        struct ifnet *ifp;
        struct ifaddr *ifa;
-       struct ifaddr *ifa_maybe = (struct ifaddr *) 0;
+       struct ifaddr *ifa_maybe = NULL;
        u_int af = addr->sa_family;
        char *addr_data = addr->sa_data, *cplim;
 
@@ -1602,8 +1611,10 @@ ifa_ifwithnet(struct sockaddr *addr)
        }
 
        /*
-        * Scan though each interface, looking for ones that have
-        * addresses in this address family.
+        * Scan though each interface, looking for ones that have addresses
+        * in this address family.  Maintain a reference on ifa_maybe once
+        * we find one, as we release the IF_ADDR_LOCK() that kept it stable
+        * when we move onto the next interface.
         */
        IFNET_RLOCK();
        TAILQ_FOREACH(ifp, &V_ifnet, if_link) {
@@ -1624,6 +1635,7 @@ next:                             continue;
                                 */
                                if (ifa->ifa_dstaddr != NULL &&
                                    sa_equal(addr, ifa->ifa_dstaddr)) {
+                                       ifa_ref(ifa);
                                        IF_ADDR_UNLOCK(ifp);
                                        goto done;
                                }
@@ -1634,6 +1646,7 @@ next:                             continue;
                                 */
                                if (ifa->ifa_claim_addr) {
                                        if ((*ifa->ifa_claim_addr)(ifa, addr)) {
+                                               ifa_ref(ifa);
                                                IF_ADDR_UNLOCK(ifp);
                                                goto done;
                                        }
@@ -1664,17 +1677,24 @@ next:                           continue;
                                 * before continuing to search
                                 * for an even better one.
                                 */
-                               if (ifa_maybe == 0 ||
+                               if (ifa_maybe == NULL ||
                                    rn_refines((caddr_t)ifa->ifa_netmask,
-                                   (caddr_t)ifa_maybe->ifa_netmask))
+                                   (caddr_t)ifa_maybe->ifa_netmask)) {
+                                       if (ifa_maybe != NULL)
+                                               ifa_free(ifa_maybe);
                                        ifa_maybe = ifa;
+                                       ifa_ref(ifa_maybe);
+                               }
                        }
                }
                IF_ADDR_UNLOCK(ifp);
        }
        ifa = ifa_maybe;
+       ifa_maybe = NULL;
 done:
        IFNET_RUNLOCK();
+       if (ifa_maybe != NULL)
+               ifa_free(ifa_maybe);
        return (ifa);
 }
 
@@ -1688,7 +1708,7 @@ ifaof_ifpforaddr(struct sockaddr *addr, 
        struct ifaddr *ifa;
        char *cp, *cp2, *cp3;
        char *cplim;
-       struct ifaddr *ifa_maybe = 0;
+       struct ifaddr *ifa_maybe = NULL;
        u_int af = addr->sa_family;
 
        if (af >= AF_MAX)
@@ -1697,7 +1717,7 @@ ifaof_ifpforaddr(struct sockaddr *addr, 
        TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
                if (ifa->ifa_addr->sa_family != af)
                        continue;
-               if (ifa_maybe == 0)
+               if (ifa_maybe == NULL)
                        ifa_maybe = ifa;
                if (ifa->ifa_netmask == 0) {
                        if (sa_equal(addr, ifa->ifa_addr) ||
@@ -1723,6 +1743,8 @@ ifaof_ifpforaddr(struct sockaddr *addr, 
        }
        ifa = ifa_maybe;
 done:
+       if (ifa != NULL)
+               ifa_ref(ifa);
        IF_ADDR_UNLOCK(ifp);
        return (ifa);
 }
@@ -1748,7 +1770,6 @@ link_rtrequest(int cmd, struct rtentry *
                return;
        ifa = ifaof_ifpforaddr(dst, ifp);
        if (ifa) {
-               ifa_ref(ifa);           /* XXX */
                oifa = rt->rt_ifa;
                rt->rt_ifa = ifa;
                ifa_free(oifa);

Modified: head/sys/net/route.c
==============================================================================
--- head/sys/net/route.c        Tue Jun 23 20:19:02 2009        (r194759)
+++ head/sys/net/route.c        Tue Jun 23 20:19:09 2009        (r194760)
@@ -559,6 +559,7 @@ rtredirect_fib(struct sockaddr *dst,
        struct ifaddr *ifa;
        struct radix_node_head *rnh;
 
+       ifa = NULL;
        rnh = rt_tables_get_rnh(fibnum, dst->sa_family);
        if (rnh == NULL) {
                error = EAFNOSUPPORT;
@@ -664,6 +665,8 @@ out:
        info.rti_info[RTAX_NETMASK] = netmask;
        info.rti_info[RTAX_AUTHOR] = src;
        rt_missmsg(RTM_REDIRECT, &info, flags, error);
+       if (ifa != NULL)
+               ifa_free(ifa);
 }
 
 int
@@ -693,6 +696,9 @@ rtioctl_fib(u_long req, caddr_t data, u_
 #endif /* INET */
 }
 
+/*
+ * For both ifa_ifwithroute() routines, 'ifa' is returned referenced.
+ */
 struct ifaddr *
 ifa_ifwithroute(int flags, struct sockaddr *dst, struct sockaddr *gateway)
 {
@@ -749,11 +755,13 @@ ifa_ifwithroute_fib(int flags, struct so
                default:
                        break;
                }
+               if (!not_found && rt->rt_ifa != NULL) {
+                       ifa = rt->rt_ifa;
+                       ifa_ref(ifa);
+               }
                RT_REMREF(rt);
                RT_UNLOCK(rt);
-               if (not_found)
-                       return (NULL);
-               if ((ifa = rt->rt_ifa) == NULL)
+               if (not_found || ifa == NULL)
                        return (NULL);
        }
        if (ifa->ifa_addr->sa_family != dst->sa_family) {
@@ -761,6 +769,8 @@ ifa_ifwithroute_fib(int flags, struct so
                ifa = ifaof_ifpforaddr(dst, ifa->ifa_ifp);
                if (ifa == NULL)
                        ifa = oifa;
+               else
+                       ifa_free(oifa);
        }
        return (ifa);
 }
@@ -819,6 +829,10 @@ rt_getifa(struct rt_addrinfo *info)
        return (rt_getifa_fib(info, 0));
 }
 
+/*
+ * Look up rt_addrinfo for a specific fib.  Note that if rti_ifa is defined,
+ * it will be referenced so the caller must free it.
+ */
 int
 rt_getifa_fib(struct rt_addrinfo *info, u_int fibnum)
 {
@@ -831,8 +845,10 @@ rt_getifa_fib(struct rt_addrinfo *info, 
         */
        if (info->rti_ifp == NULL && ifpaddr != NULL &&
            ifpaddr->sa_family == AF_LINK &&
-           (ifa = ifa_ifwithnet(ifpaddr)) != NULL)
+           (ifa = ifa_ifwithnet(ifpaddr)) != NULL) {
                info->rti_ifp = ifa->ifa_ifp;
+               ifa_free(ifa);
+       }
        if (info->rti_ifa == NULL && ifaaddr != NULL)
                info->rti_ifa = ifa_ifwithaddr(ifaaddr);
        if (info->rti_ifa == NULL) {
@@ -1123,12 +1139,19 @@ rtrequest1_fib(int req, struct rt_addrin
                    (gateway->sa_family != AF_UNSPEC) && (gateway->sa_family != 
AF_LINK))
                        senderr(EINVAL);
 
-               if (info->rti_ifa == NULL && (error = rt_getifa_fib(info, 
fibnum)))
-                       senderr(error);
+               if (info->rti_ifa == NULL) {
+                       error = rt_getifa_fib(info, fibnum);
+                       if (error)
+                               senderr(error);
+               } else
+                       ifa_ref(info->rti_ifa);
                ifa = info->rti_ifa;
                rt = uma_zalloc(V_rtzone, M_NOWAIT | M_ZERO);
-               if (rt == NULL)
+               if (rt == NULL) {
+                       if (ifa != NULL)
+                               ifa_free(ifa);
                        senderr(ENOBUFS);
+               }
                RT_LOCK_INIT(rt);
                rt->rt_flags = RTF_UP | flags;
                rt->rt_fibnum = fibnum;
@@ -1139,6 +1162,8 @@ rtrequest1_fib(int req, struct rt_addrin
                RT_LOCK(rt);
                if ((error = rt_setgate(rt, dst, gateway)) != 0) {
                        RT_LOCK_DESTROY(rt);
+                       if (ifa != NULL)
+                               ifa_free(ifa);
                        uma_zfree(V_rtzone, rt);
                        senderr(error);
                }
@@ -1157,11 +1182,10 @@ rtrequest1_fib(int req, struct rt_addrin
                        bcopy(dst, ndst, dst->sa_len);
 
                /*
-                * Note that we now have a reference to the ifa.
+                * We use the ifa reference returned by rt_getifa_fib().
                 * This moved from below so that rnh->rnh_addaddr() can
                 * examine the ifa and  ifa->ifa_ifp if it so desires.
                 */
-               ifa_ref(ifa);
                rt->rt_ifa = ifa;
                rt->rt_ifp = ifa->ifa_ifp;
                rt->rt_rmx.rmx_weight = 1;

Modified: head/sys/net/rtsock.c
==============================================================================
--- head/sys/net/rtsock.c       Tue Jun 23 20:19:02 2009        (r194759)
+++ head/sys/net/rtsock.c       Tue Jun 23 20:19:09 2009        (r194760)
@@ -683,6 +683,13 @@ route_output(struct mbuf *m, struct sock
                                RT_UNLOCK(rt);
                                RADIX_NODE_HEAD_LOCK(rnh);
                                error = rt_getifa_fib(&info, rt->rt_fibnum);
+                               /*
+                                * XXXRW: Really we should release this
+                                * reference later, but this maintains
+                                * historical behavior.
+                                */
+                               if (info.rti_ifa != NULL)
+                                       ifa_free(info.rti_ifa);
                                RADIX_NODE_HEAD_UNLOCK(rnh);
                                if (error != 0)
                                        senderr(error);

Modified: head/sys/net80211/ieee80211.c
==============================================================================
--- head/sys/net80211/ieee80211.c       Tue Jun 23 20:19:02 2009        
(r194759)
+++ head/sys/net80211/ieee80211.c       Tue Jun 23 20:19:09 2009        
(r194760)
@@ -301,6 +301,7 @@ ieee80211_ifattach(struct ieee80211com *
        sdl->sdl_type = IFT_ETHER;              /* XXX IFT_IEEE80211? */
        sdl->sdl_alen = IEEE80211_ADDR_LEN;
        IEEE80211_ADDR_COPY(LLADDR(sdl), macaddr);
+       ifa_free(ifa);
 }
 
 /*

Modified: head/sys/netinet/igmp.c
==============================================================================
--- head/sys/netinet/igmp.c     Tue Jun 23 20:19:02 2009        (r194759)
+++ head/sys/netinet/igmp.c     Tue Jun 23 20:19:09 2009        (r194760)
@@ -1233,8 +1233,10 @@ igmp_input_v1_report(struct ifnet *ifp, 
         */
        if (V_igmp_recvifkludge && in_nullhost(ip->ip_src)) {
                IFP_TO_IA(ifp, ia);
-               if (ia != NULL)
+               if (ia != NULL) {
                        ip->ip_src.s_addr = htonl(ia->ia_subnet);
+                       ifa_free(&ia->ia_ifa);
+               }
        }
 
        CTR3(KTR_IGMPV3, "process v1 report %s on ifp %p(%s)",
@@ -1326,16 +1328,23 @@ igmp_input_v2_report(struct ifnet *ifp, 
         * group.
         */
        IFP_TO_IA(ifp, ia);
-       if (ia != NULL && in_hosteq(ip->ip_src, IA_SIN(ia)->sin_addr))
+       if (ia != NULL && in_hosteq(ip->ip_src, IA_SIN(ia)->sin_addr)) {
+               ifa_free(&ia->ia_ifa);
                return (0);
+       }
 
        IGMPSTAT_INC(igps_rcv_reports);
 
-       if (ifp->if_flags & IFF_LOOPBACK)
+       if (ifp->if_flags & IFF_LOOPBACK) {
+               if (ia != NULL)
+                       ifa_free(&ia->ia_ifa);
                return (0);
+       }
 
        if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr)) ||
            !in_hosteq(igmp->igmp_group, ip->ip_dst)) {
+               if (ia != NULL)
+                       ifa_free(&ia->ia_ifa);
                IGMPSTAT_INC(igps_rcv_badreports);
                return (EINVAL);
        }
@@ -1351,6 +1360,8 @@ igmp_input_v2_report(struct ifnet *ifp, 
                if (ia != NULL)
                        ip->ip_src.s_addr = htonl(ia->ia_subnet);
        }
+       if (ia != NULL)
+               ifa_free(&ia->ia_ifa);
 
        CTR3(KTR_IGMPV3, "process v2 report %s on ifp %p(%s)",
             inet_ntoa(igmp->igmp_group), ifp, ifp->if_xname);
@@ -3534,8 +3545,10 @@ igmp_v3_encap_report(struct ifnet *ifp, 
                struct in_ifaddr *ia;
 
                IFP_TO_IA(ifp, ia);
-               if (ia != NULL)
+               if (ia != NULL) {
                        ip->ip_src = ia->ia_addr.sin_addr;
+                       ifa_free(&ia->ia_ifa);
+               }
        }
 
        ip->ip_dst.s_addr = htonl(INADDR_ALLRPTS_GROUP);

Modified: head/sys/netinet/in.c
==============================================================================
--- head/sys/netinet/in.c       Tue Jun 23 20:19:02 2009        (r194759)
+++ head/sys/netinet/in.c       Tue Jun 23 20:19:09 2009        (r194760)
@@ -219,7 +219,6 @@ in_control(struct socket *so, u_long cmd
        register struct ifaddr *ifa;
        struct in_addr allhosts_addr;
        struct in_addr dst;
-       struct in_ifaddr *oia;
        struct in_ifinfo *ii;
        struct in_aliasreq *ifra = (struct in_aliasreq *)data;
        struct sockaddr_in oldaddr;
@@ -323,8 +322,10 @@ in_control(struct socket *so, u_long cmd
                        break;
                }
        }
-       IF_ADDR_LOCK(ifp);
+       if (ia != NULL)
+               ifa_ref(&ia->ia_ifa);
        if (ia == NULL) {
+               IF_ADDR_LOCK(ifp);
                TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
                        iap = ifatoia(ifa);
                        if (iap->ia_addr.sin_family == AF_INET) {
@@ -336,6 +337,9 @@ in_control(struct socket *so, u_long cmd
                                break;
                        }
                }
+               if (ia != NULL)
+                       ifa_ref(&ia->ia_ifa);
+               IF_ADDR_UNLOCK(ifp);
        }
        if (ia == NULL)
                iaIsFirst = 1;
@@ -345,23 +349,29 @@ in_control(struct socket *so, u_long cmd
        case SIOCAIFADDR:
        case SIOCDIFADDR:
                if (ifra->ifra_addr.sin_family == AF_INET) {
+                       struct in_ifaddr *oia;
+
                        for (oia = ia; ia; ia = TAILQ_NEXT(ia, ia_link)) {
                                if (ia->ia_ifp == ifp  &&
                                    ia->ia_addr.sin_addr.s_addr ==
                                    ifra->ifra_addr.sin_addr.s_addr)
                                        break;
                        }
+                       if (ia != NULL && ia != oia)
+                               ifa_ref(&ia->ia_ifa);
+                       if (oia != NULL && ia != oia)
+                               ifa_free(&oia->ia_ifa);
                        if ((ifp->if_flags & IFF_POINTOPOINT)
                            && (cmd == SIOCAIFADDR)
                            && (ifra->ifra_dstaddr.sin_addr.s_addr
                                == INADDR_ANY)) {
                                error = EDESTADDRREQ;
-                               goto out_unlock;
+                               goto out;
                        }
                }
                if (cmd == SIOCDIFADDR && ia == NULL) {
                        error = EADDRNOTAVAIL;
-                       goto out_unlock;
+                       goto out;
                }
                /* FALLTHROUGH */
        case SIOCSIFADDR:
@@ -373,7 +383,7 @@ in_control(struct socket *so, u_long cmd
                                    M_ZERO);
                        if (ia == NULL) {
                                error = ENOBUFS;
-                               goto out_unlock;
+                               goto out;
                        }
 
                        ifa = &ia->ia_ifa;
@@ -390,7 +400,11 @@ in_control(struct socket *so, u_long cmd
                        }
                        ia->ia_ifp = ifp;
 
+                       ifa_ref(ifa);                   /* if_addrhead */
+                       IF_ADDR_LOCK(ifp);
                        TAILQ_INSERT_TAIL(&ifp->if_addrhead, ifa, ifa_link);
+                       IF_ADDR_UNLOCK(ifp);
+                       ifa_ref(ifa);                   /* in_ifaddrhead */
                        s = splnet();
                        TAILQ_INSERT_TAIL(&V_in_ifaddrhead, ia, ia_link);
                        splx(s);
@@ -405,64 +419,53 @@ in_control(struct socket *so, u_long cmd
        case SIOCGIFBRDADDR:
                if (ia == NULL) {
                        error = EADDRNOTAVAIL;
-                       goto out_unlock;
+                       goto out;
                }
                break;
        }
 
        /*
-        * Most paths in this switch return directly or via out_unlock.  Only
-        * paths that remove the address break in order to hit common removal
-        * code.
-        *
-        * XXXRW: We enter the switch with IF_ADDR_LOCK() held, but leave
-        * without it.  This is a bug.
+        * Most paths in this switch return directly or via out.  Only paths
+        * that remove the address break in order to hit common removal code.
         */
-       IF_ADDR_LOCK_ASSERT(ifp);
        switch (cmd) {
        case SIOCGIFADDR:
                *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_addr;
-               goto out_unlock;
+               goto out;
 
        case SIOCGIFBRDADDR:
                if ((ifp->if_flags & IFF_BROADCAST) == 0) {
                        error = EINVAL;
-                       goto out_unlock;
+                       goto out;
                }
                *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_broadaddr;
-               goto out_unlock;
+               goto out;
 
        case SIOCGIFDSTADDR:
                if ((ifp->if_flags & IFF_POINTOPOINT) == 0) {
                        error = EINVAL;
-                       goto out_unlock;
+                       goto out;
                }
                *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_dstaddr;
-               goto out_unlock;
+               goto out;
 
        case SIOCGIFNETMASK:
                *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_sockmask;
-               goto out_unlock;
+               goto out;
 
        case SIOCSIFDSTADDR:
                if ((ifp->if_flags & IFF_POINTOPOINT) == 0) {
                        error = EINVAL;
-                       goto out_unlock;
+                       goto out;
                }
                oldaddr = ia->ia_dstaddr;
                ia->ia_dstaddr = *(struct sockaddr_in *)&ifr->ifr_dstaddr;
-               IF_ADDR_UNLOCK(ifp);
-
-               /*
-                * XXXRW: Locks dropped for if_ioctl and rtinit, but ia is
-                * still being used.
-                */
                if (ifp->if_ioctl != NULL) {
                        error = (*ifp->if_ioctl)(ifp, SIOCSIFDSTADDR,
                            (caddr_t)ia);
                        if (error) {
                                ia->ia_dstaddr = oldaddr;
-                               return (error);
+                               goto out;
                        }
                }
                if (ia->ia_flags & IFA_ROUTE) {
@@ -472,23 +475,17 @@ in_control(struct socket *so, u_long cmd
                                        (struct sockaddr *)&ia->ia_dstaddr;
                        rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP);
                }
-               return (0);
+               goto out;
 
        case SIOCSIFBRDADDR:
                if ((ifp->if_flags & IFF_BROADCAST) == 0) {
                        error = EINVAL;
-                       goto out_unlock;
+                       goto out;
                }
                ia->ia_broadaddr = *(struct sockaddr_in *)&ifr->ifr_broadaddr;
-               goto out_unlock;
+               goto out;
 
        case SIOCSIFADDR:
-               IF_ADDR_UNLOCK(ifp);
-
-               /*
-                * XXXRW: Locks dropped for in_ifinit and in_joingroup, but ia
-                * is still being used.
-                */
                error = in_ifinit(ifp, ia,
                    (struct sockaddr_in *) &ifr->ifr_addr, 1);
                if (error != 0 && iaIsNew)
@@ -502,12 +499,13 @@ in_control(struct socket *so, u_long cmd
                        }
                        EVENTHANDLER_INVOKE(ifaddr_event, ifp);
                }
-               return (0);
+               error = 0;
+               goto out;
 
        case SIOCSIFNETMASK:
                ia->ia_sockmask.sin_addr = ifra->ifra_addr.sin_addr;
                ia->ia_subnetmask = ntohl(ia->ia_sockmask.sin_addr.s_addr);
-               goto out_unlock;
+               goto out;
 
        case SIOCAIFADDR:
                maskIsNew = 0;
@@ -521,12 +519,6 @@ in_control(struct socket *so, u_long cmd
                                               ia->ia_addr.sin_addr.s_addr)
                                hostIsNew = 0;
                }
-               IF_ADDR_UNLOCK(ifp);
-
-               /*
-                * XXXRW: Locks dropped for in_ifscrub and in_ifinit, but ia
-                * is still being used.
-                */
                if (ifra->ifra_mask.sin_len) {
                        in_ifscrub(ifp, ia);
                        ia->ia_sockmask = ifra->ifra_mask;
@@ -545,7 +537,7 @@ in_control(struct socket *so, u_long cmd
                    (hostIsNew || maskIsNew))
                        error = in_ifinit(ifp, ia, &ifra->ifra_addr, 0);
                if (error != 0 && iaIsNew)
-                       break;
+                       goto out;
 
                if ((ifp->if_flags & IFF_BROADCAST) &&
                    (ifra->ifra_broadaddr.sin_family == AF_INET))
@@ -559,15 +551,10 @@ in_control(struct socket *so, u_long cmd
                        }
                        EVENTHANDLER_INVOKE(ifaddr_event, ifp);
                }
-               return (error);
+               goto out;
 
        case SIOCDIFADDR:
-               IF_ADDR_UNLOCK(ifp);
-
                /*
-                * XXXRW: Locks dropped for in_ifscrub and in_ifadown, but ia
-                * is still being used.
-                *
                 * in_ifscrub kills the interface route.
                 */
                in_ifscrub(ifp, ia);
@@ -587,25 +574,25 @@ in_control(struct socket *so, u_long cmd
                panic("in_control: unsupported ioctl");
        }
 
-       /*
-        * XXXRW: In a more ideal world, we would still be holding
-        * IF_ADDR_LOCK here.
-        */
        IF_ADDR_LOCK(ifp);
        TAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifa_link);
        IF_ADDR_UNLOCK(ifp);
+       ifa_free(&ia->ia_ifa);                          /* if_addrhead */
        s = splnet();
        TAILQ_REMOVE(&V_in_ifaddrhead, ia, ia_link);
+       ifa_free(&ia->ia_ifa);                          /* in_ifaddrhead */
        if (ia->ia_addr.sin_family == AF_INET) {
+               struct in_ifaddr *if_ia;
+
                LIST_REMOVE(ia, ia_hash);
                /*
                 * If this is the last IPv4 address configured on this
                 * interface, leave the all-hosts group.
                 * No state-change report need be transmitted.
                 */
-               oia = NULL;
-               IFP_TO_IA(ifp, oia);
-               if (oia == NULL) {
+               if_ia = NULL;
+               IFP_TO_IA(ifp, if_ia);
+               if (if_ia == NULL) {
                        ii = ((struct in_ifinfo *)ifp->if_afdata[AF_INET]);
                        IN_MULTI_LOCK();
                        if (ii->ii_allhosts) {
@@ -614,15 +601,13 @@ in_control(struct socket *so, u_long cmd
                                ii->ii_allhosts = NULL;
                        }
                        IN_MULTI_UNLOCK();
-               }
+               } else
+                       ifa_free(&if_ia->ia_ifa);
        }
-       ifa_free(&ia->ia_ifa);
        splx(s);
-
-       return (error);
-
-out_unlock:
-       IF_ADDR_UNLOCK(ifp);
+out:
+       if (ia != NULL)
+               ifa_free(&ia->ia_ifa);
        return (error);
 }
 

Modified: head/sys/netinet/in_mcast.c
==============================================================================
--- head/sys/netinet/in_mcast.c Tue Jun 23 20:19:02 2009        (r194759)
+++ head/sys/netinet/in_mcast.c Tue Jun 23 20:19:09 2009        (r194760)
@@ -1722,6 +1722,7 @@ inp_getmoptions(struct inpcb *inp, struc
                                if (ia != NULL) {
                                        mreqn.imr_address =
                                            IA_SIN(ia)->sin_addr;
+                                       ifa_free(&ia->ia_ifa);
                                }
                        }
                }

Modified: head/sys/netinet/in_pcb.c
==============================================================================
--- head/sys/netinet/in_pcb.c   Tue Jun 23 20:19:02 2009        (r194759)
+++ head/sys/netinet/in_pcb.c   Tue Jun 23 20:19:09 2009        (r194760)
@@ -549,7 +549,6 @@ static int
 in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr,
     struct ucred *cred)
 {
-       struct in_ifaddr *ia;
        struct ifaddr *ifa;
        struct sockaddr *sa;
        struct sockaddr_in *sin;
@@ -559,7 +558,6 @@ in_pcbladdr(struct inpcb *inp, struct in
        KASSERT(laddr != NULL, ("%s: laddr NULL", __func__));
 
        error = 0;
-       ia = NULL;
        bzero(&sro, sizeof(sro));
 
        sin = (struct sockaddr_in *)&sro.ro_dst;
@@ -585,6 +583,7 @@ in_pcbladdr(struct inpcb *inp, struct in
         * the source address from.
         */
        if (sro.ro_rt == NULL || sro.ro_rt->rt_ifp == NULL) {
+               struct in_ifaddr *ia;
                struct ifnet *ifp;
 
                ia = ifatoia(ifa_ifwithdstaddr((struct sockaddr *)sin));
@@ -597,10 +596,12 @@ in_pcbladdr(struct inpcb *inp, struct in
 
                if (cred == NULL || !prison_flag(cred, PR_IP4)) {
                        laddr->s_addr = ia->ia_addr.sin_addr.s_addr;
+                       ifa_free(&ia->ia_ifa);
                        goto done;
                }
 
                ifp = ia->ia_ifp;
+               ifa_free(&ia->ia_ifa);
                ia = NULL;
                IF_ADDR_LOCK(ifp);
                TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
@@ -636,6 +637,7 @@ in_pcbladdr(struct inpcb *inp, struct in
         * 3. as a last resort return the 'default' jail address.
         */
        if ((sro.ro_rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0) {
+               struct in_ifaddr *ia;
                struct ifnet *ifp;
 
                /* If not jailed, use the default returned. */
@@ -658,10 +660,10 @@ in_pcbladdr(struct inpcb *inp, struct in
                 * 2. Check if we have any address on the outgoing interface
                 *    belonging to this jail.
                 */
+               ia = NULL;
                ifp = sro.ro_rt->rt_ifp;
                IF_ADDR_LOCK(ifp);
                TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
-
                        sa = ifa->ifa_addr;
                        if (sa->sa_family != AF_INET)
                                continue;
@@ -694,6 +696,7 @@ in_pcbladdr(struct inpcb *inp, struct in
         */
        if ((sro.ro_rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0) {
                struct sockaddr_in sain;
+               struct in_ifaddr *ia;
 
                bzero(&sain, sizeof(struct sockaddr_in));
                sain.sin_family = AF_INET;
@@ -710,6 +713,7 @@ in_pcbladdr(struct inpcb *inp, struct in
                                goto done;
                        }
                        laddr->s_addr = ia->ia_addr.sin_addr.s_addr;
+                       ifa_free(&ia->ia_ifa);
                        goto done;
                }
 
@@ -718,6 +722,7 @@ in_pcbladdr(struct inpcb *inp, struct in
                        struct ifnet *ifp;
 
                        ifp = ia->ia_ifp;
+                       ifa_free(&ia->ia_ifa);
                        ia = NULL;
                        IF_ADDR_LOCK(ifp);
                        TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {

Modified: head/sys/netinet/in_var.h
==============================================================================
--- head/sys/netinet/in_var.h   Tue Jun 23 20:19:02 2009        (r194759)
+++ head/sys/netinet/in_var.h   Tue Jun 23 20:19:09 2009        (r194760)
@@ -146,14 +146,16 @@ do { \
  * Macro for finding the internet address structure (in_ifaddr) corresponding
  * to a given interface (ifnet structure).
  */
-#define IFP_TO_IA(ifp, ia) \
-       /* struct ifnet *ifp; */ \
-       /* struct in_ifaddr *ia; */ \
-{ \
-       for ((ia) = TAILQ_FIRST(&V_in_ifaddrhead); \
-           (ia) != NULL && (ia)->ia_ifp != (ifp); \
-           (ia) = TAILQ_NEXT((ia), ia_link)) \
-               continue; \
+#define IFP_TO_IA(ifp, ia)                                             \
+       /* struct ifnet *ifp; */                                        \
+       /* struct in_ifaddr *ia; */                                     \
+{                                                                      \
+       for ((ia) = TAILQ_FIRST(&V_in_ifaddrhead);                      \
+           (ia) != NULL && (ia)->ia_ifp != (ifp);                      \
+           (ia) = TAILQ_NEXT((ia), ia_link))                           \
+               continue;                                               \
+       if ((ia) != NULL)                                               \
+               ifa_ref(&(ia)->ia_ifa);                                 \
 }
 #endif
 

Modified: head/sys/netinet/ip_carp.c
==============================================================================
--- head/sys/netinet/ip_carp.c  Tue Jun 23 20:19:02 2009        (r194759)
+++ head/sys/netinet/ip_carp.c  Tue Jun 23 20:19:09 2009        (r194760)
@@ -1239,6 +1239,7 @@ carp_iamatch6(void *v, struct in6_addr *
                            (SC2IFP(vh)->if_flags & IFF_UP) &&
                            (SC2IFP(vh)->if_drv_flags & IFF_DRV_RUNNING) &&
                            vh->sc_state == MASTER) {
+                               ifa_ref(ifa);
                                IF_ADDR_UNLOCK(SC2IFP(vh));
                                CARP_UNLOCK(cif);
                                return (ifa);

Modified: head/sys/netinet/ip_divert.c
==============================================================================
--- head/sys/netinet/ip_divert.c        Tue Jun 23 20:19:02 2009        
(r194759)
+++ head/sys/netinet/ip_divert.c        Tue Jun 23 20:19:09 2009        
(r194760)
@@ -464,6 +464,7 @@ div_output(struct socket *so, struct mbu
                                goto cantsend;
                        }
                        m->m_pkthdr.rcvif = ifa->ifa_ifp;
+                       ifa_free(ifa);
                }
 #ifdef MAC
                mac_socket_create_mbuf(so, m);

Modified: head/sys/netinet/ip_icmp.c
==============================================================================
--- head/sys/netinet/ip_icmp.c  Tue Jun 23 20:19:02 2009        (r194759)
+++ head/sys/netinet/ip_icmp.c  Tue Jun 23 20:19:09 2009        (r194760)
@@ -536,10 +536,12 @@ icmp_input(struct mbuf *m, int off)
                }
                ia = (struct in_ifaddr *)ifaof_ifpforaddr(
                            (struct sockaddr *)&icmpdst, m->m_pkthdr.rcvif);
-               if (ia == 0)
+               if (ia == NULL)
                        break;
-               if (ia->ia_ifp == 0)
+               if (ia->ia_ifp == NULL) {
+                       ifa_free(&ia->ia_ifa);
                        break;
+               }
                icp->icmp_type = ICMP_MASKREPLY;
                if (V_icmpmaskfake == 0)
                        icp->icmp_mask = ia->ia_sockmask.sin_addr.s_addr;
@@ -551,6 +553,7 @@ icmp_input(struct mbuf *m, int off)
                        else if (ia->ia_ifp->if_flags & IFF_POINTOPOINT)
                            ip->ip_src = satosin(&ia->ia_dstaddr)->sin_addr;
                }
+               ifa_free(&ia->ia_ifa);
 reflect:
                ip->ip_len += hlen;     /* since ip_input deducts this */
                ICMPSTAT_INC(icps_reflect);
@@ -748,6 +751,7 @@ icmp_reflect(struct mbuf *m)
                goto done;
        }
        t = IA_SIN(ia)->sin_addr;
+       ifa_free(&ia->ia_ifa);
 match:
 #ifdef MAC
        mac_netinet_icmp_replyinplace(m);

Modified: head/sys/netinet/ip_input.c
==============================================================================
--- head/sys/netinet/ip_input.c Tue Jun 23 20:19:02 2009        (r194759)
+++ head/sys/netinet/ip_input.c Tue Jun 23 20:19:09 2009        (r194760)
@@ -622,8 +622,10 @@ passin:
                 * enabled.
                 */
                if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr && 
-                   (!checkif || ia->ia_ifp == ifp))
+                   (!checkif || ia->ia_ifp == ifp)) {
+                       ifa_ref(&ia->ia_ifa);
                        goto ours;
+               }
        }
        /*
         * Check for broadcast addresses.
@@ -641,15 +643,18 @@ passin:
                        ia = ifatoia(ifa);
                        if (satosin(&ia->ia_broadaddr)->sin_addr.s_addr ==
                            ip->ip_dst.s_addr) {
+                               ifa_ref(ifa);
                                IF_ADDR_UNLOCK(ifp);
                                goto ours;
                        }
                        if (ia->ia_netbroadcast.s_addr == ip->ip_dst.s_addr) {
+                               ifa_ref(ifa);
                                IF_ADDR_UNLOCK(ifp);
                                goto ours;
                        }
 #ifdef BOOTP_COMPAT
                        if (IA_SIN(ia)->sin_addr.s_addr == INADDR_ANY) {
+                               ifa_ref(ifa);
                                IF_ADDR_UNLOCK(ifp);
                                goto ours;
                        }
@@ -742,6 +747,7 @@ ours:
        if (ia != NULL) {
                ia->ia_ifa.if_ipackets++;
                ia->ia_ifa.if_ibytes += m->m_pkthdr.len;
+               ifa_free(&ia->ia_ifa);
        }
 
        /*
@@ -1335,8 +1341,8 @@ ipproto_unregister(u_char ipproto)
 }
 
 /*
- * Given address of next destination (final or next hop),
- * return internet address info of interface to be used to get there.
+ * Given address of next destination (final or next hop), return (referenced)
+ * internet address info of interface to be used to get there.
  */
 struct in_ifaddr *
 ip_rtaddr(struct in_addr dst, u_int fibnum)
@@ -1356,6 +1362,7 @@ ip_rtaddr(struct in_addr dst, u_int fibn
                return (NULL);
 
        ifa = ifatoia(sro.ro_rt->rt_ifa);
+       ifa_ref(&ifa->ia_ifa);
        RTFREE(sro.ro_rt);
        return (ifa);
 }
@@ -1530,11 +1537,16 @@ ip_forward(struct mbuf *m, int srcrt)
                else {
                        if (mcopy)
                                m_freem(mcopy);
+                       if (ia != NULL)
+                               ifa_free(&ia->ia_ifa);
                        return;
                }
        }
-       if (mcopy == NULL)
+       if (mcopy == NULL) {
+               if (ia != NULL)
+                       ifa_free(&ia->ia_ifa);
                return;
+       }
 
        switch (error) {
 
@@ -1592,6 +1604,8 @@ ip_forward(struct mbuf *m, int srcrt)
                 */
                if (V_ip_sendsourcequench == 0) {
                        m_freem(mcopy);
+                       if (ia != NULL)
+                               ifa_free(&ia->ia_ifa);
                        return;
                } else {
                        type = ICMP_SOURCEQUENCH;
@@ -1601,8 +1615,12 @@ ip_forward(struct mbuf *m, int srcrt)
 
        case EACCES:                    /* ipfw denied packet */
                m_freem(mcopy);
+               if (ia != NULL)
+                       ifa_free(&ia->ia_ifa);
                return;
        }
+       if (ia != NULL)
+               ifa_free(&ia->ia_ifa);
        icmp_error(mcopy, type, code, dest.s_addr, mtu);
 }
 

Modified: head/sys/netinet/ip_mroute.c
==============================================================================
--- head/sys/netinet/ip_mroute.c        Tue Jun 23 20:19:02 2009        
(r194759)
+++ head/sys/netinet/ip_mroute.c        Tue Jun 23 20:19:09 2009        
(r194760)
@@ -883,6 +883,7 @@ add_vif(struct vifctl *vifcp)
            return EADDRNOTAVAIL;
        }
        ifp = ifa->ifa_ifp;
+       ifa_free(ifa);
     }
 
     if ((vifcp->vifc_flags & VIFF_TUNNEL) != 0) {

Modified: head/sys/netinet/ip_options.c
==============================================================================
--- head/sys/netinet/ip_options.c       Tue Jun 23 20:19:02 2009        
(r194759)

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to