getifaddrs returns truncated sockaddr_in’s for AF_INET ifa_netmasks

Apple “Feedback” #12149675.

Some netmasks returned by getifaddrs have family of AF_INET yet a length less than sizeof(sockaddr_in), e.g. 5, 6, 7, or 8. On macOS Ventura 13.3.1, at least.

It looks like it’s actually allocating only eight bytes for the ifa_netmask (not the 16 that is the size of sockaddr_in per MacOSX13.3.sdk/usr/include/netinet/in.h), as it clearly has a different sockaddr_in immediately after it, e.g.:

sockaddr
  - sa_len : 5
  - sa_family : 2
  ▿ sa_data : 14 elements
    - .0 : 0
    - .1 : 0
    - .2 : 255
    - .3 : 0
    - .4 : 0
    - .5 : 0
    - .6 : 16 // This is obviously the start of the next sockaddr_in; a correct entry with length of 16.
    - .7 : 2
    - .8 : 0
    - .9 : 0
    - .10 : 127
    - .11 : 0
    - .12 : 0
    - .13 : 1

It’s thus clear that it’s both wrong about the actual length in memory and it is indeed truncating the sockaddr_in structure, although it looks like it’s actually including the full address (the lo0 netmask in the above example is indeed 255.0.0.0).

Here’s another example, where the netmask is 255.255.255.0:

▿ sockaddr
  - sa_len : 7
  - sa_family : 2
  ▿ sa_data : 14 elements
    - .0 : 0
    - .1 : 0
    - .2 : 255
    - .3 : 255
    - .4 : 255
    - .5 : 0
    - .6 : 16 // Again, clearly the start of the next sockaddr_in.
    - .7 : 2
    - .8 : 0
    - .9 : 0
    - .10 : 192
    - .11 : 168
    - .12 : 0
    - .13 : 24

So you can see it’s:

  • Only allocating up to the address of sockaddr_in, not counting the padding (so sizeof(sockaddr_in), MemoryLayout<sockaddr_in>.size, etc disagree with what getifaddrs is doing), and
  • Weirdly truncating the stated length (sin_len) to the end of the non-zero bytes in the netmask.

This is undocumented (well, all these data structures are completely undocumented 😔) and is all very messy. getifaddrs should just provide the full sockaddr_in without any of this weirdness, so that users which blindly cast to sockaddr_in (without checking sin_len) don’t risk segmentation faults or reading corrupt data.

I have no idea why it does this only for ifa_netmask, not ifa_addr or ifa_dstaddr. That discrepancy just makes it even more troublesome.

As far as I can tell there are no similar issues with AF_INET6 netmasks, although without knowing under what criteria it’s truncating, I can’t be sure I have all relevant examples covered on the happenstance networks of my laptop.

For these systems, there’s seemingly not much you can do… it seems you have to hard-code the assumption that ifa_netmask contains exactly & only the first eight bytes of the sockaddr_in. You have to completely ignore sin_len (at least if it’s less than eight).

Leave a Comment