Dereferencing type-punned pointers is undefined behaviour according to the C standard. We should either obey the standard, or ensure that all supported compilers deal with dereferencing type-punned pointers as we want them to. I think just obeying the standard is the easiest solution.
See e.g. http://blog.regehr.org/archives/959. This commit refactors the offending code to use unions or memcpy() to comply to strict aliasing rules. Note that this also slightly changes mroute_addr_mask_host_bits(), to behave as it was probably intended to: only mask the address part, not also the port part of IPv6 adresses if MR_WITH_PORT is used (ie ma->len is sizeof(struct in6_addr)+2). v2: fix all strict aliasing occurrences, not just those in mroute.h v3: add missing ntohs() in mroute_addr_print_ex() Signed-off-by: Steffan Karger <stef...@karger.me> --- src/openvpn/error.h | 10 +++++++ src/openvpn/mroute.c | 85 +++++++++++++++++++++++++--------------------------- src/openvpn/mroute.h | 41 +++++++++++++++++++++---- src/openvpn/multi.c | 4 +-- src/openvpn/ps.c | 10 ++++--- src/openvpn/socket.c | 3 +- 6 files changed, 96 insertions(+), 57 deletions(-) diff --git a/src/openvpn/error.h b/src/openvpn/error.h index ced2fdf..c90b36b 100644 --- a/src/openvpn/error.h +++ b/src/openvpn/error.h @@ -27,6 +27,8 @@ #include "basic.h" +#include <assert.h> + /* #define ABORT_ON_ERROR */ #ifdef ENABLE_PKCS11 @@ -219,6 +221,14 @@ FILE *msg_fp(const unsigned int flags); void assert_failed (const char *filename, int line, const char *condition) __attribute__((__noreturn__)); +/* Poor-man's static_assert() for when not supplied by assert.h, taken from + * Linux's sys/cdefs.h under GPLv2 */ +#ifndef static_assert +#define static_assert(expr, diagnostic) \ + extern int (*__OpenVPN_static_assert_function (void)) \ + [!!sizeof (struct { int __error_if_negative: (expr) ? 2 : -1; })] +#endif + #ifdef ENABLE_DEBUG void crash (void); /* force a segfault (debugging only) */ #endif diff --git a/src/openvpn/mroute.c b/src/openvpn/mroute.c index 972f1dd..c905af7 100644 --- a/src/openvpn/mroute.c +++ b/src/openvpn/mroute.c @@ -58,7 +58,8 @@ is_mac_mcast_addr (const uint8_t *mac) static inline bool is_mac_mcast_maddr (const struct mroute_addr *addr) { - return (addr->type & MR_ADDR_MASK) == MR_ADDR_ETHER && is_mac_mcast_addr (addr->addr); + return (addr->type & MR_ADDR_MASK) == MR_ADDR_ETHER && + is_mac_mcast_addr (addr->eth_addr); } /* @@ -73,7 +74,7 @@ mroute_learnable_address (const struct mroute_addr *addr) for (i = 0; i < addr->len; ++i) { - int b = addr->addr[i]; + int b = addr->raw_addr[i]; if (b != 0x00) not_all_zeros = true; if (b != 0xFF) @@ -90,7 +91,7 @@ mroute_get_in_addr_t (struct mroute_addr *ma, const in_addr_t src, unsigned int ma->type = MR_ADDR_IPV4 | mask; ma->netbits = 0; ma->len = 4; - *(in_addr_t*)ma->addr = src; + ma->v4.addr = src; } } @@ -102,7 +103,7 @@ mroute_get_in6_addr (struct mroute_addr *ma, const struct in6_addr src, unsigned ma->type = MR_ADDR_IPV6 | mask; ma->netbits = 0; ma->len = 16; - *(struct in6_addr *)ma->addr = src; + ma->v6.addr = src; } } @@ -226,14 +227,14 @@ mroute_extract_addr_ether (struct mroute_addr *src, src->type = MR_ADDR_ETHER; src->netbits = 0; src->len = 6; - memcpy (src->addr, eth->source, 6); + memcpy (src->eth_addr, eth->source, sizeof(dest->eth_addr)); } if (dest) { dest->type = MR_ADDR_ETHER; dest->netbits = 0; dest->len = 6; - memcpy (dest->addr, eth->dest, 6); + memcpy (dest->eth_addr, eth->dest, sizeof(dest->eth_addr)); /* ethernet broadcast/multicast packet? */ if (is_mac_mcast_addr (eth->dest)) @@ -281,15 +282,15 @@ bool mroute_extract_openvpn_sockaddr (struct mroute_addr *addr, addr->type = MR_ADDR_IPV4 | MR_WITH_PORT; addr->netbits = 0; addr->len = 6; - memcpy (addr->addr, &osaddr->addr.in4.sin_addr.s_addr, 4); - memcpy (addr->addr + 4, &osaddr->addr.in4.sin_port, 2); + addr->v4.addr = osaddr->addr.in4.sin_addr.s_addr; + addr->v4.port = osaddr->addr.in4.sin_port; } else { addr->type = MR_ADDR_IPV4; addr->netbits = 0; addr->len = 4; - memcpy (addr->addr, &osaddr->addr.in4.sin_addr.s_addr, 4); + addr->v4.addr = osaddr->addr.in4.sin_addr.s_addr; } return true; } @@ -299,15 +300,15 @@ bool mroute_extract_openvpn_sockaddr (struct mroute_addr *addr, addr->type = MR_ADDR_IPV6 | MR_WITH_PORT; addr->netbits = 0; addr->len = 18; - memcpy (addr->addr, &osaddr->addr.in6.sin6_addr, 16); - memcpy (addr->addr + 16, &osaddr->addr.in6.sin6_port, 2); + addr->v6.addr = osaddr->addr.in6.sin6_addr; + addr->v6.port = osaddr->addr.in6.sin6_port; } else { addr->type = MR_ADDR_IPV6; addr->netbits = 0; addr->len = 16; - memcpy (addr->addr, &osaddr->addr.in6.sin6_addr, 16); + addr->v6.addr = osaddr->addr.in6.sin6_addr; } return true; } @@ -326,23 +327,29 @@ bool mroute_extract_openvpn_sockaddr (struct mroute_addr *addr, void mroute_addr_mask_host_bits (struct mroute_addr *ma) { - in_addr_t addr = ntohl(*(in_addr_t*)ma->addr); if ((ma->type & MR_ADDR_MASK) == MR_ADDR_IPV4) { + in_addr_t addr = ntohl (ma->v4.addr); addr &= netbits_to_netmask (ma->netbits); - *(in_addr_t*)ma->addr = htonl (addr); + ma->v4.addr = htonl (addr); } else if ((ma->type & MR_ADDR_MASK) == MR_ADDR_IPV6) { - int byte = ma->len-1; /* rightmost byte in address */ + int byte = sizeof (ma->v6.addr) - 1; /* rightmost byte in address */ int bits_to_clear = 128 - ma->netbits; while( byte >= 0 && bits_to_clear > 0 ) { if ( bits_to_clear >= 8 ) - { ma->addr[byte--] = 0; bits_to_clear -= 8; } + { + ma->v6.addr.s6_addr[byte--] = 0; + bits_to_clear -= 8; + } else - { ma->addr[byte--] &= (IPV4_NETMASK_HOST << bits_to_clear); bits_to_clear = 0; } + { + ma->v6.addr.s6_addr[byte--] &= (IPV4_NETMASK_HOST << bits_to_clear); + bits_to_clear = 0; + } } ASSERT( bits_to_clear == 0 ); } @@ -390,51 +397,41 @@ mroute_addr_print_ex (const struct mroute_addr *ma, switch (maddr.type & MR_ADDR_MASK) { case MR_ADDR_ETHER: - buf_printf (&out, "%s", format_hex_ex (ma->addr, 6, 0, 1, ":", gc)); + buf_printf (&out, "%s", format_hex_ex (ma->eth_addr, + sizeof(ma->eth_addr), 0, 1, ":", gc)); break; case MR_ADDR_IPV4: { - struct buffer buf; - in_addr_t addr; - int port; - bool status; - buf_set_read (&buf, maddr.addr, maddr.len); - addr = buf_read_u32 (&buf, &status); - if (status) + if ((flags & MAPF_SHOW_ARP) && (maddr.type & MR_ARP)) + buf_printf (&out, "ARP/"); + buf_printf (&out, "%s", print_in_addr_t (ntohl (maddr.v4.addr), + (flags & MAPF_IA_EMPTY_IF_UNDEF) ? IA_EMPTY_IF_UNDEF : 0, gc)); + if (maddr.type & MR_WITH_NETBITS) { - if ((flags & MAPF_SHOW_ARP) && (maddr.type & MR_ARP)) - buf_printf (&out, "ARP/"); - buf_printf (&out, "%s", print_in_addr_t (addr, (flags & MAPF_IA_EMPTY_IF_UNDEF) ? IA_EMPTY_IF_UNDEF : 0, gc)); - if (maddr.type & MR_WITH_NETBITS) + if (flags & MAPF_SUBNET) { - if (flags & MAPF_SUBNET) - { - const in_addr_t netmask = netbits_to_netmask (maddr.netbits); - buf_printf (&out, "/%s", print_in_addr_t (netmask, 0, gc)); - } - else - buf_printf (&out, "/%d", maddr.netbits); + const in_addr_t netmask = netbits_to_netmask (maddr.netbits); + buf_printf (&out, "/%s", print_in_addr_t (netmask, 0, gc)); } + else + buf_printf (&out, "/%d", maddr.netbits); } if (maddr.type & MR_WITH_PORT) { - port = buf_read_u16 (&buf); - if (port >= 0) - buf_printf (&out, ":%d", port); + buf_printf (&out, ":%d", ntohs (maddr.v4.port)); } } break; case MR_ADDR_IPV6: { - if ( IN6_IS_ADDR_V4MAPPED( (struct in6_addr*)&maddr.addr ) ) + if ( IN6_IS_ADDR_V4MAPPED( &maddr.v6.addr ) ) { - buf_printf (&out, "%s", - print_in_addr_t( *(in_addr_t*)(&maddr.addr[12]), IA_NET_ORDER, gc)); + buf_printf (&out, "%s", print_in_addr_t (maddr.v4mappedv6.addr, + IA_NET_ORDER, gc)); } else { - buf_printf (&out, "%s", - print_in6_addr( *(struct in6_addr*)&maddr.addr, 0, gc)); + buf_printf (&out, "%s", print_in6_addr (maddr.v6.addr, 0, gc)); } if (maddr.type & MR_WITH_NETBITS) { diff --git a/src/openvpn/mroute.h b/src/openvpn/mroute.h index 608f70b..5fe17e7 100644 --- a/src/openvpn/mroute.h +++ b/src/openvpn/mroute.h @@ -31,6 +31,8 @@ #include "list.h" #include "route.h" +#include <stddef.h> + #define IP_MCAST_SUBNET_MASK ((in_addr_t)240<<24) #define IP_MCAST_NETWORK ((in_addr_t)224<<24) @@ -79,9 +81,35 @@ struct mroute_addr { uint8_t type; /* MR_ADDR/MR_WITH flags */ uint8_t netbits; /* number of bits in network part of address, valid if MR_WITH_NETBITS is set */ - uint8_t addr[MR_MAX_ADDR_LEN]; /* actual address */ + union { + uint8_t raw_addr[MR_MAX_ADDR_LEN]; /* actual address */ + uint8_t eth_addr[OPENVPN_ETH_ALEN]; + struct { + in_addr_t addr; /* _network order_ IPv4 address */ + in_port_t port; /* _network order_ TCP/UDP port */ + } v4; + struct { + struct in6_addr addr; + in_port_t port; /* _network order_ TCP/UDP port */ + } v6; + struct { + uint8_t prefix[12]; + in_addr_t addr; /* _network order_ IPv4 address */ + } v4mappedv6; + }; }; +/* Double-check that struct packing works as expected */ +static_assert (offsetof(struct mroute_addr, v4.port) == + offsetof(struct mroute_addr, v4) + 4, + "Unexpected struct packing of v4"); +static_assert (offsetof(struct mroute_addr, v6.port) == + offsetof(struct mroute_addr, v6) + 16, + "Unexpected struct packing of v6"); +static_assert (offsetof(struct mroute_addr, v4mappedv6.addr) == + offsetof(struct mroute_addr, v4mappedv6) + 12, + "Unexpected struct packing of v4mappedv6"); + /* * Number of bits in an address. Should be raised for IPv6. */ @@ -167,7 +195,7 @@ mroute_addr_equal (const struct mroute_addr *a1, const struct mroute_addr *a2) return false; if (a1->len != a2->len) return false; - return memcmp (a1->addr, a2->addr, a1->len) == 0; + return memcmp (a1->raw_addr, a2->raw_addr, a1->len) == 0; } static inline const uint8_t * @@ -189,16 +217,17 @@ mroute_extract_in_addr_t (struct mroute_addr *dest, const in_addr_t src) dest->type = MR_ADDR_IPV4; dest->netbits = 0; dest->len = 4; - *(in_addr_t*)dest->addr = htonl (src); + dest->v4.addr = htonl (src); } static inline in_addr_t in_addr_t_from_mroute_addr (const struct mroute_addr *addr) { - if ((addr->type & MR_ADDR_MASK) == MR_ADDR_IPV4 && addr->netbits == 0 && addr->len == 4) - return ntohl(*(in_addr_t*)addr->addr); - else + if ((addr->type & MR_ADDR_MASK) == MR_ADDR_IPV4 && addr->netbits == 0 && addr->len == 4) { + return ntohl(addr->v4.addr); + } else { return 0; + } } static inline void diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c index a834e94..83b1e99 100644 --- a/src/openvpn/multi.c +++ b/src/openvpn/multi.c @@ -1201,7 +1201,7 @@ multi_learn_in6_addr (struct multi_context *m, addr.len = 16; addr.type = MR_ADDR_IPV6; addr.netbits = 0; - memcpy( &addr.addr, &a6, sizeof(a6) ); + addr.v6.addr = a6; if (netbits >= 0) { @@ -2431,7 +2431,7 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins { /* IPv6 link-local address (fe80::xxx)? */ if ( (src.type & MR_ADDR_MASK) == MR_ADDR_IPV6 && - src.addr[0] == 0xfe && src.addr[1] == 0x80 ) + IN6_IS_ADDR_LINKLOCAL (&src.v6.addr) ) { /* do nothing, for now. TODO: add address learning */ } diff --git a/src/openvpn/ps.c b/src/openvpn/ps.c index fe18a9d..2cb68f1 100644 --- a/src/openvpn/ps.c +++ b/src/openvpn/ps.c @@ -223,12 +223,12 @@ port_share_sendmsg (const socket_descriptor_t sd, if (socket_defined (sd_send)) { - *((socket_descriptor_t*)CMSG_DATA(h)) = sd_send; + memcpy (CMSG_DATA(h), &sd_send, sizeof (sd_send)); } else { socketpair (PF_UNIX, SOCK_DGRAM, 0, sd_null); - *((socket_descriptor_t*)CMSG_DATA(h)) = sd_null[0]; + memcpy (CMSG_DATA(h), &sd_null[0], sizeof (sd_null[0])); } status = sendmsg (sd, &mesg, MSG_NOSIGNAL); @@ -502,7 +502,8 @@ control_message_from_parent (const socket_descriptor_t sd_control, h->cmsg_len = CMSG_LEN(sizeof(socket_descriptor_t)); h->cmsg_level = SOL_SOCKET; h->cmsg_type = SCM_RIGHTS; - *((socket_descriptor_t*)CMSG_DATA(h)) = SOCKET_UNDEFINED; + static const socket_descriptor_t socket_undefined = SOCKET_UNDEFINED; + memcpy (CMSG_DATA(h), &socket_undefined, sizeof(socket_undefined)); status = recvmsg (sd_control, &mesg, MSG_NOSIGNAL); if (status != -1) @@ -516,7 +517,8 @@ control_message_from_parent (const socket_descriptor_t sd_control, } else { - const socket_descriptor_t received_fd = *((socket_descriptor_t*)CMSG_DATA(h)); + socket_descriptor_t received_fd; + memcpy (&received_fd, CMSG_DATA(h), sizeof(received_fd)); dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: RECEIVED sd=%d", (int)received_fd); if (status >= 2 && command == COMMAND_REDIRECT) diff --git a/src/openvpn/socket.c b/src/openvpn/socket.c index 18e6409..1fbb415 100644 --- a/src/openvpn/socket.c +++ b/src/openvpn/socket.c @@ -2598,7 +2598,8 @@ setenv_sockaddr (struct env_set *es, const char *name_prefix, const struct openv if ( IN6_IS_ADDR_V4MAPPED( &addr->addr.in6.sin6_addr )) { struct in_addr ia; - ia.s_addr = *(in_addr_t *)&addr->addr.in6.sin6_addr.s6_addr[12] ; + memcpy (&ia.s_addr, &addr->addr.in6.sin6_addr.s6_addr[12], + sizeof (ia.s_addr)); openvpn_snprintf (name_buf, sizeof (name_buf), "%s_ip", name_prefix); openvpn_snprintf (buf, sizeof(buf), "%s", inet_ntoa(ia) ); } -- 2.7.4 ------------------------------------------------------------------------------ Developer Access Program for Intel Xeon Phi Processors Access to Intel Xeon Phi processor-based developer platforms. With one year of Intel Parallel Studio XE. Training and support from Colfax. Order your platform today. http://sdm.link/xeonphi _______________________________________________ Openvpn-devel mailing list Openvpn-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/openvpn-devel