ctdb-common: Fix aliasing issue in IPv6 checksum
authorMartin Schwenke <martin@meltin.net>
Mon, 13 Aug 2018 02:18:51 +0000 (12:18 +1000)
committerAmitay Isaacs <amitay@samba.org>
Fri, 24 Aug 2018 08:59:20 +0000 (10:59 +0200)
Since commit 9c51b278b1700cd5f3e2addc19b7c711cc2ea10b the compiler has
been able to inline the affected call to uint16_checksum().  Given
that the data (phdr) is being accessed by an incompatible
pointer (data) there is an aliasing problem when the call is inlined.
This results in incorrect behaviour with -O2/-O3 when compiling with
at least GCC 6, 7, and 8.

Fix this by making the types compatible.

Also fixes CID 1437604 (Reliance on integer endianness).  This is a
false positive because the uint16_checksum doesn't depend on the order
of the input uint16_t items.

https://bugzilla.samba.org/show_bug.cgi?id=13588

Pair-programmed-with: Amitay Isaacs <amitay@gmail.com>
Signed-off-by: Martin Schwenke <martin@meltin.net>
Reviewed-by: Amitay Isaacs <amitay@gmail.com>
ctdb/common/system_socket.c

index 22776f73ea557ab0852a50601e8532d081fa55b8..4a7a8c8c464abd09a9fb0b6a34ab2f1d703f82bb 100644 (file)
@@ -135,16 +135,20 @@ static uint16_t ip_checksum(uint16_t *data, size_t n, struct ip *ip)
 
 static uint16_t ip6_checksum(uint16_t *data, size_t n, struct ip6_hdr *ip6)
 {
-       uint32_t phdr[2];
+       uint16_t phdr[3];
        uint32_t sum = 0;
        uint16_t sum2;
+       uint32_t len;
 
        sum += uint16_checksum((uint16_t *)(void *)&ip6->ip6_src, 16);
        sum += uint16_checksum((uint16_t *)(void *)&ip6->ip6_dst, 16);
 
-       phdr[0] = htonl(n);
-       phdr[1] = htonl(ip6->ip6_nxt);
-       sum += uint16_checksum((uint16_t *)phdr, 8);
+       len = htonl(n);
+       phdr[0] = len & UINT16_MAX;
+       phdr[1] = (len >> 16) & UINT16_MAX;
+       /* ip6_nxt is only 8 bits, so fits comfortably into a uint16_t */
+       phdr[2] = htons(ip6->ip6_nxt);
+       sum += uint16_checksum(phdr, sizeof(phdr));
 
        sum += uint16_checksum(data, n);