IN6_IS_ADDR_* macros use invalid type punning?

Matthias Andree mandree at FreeBSD.org
Thu Jun 6 20:23:32 UTC 2013


-- NOTE -- Please Cc: me on replies, I am not subscribed to freebsd-net.


Greetings,

I am just staring at gcc 4.8 warnings when compiling the try.c code
shown below:

----------------------------------------------------------------
$ cat try.c
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>

int f(void) {
    struct sockaddr_in6 sin6;

    int r = IN6_IS_ADDR_V4MAPPED((&sin6.sin6_addr));
    return r;
}
----------------------------------------------------------------

----------------------------------------------------------------
$ gcc48 -Wall -O2 -c try.c
try.c: In function 'f':
try.c:9:5: warning: dereferencing type-punned pointer will break
strict-aliasing rules [-Wstrict-aliasing]
     int r = IN6_IS_ADDR_V4MAPPED((&sin6.sin6_addr));
     ^
try.c:9:5: warning: dereferencing type-punned pointer will break
strict-aliasing rules [-Wstrict-aliasing]
try.c:9:5: warning: dereferencing type-punned pointer will break
strict-aliasing rules [-Wstrict-aliasing]
----------------------------------------------------------------

This is on FreeBSD 9.1-RELEASE amd64.

/usr/include/netinet6/in6.h contains:

/*
 * Mapped
 */
#define IN6_IS_ADDR_V4MAPPED(a)               \
        ((*(const u_int32_t *)(const void *)(&(a)->s6_addr[0]) == 0) && \
         (*(const u_int32_t *)(const void *)(&(a)->s6_addr[4]) == 0) && \
         (*(const u_int32_t *)(const void *)(&(a)->s6_addr[8]) ==
ntohl(0x0000ffff)))


So we indeed break C99/C11 aliasing rules here.


Pedantically speaking, it would also have to be htonl() instead of
ntohl() because the in6_addr is in network order, but it does not matter
to the machine because either maps to __bswap32() anyway.




The same in6.h also declares:

struct in6_addr {
        union {
                uint8_t         __u6_addr8[16];
                uint16_t        __u6_addr16[8];
                uint32_t        __u6_addr32[4];
        } __u6_addr;                    /* 128-bit IP6 address */
};


Which would appear to open up an alias-safe way  (sanctioned by the C
standard, access through union containing the type) to reimplement the
macro, which also makes the GCC 4.8 warning go away and is far more
readable:


/*
 * Mapped
 */
#define IN6_IS_ADDR_V4MAPPED(a)	\
	((a)->__u6_addr.__u6_addr32[0] == 0 && \
         (a)->__u6_addr.__u6_addr32[1] == 0 && \
         (a)->__u6_addr.__u6_addr16[2] == htonl(0x0000ffff))


Similar considerations apply to the other IN6_IS_ADDR_* macros.


Now, what do we do?

Can we get these fixed in a reasonable timeframe?

To whom, or where, would I submit a patch for all the macros so that it
actually gets committed to HEAD and MFC'd to /9 and /8?

Do we have unit tests for these macros?  Should we add some?


Best
Matthias
(I don't have src commit permission.)
-- NOTE -- Please Cc: me on replies, I am not subscribed to freebsd-net.


More information about the freebsd-net mailing list