Work around a GCC bug that causes inet_checksum() to give wrong results.
authorGuus Sliepen <guus@tinc-vpn.org>
Tue, 27 Feb 2018 20:08:57 +0000 (21:08 +0100)
committerGuus Sliepen <guus@tinc-vpn.org>
Tue, 27 Feb 2018 20:08:57 +0000 (21:08 +0100)
Valgrind reports the following bug:

==24877== Conditional jump or move depends on uninitialised value(s)
==24877==    at 0x12283E: inet_checksum (route.c:80)
==24877==    by 0x12283E: route_ipv6_unreachable (route.c:315)
==24877==    by 0x1236AC: route_ipv6 (route.c:751)
==24877==    by 0x1236AC: route (route.c:1160)
==24877==    by 0x113DE0: receive_tcppacket (net_packet.c:493)
==24877==    by 0x1119D4: receive_meta (meta.c:315)
==24877==    by 0x113288: handle_meta_connection_data (net.c:287)
==24877==    by 0x11A091: handle_meta_io (net_socket.c:491)
==24877==    by 0x10FB0C: event_loop (event.c:370)
==24877==    by 0x11362E: main_loop (net.c:489)
==24877==    by 0x10CACA: main (tincd.c:551)

Clearing the variable pseudo in route_ipv6_unreachable removes this error,
but the resulting checksum is still bad. If one instead adds a dummy
write that depends on checksum, the error goes away and the checksum is
correct.

src/route.c

index a3e92020ce4bcc47c2aa10be772990b92cb73c61..4d7127959308cc0fcb6be76614469339202359ee 100644 (file)
@@ -59,6 +59,7 @@ static const size_t opt_size = sizeof(struct nd_opt_hdr);
 #define MAX(a, b) ((a) > (b) ? (a) : (b))
 #endif
 
+volatile int dummy;
 static timeout_t age_subnets_timeout;
 
 /* RFC 1071 */
@@ -80,6 +81,11 @@ static uint16_t inet_checksum(void *data, int len, uint16_t prevsum) {
                checksum = (checksum & 0xFFFF) + (checksum >> 16);
        }
 
+       // Work around a compiler optimization bug.
+       if(checksum) {
+               dummy = 1;
+       }
+
        return ~checksum;
 }