mirror of
https://github.com/SerenityOS/serenity.git
synced 2026-11-07 04:29:30 -10:00
Tests/Kernel: Add a test for UDP datagram length validation
The test sends a datagram whose UDP Length is smaller than a UDP header and one whose Length is larger than the IPv4 payload, followed by a well-formed datagram. The two malformed datagrams must be dropped, and the well-formed one must still be delivered. Injecting a UDP header with an arbitrary Length requires a raw socket, which is restricted to the superuser, so the test is installed setuid root like the other privileged kernel tests.
This commit is contained in:
1 parent
b067d1cd5a
commit
9fff8b73b2
3 files changed
+89
No files matched your search
@@ -131,6 +131,10 @@ if [ -f mnt/usr/Tests/Kernel/TestLoopDevice ]; then
|
||||
chown 0:0 mnt/usr/Tests/Kernel/TestLoopDevice
|
||||
chmod 4755 mnt/usr/Tests/Kernel/TestLoopDevice
|
||||
fi
|
||||
if [ -f mnt/usr/Tests/Kernel/TestUDPSocket ]; then
|
||||
chown 0:0 mnt/usr/Tests/Kernel/TestUDPSocket
|
||||
chmod 4755 mnt/usr/Tests/Kernel/TestUDPSocket
|
||||
fi
|
||||
|
||||
if [ -f mnt/res/kernel.map ]; then
|
||||
chmod 0400 mnt/res/kernel.map
|
||||
|
||||
@@ -63,6 +63,7 @@ set(LIBTEST_BASED_SOURCES
|
||||
TestSignalDispatch.cpp
|
||||
TestSymlink.cpp
|
||||
TestTCPSocket.cpp
|
||||
TestUDPSocket.cpp
|
||||
TestWXProtection.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2026, Jose
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <errno.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static constexpr u16 port = 1338;
|
||||
|
||||
struct [[gnu::packed]] UDPHeader {
|
||||
u16 source_port;
|
||||
u16 destination_port;
|
||||
u16 length;
|
||||
u16 checksum;
|
||||
};
|
||||
static_assert(sizeof(UDPHeader) == 8);
|
||||
|
||||
// Regression test for UDP datagrams whose Length field disagrees with the IPv4
|
||||
// payload. They used to be handed to the socket, where UDPSocket sized its copy
|
||||
// from the unvalidated Length field: a length below sizeof(UDPPacket) tripped a
|
||||
// VERIFY() in protocol_receive(), and a length above the IPv4 payload made it
|
||||
// read past the queued packet.
|
||||
TEST_CASE(udp_packets_with_invalid_length_are_dropped)
|
||||
{
|
||||
int server_fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
EXPECT(server_fd >= 0);
|
||||
|
||||
sockaddr_in address {};
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_port = htons(port);
|
||||
address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
EXPECT_EQ(bind(server_fd, reinterpret_cast<sockaddr*>(&address), sizeof(address)), 0);
|
||||
|
||||
int raw_fd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
|
||||
EXPECT(raw_fd >= 0);
|
||||
|
||||
auto send_raw = [&](void const* data, size_t size) {
|
||||
return sendto(raw_fd, data, size, 0, reinterpret_cast<sockaddr*>(&address), sizeof(address));
|
||||
};
|
||||
|
||||
// UDP Length smaller than a UDP header.
|
||||
UDPHeader too_short {};
|
||||
too_short.source_port = htons(1234);
|
||||
too_short.destination_port = htons(port);
|
||||
too_short.length = htons(0);
|
||||
EXPECT_EQ(send_raw(&too_short, sizeof(too_short)), static_cast<ssize_t>(sizeof(too_short)));
|
||||
|
||||
// UDP Length larger than the IPv4 payload.
|
||||
UDPHeader too_long {};
|
||||
too_long.source_port = htons(1234);
|
||||
too_long.destination_port = htons(port);
|
||||
too_long.length = htons(0xffff);
|
||||
EXPECT_EQ(send_raw(&too_long, sizeof(too_long)), static_cast<ssize_t>(sizeof(too_long)));
|
||||
|
||||
// A well-formed datagram must still be delivered. Loopback delivery is
|
||||
// FIFO, so receiving this one proves the two malformed datagrams were
|
||||
// dropped instead of being queued ahead of it.
|
||||
struct [[gnu::packed]] ValidDatagram {
|
||||
UDPHeader header;
|
||||
u8 payload;
|
||||
} valid {};
|
||||
valid.header.source_port = htons(1234);
|
||||
valid.header.destination_port = htons(port);
|
||||
valid.header.length = htons(sizeof(UDPHeader) + 1);
|
||||
valid.payload = 'A';
|
||||
EXPECT_EQ(send_raw(&valid, sizeof(valid)), static_cast<ssize_t>(sizeof(valid)));
|
||||
|
||||
u8 buffer[16];
|
||||
EXPECT_EQ(recv(server_fd, buffer, sizeof(buffer), 0), 1);
|
||||
EXPECT_EQ(buffer[0], 'A');
|
||||
|
||||
// Nothing else should be queued.
|
||||
errno = 0;
|
||||
EXPECT_EQ(recv(server_fd, buffer, sizeof(buffer), MSG_DONTWAIT), -1);
|
||||
EXPECT_EQ(errno, EAGAIN);
|
||||
|
||||
EXPECT_EQ(close(raw_fd), 0);
|
||||
EXPECT_EQ(close(server_fd), 0);
|
||||
}
|
||||
Reference in new issue
Block a user