// https://syzkaller.appspot.com/bug?id=c12cf6c235ba74427b0959622cdeb9be4b5f1271 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Copyright 2026 syzkaller project authors. All rights reserved. // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. // IMPORTANT: Do not copy the macros or definitions below directly into your reproducer. // Instead, add the following line to your reproducer: // #include "race_toolkit.h" // --- Race Condition Toolkit --- // Macros and snippets for CPU pinning, memory barriers, and userfaultfd. #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Unbuffered I/O: Ensure logs are written immediately. #define SETUP_UNBUFFERED_IO() setvbuf(stdout, NULL, _IONBF, 0) // CPU Pinning: Pin the current thread to a specific CPU core. #define PIN_TO_CPU(cpu) \ do { \ cpu_set_t mask; \ CPU_ZERO(&mask); \ CPU_SET(cpu, &mask); \ if (sched_setaffinity(0, sizeof(mask), &mask) == -1) { \ perror("sched_setaffinity"); \ } \ } while (0) // Memory Barrier: Ensure memory ordering. #define MB() __atomic_thread_fence(__ATOMIC_SEQ_CST) // Spin-wait Barrier: Wait until a memory location has a specific value. // Best for tight race windows (low latency, no context switches). #define WAIT_ON(addr, val) \ do { \ while (__atomic_load_n(addr, __ATOMIC_ACQUIRE) != (val)) \ ; \ } while (0) // Signal: Set a memory location to a specific value to release a WAIT_ON. #define SIGNAL(addr, val) __atomic_store_n(addr, val, __ATOMIC_RELEASE) // --- Timing Primitives --- // Robust timing loops in VM environments (using CLOCK_MONOTONIC to avoid time(NULL) jumps). static inline double timer_elapsed_sec(struct timespec* start) { struct timespec now; if (clock_gettime(CLOCK_MONOTONIC, &now) == -1) { perror("clock_gettime(CLOCK_MONOTONIC) elapsed"); exit(1); } return (double)(now.tv_sec - start->tv_sec) + (double)(now.tv_nsec - start->tv_nsec) / 1e9; } // Initialize a monotonic timer variable. #define TIMER_START(t) \ struct timespec t; \ if (clock_gettime(CLOCK_MONOTONIC, &t) == -1) { \ perror("clock_gettime(CLOCK_MONOTONIC) start"); \ exit(1); \ } // Check if the elapsed time since 't' is less than 'sec' seconds. #define TIMER_NOT_EXPIRED(t, sec) (timer_elapsed_sec(&(t)) < (double)(sec)) // Futex-based Event: Shared with syzkaller executor. // Best for general synchronization or longer waits to save CPU. typedef struct { int state; } event_t; static void event_init(event_t* ev) { ev->state = 0; } static void event_reset(event_t* ev) { ev->state = 0; } static void event_set(event_t* ev) { if (__atomic_load_n(&ev->state, __ATOMIC_ACQUIRE)) { fprintf(stderr, "event already set\n"); exit(1); } __atomic_store_n(&ev->state, 1, __ATOMIC_RELEASE); syscall(SYS_futex, &ev->state, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1000000); } static void event_wait(event_t* ev) { while (!__atomic_load_n(&ev->state, __ATOMIC_ACQUIRE)) syscall(SYS_futex, &ev->state, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, 0, 0); } // userfaultfd setup: Register a memory range for page fault handling. static int setup_uffd(void* addr, size_t len) { int uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); if (uffd == -1) return -1; struct uffdio_api api = {.api = UFFD_API, .features = 0}; if (ioctl(uffd, UFFDIO_API, &api) == -1) { close(uffd); return -1; } struct uffdio_register reg = { .range = {.start = (uintptr_t)addr, .len = len}, .mode = UFFDIO_REGISTER_MODE_MISSING}; if (ioctl(uffd, UFFDIO_REGISTER, ®) == -1) { close(uffd); return -1; } return uffd; } // --- Guidance on Usage --- // 1. Use WAIT_ON/SIGNAL for tight race conditions to avoid scheduling overhead. // 2. Use event_t (futexes) for general coordination or when waiting for longer periods. // 3. Always use PIN_TO_CPU to increase race probability on multi-core systems. // 4. Use setup_uffd to register a memory range for page fault handling. This allows you to // pause a thread accessing that memory until you handle the fault, creating a reliable // and controllable race window. // 5. Call SETUP_UNBUFFERED_IO() at the start of main() to ensure that logs are printed // immediately. This is essential for understanding the exact interleaving of events // when debugging race conditions. // 6. For timing-based loops (e.g., running a race for 10 seconds), do NOT use time(NULL) // or loops relying on real-time clocks, as VM clocks are highly unreliable and can fail or drift. // Instead, use the robust monotonic timing primitives TIMER_START and TIMER_NOT_EXPIRED: // TIMER_START(start); // while (TIMER_NOT_EXPIRED(start, 10.0)) { // // Your race logic here // } #ifndef AF_RDS #define AF_RDS 21 #endif #ifndef SOL_RDS #define SOL_RDS 276 #endif #ifndef SO_RDS_TRANSPORT #define SO_RDS_TRANSPORT 8 #endif #ifndef RDS_TRANS_IB #define RDS_TRANS_IB 0 #endif #ifndef NETLINK_RDMA #define NETLINK_RDMA 20 #endif #ifndef RDMA_NL_NLDEV #define RDMA_NL_NLDEV 5 #endif #ifndef RDMA_NL_GET_TYPE #define RDMA_NL_GET_TYPE(client, op) (((client) << 10) + (op)) #endif #ifndef RDMA_NLDEV_CMD_GET #define RDMA_NLDEV_CMD_GET 1 #endif #ifndef RDMA_NLDEV_CMD_NEWLINK #define RDMA_NLDEV_CMD_NEWLINK 3 #endif #ifndef RDMA_NLDEV_CMD_DELLINK #define RDMA_NLDEV_CMD_DELLINK 4 #endif #ifndef RDMA_NLDEV_ATTR_DEV_INDEX #define RDMA_NLDEV_ATTR_DEV_INDEX 1 #endif #ifndef RDMA_NLDEV_ATTR_DEV_NAME #define RDMA_NLDEV_ATTR_DEV_NAME 2 #endif #ifndef RDMA_NLDEV_ATTR_NDEV_NAME #define RDMA_NLDEV_ATTR_NDEV_NAME 51 #endif #ifndef RDMA_NLDEV_ATTR_LINK_TYPE #define RDMA_NLDEV_ATTR_LINK_TYPE 65 #endif #ifndef NLA_ALIGNTO #define NLA_ALIGNTO 4 #endif #ifndef NLA_ALIGN #define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1)) #endif #ifndef NLA_HDRLEN #define NLA_HDRLEN ((int)NLA_ALIGN(sizeof(struct nlattr))) #endif #ifndef NLA_DATA #define NLA_DATA(nla) ((void *)((char *)(nla) + NLA_HDRLEN)) #endif #define NUM_PREBOUND_SOCKETS 16 static volatile int running = 1; static int prebound_socks[NUM_PREBOUND_SOCKETS]; static void sig_alarm(int sig) { (void)sig; __atomic_store_n(&running, 0, __ATOMIC_RELAXED); } static int send_nl_req(int nl_fd, struct nlmsghdr *nlh) { struct sockaddr_nl sa; memset(&sa, 0, sizeof(sa)); sa.nl_family = AF_NETLINK; struct iovec iov = { .iov_base = nlh, .iov_len = nlh->nlmsg_len, }; struct msghdr msg = { .msg_name = &sa, .msg_namelen = sizeof(sa), .msg_iov = &iov, .msg_iovlen = 1, }; if (sendmsg(nl_fd, &msg, 0) < 0) { return -errno; } if (!(nlh->nlmsg_flags & NLM_F_ACK)) return 0; char buf[4096]; while (1) { iov.iov_base = buf; iov.iov_len = sizeof(buf); msg.msg_name = &sa; msg.msg_namelen = sizeof(sa); msg.msg_iov = &iov; msg.msg_iovlen = 1; ssize_t len = recvmsg(nl_fd, &msg, 0); if (len < 0) return -errno; for (struct nlmsghdr *resp = (struct nlmsghdr *)buf; NLMSG_OK(resp, (size_t)len); resp = NLMSG_NEXT(resp, len)) { if (resp->nlmsg_seq == nlh->nlmsg_seq) { if (resp->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(resp); return err->error; } return 0; } } } } static void add_attr_str(struct nlmsghdr *nlh, int maxlen, int type, const char *str) { int len = strlen(str) + 1; int attrlen = NLA_HDRLEN + len; int aligned_attrlen = NLA_ALIGN(attrlen); if ((int)nlh->nlmsg_len + aligned_attrlen > maxlen) return; struct nlattr *nla = (struct nlattr *)((char *)nlh + NLMSG_ALIGN(nlh->nlmsg_len)); nla->nla_type = type; nla->nla_len = attrlen; memcpy(NLA_DATA(nla), str, len); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + aligned_attrlen; } static void dellink_by_index(int nl_fd, uint32_t idx) { char buf[128] = {0}; struct nlmsghdr *nlh = (struct nlmsghdr *)buf; nlh->nlmsg_len = NLMSG_HDRLEN; nlh->nlmsg_type = RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_DELLINK); nlh->nlmsg_flags = NLM_F_REQUEST; nlh->nlmsg_seq = rand(); struct nlattr *nla = (struct nlattr *)((char *)nlh + NLMSG_HDRLEN); nla->nla_type = RDMA_NLDEV_ATTR_DEV_INDEX; nla->nla_len = NLA_HDRLEN + sizeof(uint32_t); *(uint32_t *)((char *)nla + NLA_HDRLEN) = idx; nlh->nlmsg_len = NLMSG_HDRLEN + NLA_ALIGN(nla->nla_len); send(nl_fd, nlh, nlh->nlmsg_len, 0); } static void cleanup_all_rxe(int nl_fd) { char resp[4096]; while (recv(nl_fd, resp, sizeof(resp), MSG_DONTWAIT) > 0) {} char req[NLMSG_SPACE(0)] = {0}; struct nlmsghdr *nlh = (struct nlmsghdr *)req; nlh->nlmsg_len = NLMSG_LENGTH(0); nlh->nlmsg_type = RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET); nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; nlh->nlmsg_seq = rand(); if (send(nl_fd, nlh, nlh->nlmsg_len, 0) < 0) { for (uint32_t i = 0; i < 32; i++) dellink_by_index(nl_fd, i); while (recv(nl_fd, resp, sizeof(resp), MSG_DONTWAIT) > 0) {} return; } uint32_t indices[64]; int num_indices = 0; while (1) { ssize_t len = recv(nl_fd, resp, sizeof(resp), 0); if (len <= 0) break; struct nlmsghdr *h = (struct nlmsghdr *)resp; int break_outer = 0; for (; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len)) { if (h->nlmsg_type == NLMSG_DONE || h->nlmsg_type == NLMSG_ERROR) { break_outer = 1; break; } if (h->nlmsg_type != RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET)) continue; struct nlattr *nla = (struct nlattr *)NLMSG_DATA(h); int rem = NLMSG_PAYLOAD(h, 0); while (rem >= (int)sizeof(struct nlattr) && nla->nla_len >= sizeof(struct nlattr) && nla->nla_len <= rem) { if (nla->nla_type == RDMA_NLDEV_ATTR_DEV_INDEX && nla->nla_len >= NLA_HDRLEN + sizeof(uint32_t)) { uint32_t idx = *(uint32_t *)((char *)nla + NLA_HDRLEN); if (num_indices < 64) { indices[num_indices++] = idx; } } rem -= NLA_ALIGN(nla->nla_len); nla = (struct nlattr *)((char *)nla + NLA_ALIGN(nla->nla_len)); } } if (break_outer) break; } for (int i = 0; i < num_indices; i++) { dellink_by_index(nl_fd, indices[i]); } for (uint32_t i = 0; i < 32; i++) { dellink_by_index(nl_fd, i); } while (recv(nl_fd, resp, sizeof(resp), MSG_DONTWAIT) > 0) {} } static int create_rxe_link(int nl_fd, const char *ibdev_name, const char *ndev_name) { char drain_buf[4096]; while (recv(nl_fd, drain_buf, sizeof(drain_buf), MSG_DONTWAIT) > 0) {} static uint32_t seq = 1000; char req_buf[256]; memset(req_buf, 0, sizeof(req_buf)); struct nlmsghdr *nlh = (struct nlmsghdr *)req_buf; nlh->nlmsg_len = NLMSG_HDRLEN; nlh->nlmsg_type = RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_NEWLINK); nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; nlh->nlmsg_seq = ++seq; add_attr_str(nlh, sizeof(req_buf), RDMA_NLDEV_ATTR_DEV_NAME, ibdev_name); add_attr_str(nlh, sizeof(req_buf), RDMA_NLDEV_ATTR_LINK_TYPE, "rxe"); add_attr_str(nlh, sizeof(req_buf), RDMA_NLDEV_ATTR_NDEV_NAME, ndev_name); int res = send_nl_req(nl_fd, nlh); if (res == -ENFILE || res == -EEXIST) { return 0; } return res; } static int bring_interface_up(const char *ifname) { int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd < 0) { printf("[-] Failed to open NETLINK_ROUTE socket: %s\n", strerror(errno)); return -errno; } unsigned int ifindex = if_nametoindex(ifname); if (!ifindex) { printf("[-] Failed to get ifindex for %s: %s\n", ifname, strerror(errno)); close(fd); return -errno; } char req_buf[256]; memset(req_buf, 0, sizeof(req_buf)); struct nlmsghdr *nlh = (struct nlmsghdr *)req_buf; nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); nlh->nlmsg_type = RTM_NEWLINK; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; nlh->nlmsg_seq = 1; struct ifinfomsg *ifi = (struct ifinfomsg *)NLMSG_DATA(nlh); ifi->ifi_family = AF_UNSPEC; ifi->ifi_index = ifindex; ifi->ifi_flags = IFF_UP; ifi->ifi_change = IFF_UP; int res = send_nl_req(fd, nlh); close(fd); return res; } static int add_ip_addr(const char *ifname, const char *ip_str, int prefixlen) { int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd < 0) { printf("[-] Failed to open NETLINK_ROUTE socket: %s\n", strerror(errno)); return -1; } unsigned int ifindex = if_nametoindex(ifname); if (!ifindex) { printf("[-] Failed to get ifindex for %s: %s\n", ifname, strerror(errno)); close(fd); return -1; } struct in_addr addr; if (inet_pton(AF_INET, ip_str, &addr) != 1) { printf("[-] Failed to parse IP address %s\n", ip_str); close(fd); return -1; } char req_buf[256]; memset(req_buf, 0, sizeof(req_buf)); struct nlmsghdr *nlh = (struct nlmsghdr *)req_buf; nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); nlh->nlmsg_type = RTM_NEWADDR; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK; nlh->nlmsg_seq = 1; struct ifaddrmsg *ifa = (struct ifaddrmsg *)NLMSG_DATA(nlh); ifa->ifa_family = AF_INET; ifa->ifa_prefixlen = prefixlen; ifa->ifa_flags = IFA_F_PERMANENT; ifa->ifa_scope = RT_SCOPE_UNIVERSE; ifa->ifa_index = ifindex; int rta_len = RTA_LENGTH(sizeof(addr)); struct rtattr *rta = (struct rtattr *)((char *)nlh + NLMSG_ALIGN(nlh->nlmsg_len)); rta->rta_type = IFA_LOCAL; rta->rta_len = rta_len; memcpy(RTA_DATA(rta), &addr, sizeof(addr)); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + RTA_ALIGN(rta_len); rta = (struct rtattr *)((char *)nlh + NLMSG_ALIGN(nlh->nlmsg_len)); rta->rta_type = IFA_ADDRESS; rta->rta_len = rta_len; memcpy(RTA_DATA(rta), &addr, sizeof(addr)); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + RTA_ALIGN(rta_len); int res = send_nl_req(fd, nlh); close(fd); if (res < 0 && res != -EEXIST) { printf("[-] Netlink RTM_NEWADDR failed: %s\n", strerror(-res)); return -1; } printf("[+] IP %s/%d configured on %s (status=%d).\n", ip_str, prefixlen, ifname, res); return 0; } static void *thread_link_flapper(void *arg) { (void)arg; PIN_TO_CPU(0); int nl_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_RDMA); if (nl_fd < 0) { printf("[-] Thread 0 failed to open NETLINK_RDMA socket: %s\n", strerror(errno)); return NULL; } struct timeval tv = { .tv_sec = 0, .tv_usec = 200000 }; setsockopt(nl_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); while (__atomic_load_n(&running, __ATOMIC_RELAXED)) { create_rxe_link(nl_fd, "rxe0", "lo"); usleep(1000 + (rand() % 2000)); cleanup_all_rxe(nl_fd); usleep(500 + (rand() % 1000)); } close(nl_fd); return NULL; } static void *thread_send_loop(void *arg) { int cpu = (int)(intptr_t)arg; PIN_TO_CPU(cpu); char buf[64] = "crash_rdma_resolve_addr"; uint32_t ip_counter = 2 + cpu * 100; while (__atomic_load_n(&running, __ATOMIC_RELAXED)) { for (int i = 0; i < NUM_PREBOUND_SOCKETS; i++) { struct sockaddr_in dst; memset(&dst, 0, sizeof(dst)); dst.sin_family = AF_INET; dst.sin_port = htons(20000 + (ip_counter % 1000)); dst.sin_addr.s_addr = htonl((172U << 24) | (16U << 16) | (42U << 8) | (uint32_t)(2 + (ip_counter % 250))); ip_counter++; sendto(prebound_socks[i], buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr *)&dst, sizeof(dst)); } usleep(100); } return NULL; } static void *thread_socket_churn(void *arg) { (void)arg; PIN_TO_CPU(0); char buf[64] = "churn"; uint32_t port = 30000; uint32_t ip_sub = 2; while (__atomic_load_n(&running, __ATOMIC_RELAXED)) { int s = socket(AF_RDS, SOCK_SEQPACKET, 0); if (s >= 0) { int one = 1; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); int trans = RDS_TRANS_IB; setsockopt(s, SOL_RDS, SO_RDS_TRANSPORT, &trans, sizeof(trans)); struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = inet_addr("172.16.42.1"); sin.sin_port = htons(port++); if (port > 60000) port = 30000; if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == 0) { struct sockaddr_in dst; memset(&dst, 0, sizeof(dst)); dst.sin_family = AF_INET; dst.sin_port = htons(12345); dst.sin_addr.s_addr = htonl((172U << 24) | (16U << 16) | (42U << 8) | (uint32_t)(2 + (ip_sub++ % 250))); sendto(s, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr *)&dst, sizeof(dst)); } close(s); } usleep(rand() % 500); } return NULL; } static void *thread_connected_send(void *arg) { (void)arg; PIN_TO_CPU(1); char buf[64] = "conn_send"; uint32_t port = 40000; uint32_t ip_sub = 2; while (__atomic_load_n(&running, __ATOMIC_RELAXED)) { int s = socket(AF_RDS, SOCK_SEQPACKET, 0); if (s >= 0) { int one = 1; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); int trans = RDS_TRANS_IB; setsockopt(s, SOL_RDS, SO_RDS_TRANSPORT, &trans, sizeof(trans)); struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = inet_addr("172.16.42.1"); sin.sin_port = htons(port++); if (port > 60000) port = 40000; if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == 0) { struct sockaddr_in dst; memset(&dst, 0, sizeof(dst)); dst.sin_family = AF_INET; dst.sin_port = htons(54321); dst.sin_addr.s_addr = htonl((172U << 24) | (16U << 16) | (42U << 8) | (uint32_t)(2 + (ip_sub++ % 250))); connect(s, (struct sockaddr *)&dst, sizeof(dst)); send(s, buf, sizeof(buf), MSG_DONTWAIT); } close(s); } usleep(rand() % 500); } return NULL; } int main(void) { setvbuf(stdout, NULL, _IONBF, 0); SETUP_UNBUFFERED_IO(); printf("[*] Starting reproducer for WARNING in rdma_resolve_addr...\n"); signal(SIGALRM, sig_alarm); alarm(25); /* 1. Bring loopback interface up */ int res = bring_interface_up("lo"); if (res < 0) { printf("[-] Failed to bring lo interface up: %s\n", strerror(-res)); exit(1); } printf("[+] Interface 'lo' is UP.\n"); /* 2. Assign non-loopback IPv4 address 172.16.42.1/24 to lo */ res = add_ip_addr("lo", "172.16.42.1", 24); if (res < 0) { printf("[-] Failed to configure IP address on lo.\n"); exit(1); } /* 3. Open Netlink RDMA socket and clean up any pre-existing links */ int nl_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_RDMA); if (nl_fd < 0) { printf("[-] Failed to open NETLINK_RDMA socket: %s\n", strerror(errno)); exit(1); } printf("[+] NETLINK_RDMA socket opened.\n"); struct timeval tv = { .tv_sec = 0, .tv_usec = 200000 }; setsockopt(nl_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); cleanup_all_rxe(nl_fd); printf("[+] Initial RDMA cleanup completed.\n"); /* 4. Create initial rxe0 link on lo */ res = create_rxe_link(nl_fd, "rxe0", "lo"); if (res < 0) { printf("[*] Note: create_rxe_link returned %d (%s), continuing...\n", res, strerror(-res)); } else { printf("[+] Created rxe0 link on 'lo'.\n"); } close(nl_fd); /* 5. Allow Soft-RoCE to scan GIDs and RDS/IB to register the device */ usleep(100000); printf("[+] Initialized Soft-RoCE device rxe0.\n"); /* 6. Pre-create and bind sockets to 172.16.42.1 with SO_RDS_TRANSPORT = RDS_TRANS_IB */ for (int i = 0; i < NUM_PREBOUND_SOCKETS; i++) { int s = socket(AF_RDS, SOCK_SEQPACKET, 0); if (s < 0) { printf("[-] Failed to create AF_RDS socket %d: %s\n", i, strerror(errno)); exit(1); } int one = 1; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); int trans = RDS_TRANS_IB; res = setsockopt(s, SOL_RDS, SO_RDS_TRANSPORT, &trans, sizeof(trans)); if (res < 0) { printf("[-] Failed to set SO_RDS_TRANSPORT on socket %d: %s\n", i, strerror(errno)); close(s); exit(1); } struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = inet_addr("172.16.42.1"); sin.sin_port = htons(10000 + i); res = bind(s, (struct sockaddr *)&sin, sizeof(sin)); if (res < 0) { printf("[-] Failed to bind socket %d: %s\n", i, strerror(errno)); close(s); exit(1); } prebound_socks[i] = s; } printf("[+] Successfully pre-bound %d AF_RDS sockets with RDS_TRANS_IB.\n", NUM_PREBOUND_SOCKETS); /* 7. Spawn concurrent racing threads */ pthread_t th[5]; res = pthread_create(&th[0], NULL, thread_link_flapper, NULL); if (res != 0) { printf("[-] Failed to create thread_link_flapper: %s\n", strerror(res)); exit(1); } res = pthread_create(&th[1], NULL, thread_send_loop, (void *)(intptr_t)1); if (res != 0) { printf("[-] Failed to create thread_send_loop 1: %s\n", strerror(res)); exit(1); } res = pthread_create(&th[2], NULL, thread_send_loop, (void *)(intptr_t)0); if (res != 0) { printf("[-] Failed to create thread_send_loop 2: %s\n", strerror(res)); exit(1); } res = pthread_create(&th[3], NULL, thread_socket_churn, NULL); if (res != 0) { printf("[-] Failed to create thread_socket_churn: %s\n", strerror(res)); exit(1); } res = pthread_create(&th[4], NULL, thread_connected_send, NULL); if (res != 0) { printf("[-] Failed to create thread_connected_send: %s\n", strerror(res)); exit(1); } printf("[+] All 5 race threads spawned. Racing for 18 seconds...\n"); TIMER_START(timer); while (TIMER_NOT_EXPIRED(timer, 18.0) && __atomic_load_n(&running, __ATOMIC_RELAXED)) { sleep(1); } __atomic_store_n(&running, 0, __ATOMIC_RELAXED); printf("[*] Stopping race threads...\n"); for (int i = 0; i < 5; i++) { pthread_join(th[i], NULL); } printf("[+] All race threads joined.\n"); /* 8. Allow kernel workers to settle before exit */ printf("[*] Waiting for asynchronous kernel workers to complete...\n"); sleep(3); for (int i = 0; i < NUM_PREBOUND_SOCKETS; i++) { close(prebound_socks[i]); } printf("[+] Test completed cleanly.\n"); return 0; }