Bug 126075

Summary: [inet] [patch] internet control accesses beyond end of structure
Product: Base System Reporter: Steve Sears <sjs>
Component: kernAssignee: freebsd-bugs (Nobody) <bugs>
Status: Open ---    
Severity: Affects Only Me Keywords: patch
Priority: Normal    
Version: Unspecified   
Hardware: Any   
OS: Any   

Description Steve Sears 2008-07-29 15:10:03 UTC
In the kernel in sys/netinet/in.c: in_control() we have the following code, marked at the problem area:

     * If an alias address was specified, find that one instead of
     * the first one on the interface, if possible.
     */
    if (ifp) {
        dst = ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr;
        LIST_FOREACH(iap, INADDR_HASH(dst.s_addr), ia_hash)
            if (iap->ia_ifp == ifp &&
                iap->ia_addr.sin_addr.s_addr == dst.s_addr) {
                ia = iap;
                break;
            }
        if (ia == NULL)
            TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
->              iap = ifatoia(ifa);
->              if (iap->ia_addr.sin_family == AF_INET) {
->                  ia = iap;
->                  break;
                }
            }
    }

The macro ifatoia() is a simple cast:

#define ifatoia(ifa)    ((struct in_ifaddr *)(ifa))

Now look at the structure def for in_ifaddr:

struct in_ifaddr {
        struct  ifaddr ia_ifa;          /* protocol-independent info */
#define ia_ifp          ia_ifa.ifa_ifp
#define ia_flags        ia_ifa.ifa_flags
                                        /* ia_{,sub}net{,mask} in host order */
        u_long  ia_net;                 /* network number of interface */
        u_long  ia_netmask;             /* mask of net part */
        u_long  ia_subnet;              /* subnet number, including net */
        u_long  ia_subnetmask;          /* mask of subnet part */
        struct  in_addr ia_netbroadcast; /* to recognize net broadcasts */
        LIST_ENTRY(in_ifaddr) ia_hash;  /* entry in bucket of inet addresses */
        TAILQ_ENTRY(in_ifaddr) ia_link; /* list of internet addresses */
        struct  sockaddr_in ia_addr;    /* reserve space for interface name */
        struct  sockaddr_in ia_dstaddr; /* reserve space for broadcast addr */
#define ia_broadaddr    ia_dstaddr
        struct  sockaddr_in ia_sockmask; /* reserve space for general netmask */
};

All of the internet types include an ifaddr at the beginning, but it accessing beyond this struct after a cast is not a safe operation.

Fix: 

The problem is that ia_addr is not part of the overloaded ifaddr structure, so this check falls off the end of the structure in all but the in_ifaddr case. The corrected code should be:

        if (ia == NULL)
            TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
+               if (ifa->ifa_addr->sa_family == AF_INET) {
+                   ia = ifatoia(ifa);
+                   break;
+               }

This prevents bogus data being returned due to a cast allowing the code to walk off the end of valid data.
How-To-Repeat: Use non AF_INET network interfaces.
Comment 1 Gavin Atkinson freebsd_committer freebsd_triage 2008-07-29 21:30:36 UTC
Responsible Changed
From-To: freebsd-bugs->freebsd-net

Over to maintainers
Comment 2 Eitan Adler freebsd_committer freebsd_triage 2017-12-31 07:59:47 UTC
For bugs matching the following criteria:

Status: In Progress Changed: (is less than) 2014-06-01

Reset to default assignee and clear in-progress tags.

Mail being skipped
Comment 3 Graham Perrin freebsd_committer freebsd_triage 2022-10-17 12:40:58 UTC
Keyword: 

    patch
or  patch-ready

– in lieu of summary line prefix: 

    [patch]

* bulk change for the keyword
* summary lines may be edited manually (not in bulk). 

Keyword descriptions and search interface: 

    <https://bugs.freebsd.org/bugzilla/describekeywords.cgi>