diff options
author | Anton Kling <anton@kling.gg> | 2024-06-28 17:56:53 +0200 |
---|---|---|
committer | Anton Kling <anton@kling.gg> | 2024-06-28 17:57:45 +0200 |
commit | b747eb399c556858e05de51b1bb3e875c834c8e0 (patch) | |
tree | b8893bea96e8a01f9053a24d5558188cd1c2ea2e /kernel/network | |
parent | 0bab28c0bd18274a9aff69ce0249ae9b8009dfa4 (diff) |
Bug fix: Fix undefined behavior of ipv4_t type.
Turns out unions that include arrays and a integer can have UB due to
endianess not being the same across systems.
Diffstat (limited to 'kernel/network')
-rw-r--r-- | kernel/network/ipv4.c | 44 |
1 files changed, 19 insertions, 25 deletions
diff --git a/kernel/network/ipv4.c b/kernel/network/ipv4.c index 8fb983a..69ceef7 100644 --- a/kernel/network/ipv4.c +++ b/kernel/network/ipv4.c @@ -9,27 +9,13 @@ #include <network/udp.h> #include <string.h> -u16 ip_checksum(void *vdata, size_t length) { - // Cast the data pointer to one that can be indexed. - char *data = (char *)vdata; - +static u16 ip_checksum(const u16 *data, u16 length) { // Initialise the accumulator. u32 acc = 0xffff; // Handle complete 16-bit blocks. - for (size_t i = 0; i + 1 < length; i += 2) { - u16 word; - memcpy(&word, data + i, 2); - acc += ntohs(word); - if (acc > 0xffff) { - acc -= 0xffff; - } - } - - // Handle any partial block at the end of the data. - if (length & 1) { - u16 word = 0; - memcpy(&word, data + length - 1, 1); + for (size_t i = 0; i < length / 2; i++) { + u16 word = *(data + i); acc += ntohs(word); if (acc > 0xffff) { acc -= 0xffff; @@ -41,16 +27,24 @@ u16 ip_checksum(void *vdata, size_t length) { } void send_ipv4_packet(ipv4_t ip, u8 protocol, const u8 *payload, u16 length) { - u8 header[20] = {0}; + u16 header[10]; header[0] = (4 /*version*/ << 4) | (5 /*IHL*/); - *((u16 *)(header + 2)) = htons(length + 20); - header[8 /*TTL*/] = 0xF8; - header[9] = protocol; - memcpy(header + 12 /*src_ip*/, &ip_address, sizeof(ipv4_t)); - memcpy(header + 16, &ip, sizeof(ipv4_t)); + header[1] = htons(length + 20); + + header[2] = 0; + + header[3] = 0; + header[4] = (protocol << 8) | 0xF8; + + header[5] = 0; + header[6] = (ip_address.d >> 0) & 0xFFFF; + header[7] = (ip_address.d >> 16) & 0xFFFF; + + header[8] = (ip.d >> 0) & 0xFFFF; + header[9] = (ip.d >> 16) & 0xFFFF; - *((u16 *)(header + 10 /*checksum*/)) = ip_checksum(header, 20); + header[5] = ip_checksum(header, 20); u16 packet_length = length + 20; u8 *packet = kmalloc(packet_length); memcpy(packet, header, 20); @@ -68,7 +62,7 @@ void handle_ipv4(const u8 *payload, u32 packet_length) { u16 saved_checksum = *(u16 *)(payload + 10); *(u16 *)(payload + 10) = 0; - u16 calc_checksum = ip_checksum((u8 *)payload, 20); + u16 calc_checksum = ip_checksum((const u16 *)payload, 20); *(u16 *)(payload + 10) = saved_checksum; if (calc_checksum != saved_checksum) { klog(LOG_WARN, "Invalid ipv4 checksum"); |