Kernel: Convert TCP pseudo-headers through a union

This keeps us from tripping strict aliasing, which previously made TCP
connections inoperable when building without `-fsanitize=undefined` or
`-fno-strict-aliasing`.
This commit is contained in:
Tim Schumacher 2022-12-12 17:52:00 +01:00 committed by Linus Groh
parent 7cd99572be
commit 24f956c739

View file

@ -360,31 +360,35 @@ bool TCPSocket::should_delay_next_ack() const
NetworkOrdered<u16> TCPSocket::compute_tcp_checksum(IPv4Address const& source, IPv4Address const& destination, TCPPacket const& packet, u16 payload_size)
{
struct [[gnu::packed]] PseudoHeader {
IPv4Address source;
IPv4Address destination;
u8 zero;
u8 protocol;
NetworkOrdered<u16> payload_size;
union PseudoHeader {
struct [[gnu::packed]] {
IPv4Address source;
IPv4Address destination;
u8 zero;
u8 protocol;
NetworkOrdered<u16> payload_size;
} header;
u16 raw[6];
};
static_assert(sizeof(PseudoHeader) == 12);
PseudoHeader pseudo_header { source, destination, 0, (u8)IPv4Protocol::TCP, packet.header_size() + payload_size };
PseudoHeader pseudo_header { .header = { source, destination, 0, (u8)IPv4Protocol::TCP, packet.header_size() + payload_size } };
u32 checksum = 0;
auto raw_pseudo_header = bit_cast<u16*>(&pseudo_header);
auto* raw_pseudo_header = pseudo_header.raw;
for (size_t i = 0; i < sizeof(pseudo_header) / sizeof(u16); ++i) {
checksum += AK::convert_between_host_and_network_endian(raw_pseudo_header[i]);
if (checksum > 0xffff)
checksum = (checksum >> 16) + (checksum & 0xffff);
}
auto raw_packet = bit_cast<u16*>(&packet);
auto* raw_packet = bit_cast<u16*>(&packet);
for (size_t i = 0; i < packet.header_size() / sizeof(u16); ++i) {
checksum += AK::convert_between_host_and_network_endian(raw_packet[i]);
if (checksum > 0xffff)
checksum = (checksum >> 16) + (checksum & 0xffff);
}
VERIFY(packet.data_offset() * 4 == packet.header_size());
auto raw_payload = bit_cast<u16*>(packet.payload());
auto* raw_payload = bit_cast<u16*>(packet.payload());
for (size_t i = 0; i < payload_size / sizeof(u16); ++i) {
checksum += AK::convert_between_host_and_network_endian(raw_payload[i]);
if (checksum > 0xffff)