// https://syzkaller.appspot.com/bug?id=015a28c8fa50fb3a73f4a31444bee44883d3dcca // 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 // } #include #include #include #include #include #include #include #include #include #include #include #ifndef NETLINK_RDMA #define NETLINK_RDMA 20 #endif #ifndef RDMA_NL_NLDEV #define RDMA_NL_NLDEV 5 #endif #ifndef RDMA_NLDEV_CMD_NEWLINK #define RDMA_NLDEV_CMD_NEWLINK 3 #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 unsigned int get_ifindex(const char *name) { int fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) return 0; struct ifreq ifr; strncpy(ifr.ifr_name, name, IFNAMSIZ - 1); ifr.ifr_name[IFNAMSIZ - 1] = '\0'; if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { close(fd); return 0; } close(fd); return ifr.ifr_ifindex; } int create_dummy_netdev(const char *name) { int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd < 0) { printf("[-] socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) failed: %s\n", strerror(errno)); return -1; } char buf[1024] = {0}; struct nlmsghdr *nlh = (struct nlmsghdr *)buf; nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); nlh->nlmsg_type = RTM_NEWLINK; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK; nlh->nlmsg_seq = 1; struct ifinfomsg *ifi = (struct ifinfomsg *)NLMSG_DATA(nlh); ifi->ifi_family = AF_UNSPEC; struct rtattr *rta = (struct rtattr *)(((char *)nlh) + NLMSG_ALIGN(nlh->nlmsg_len)); rta->rta_type = IFLA_IFNAME; rta->rta_len = RTA_LENGTH(strlen(name) + 1); strcpy(RTA_DATA(rta), name); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + RTA_ALIGN(rta->rta_len); struct rtattr *linkinfo = (struct rtattr *)(((char *)nlh) + NLMSG_ALIGN(nlh->nlmsg_len)); linkinfo->rta_type = IFLA_LINKINFO; linkinfo->rta_len = RTA_LENGTH(0); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + RTA_ALIGN(linkinfo->rta_len); rta = (struct rtattr *)(((char *)linkinfo) + RTA_ALIGN(linkinfo->rta_len)); rta->rta_type = IFLA_INFO_KIND; rta->rta_len = RTA_LENGTH(strlen("dummy") + 1); strcpy(RTA_DATA(rta), "dummy"); linkinfo->rta_len += RTA_ALIGN(rta->rta_len); nlh->nlmsg_len += RTA_ALIGN(rta->rta_len); if (send(fd, buf, nlh->nlmsg_len, 0) < 0) { printf("[-] send failed: %s\n", strerror(errno)); close(fd); return -1; } int len = recv(fd, buf, sizeof(buf), 0); if (len < 0) { printf("[-] recv failed: %s\n", strerror(errno)); close(fd); return -1; } struct nlmsghdr *reply = (struct nlmsghdr *)buf; if (reply->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(reply); if (err->error != 0 && err->error != -EEXIST) { printf("[-] netlink error: %s\n", strerror(-err->error)); close(fd); return -1; } } close(fd); return 0; } int create_rdma_rxe(const char *ibdev_name, const char *ndev_name) { int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_RDMA); if (fd < 0) { printf("[-] socket(AF_NETLINK, SOCK_RAW, NETLINK_RDMA) failed: %s\n", strerror(errno)); return -1; } char buf[1024] = {0}; struct nlmsghdr *nlh = (struct nlmsghdr *)buf; nlh->nlmsg_len = NLMSG_LENGTH(0); nlh->nlmsg_type = (RDMA_NL_NLDEV << 10) | RDMA_NLDEV_CMD_NEWLINK; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; nlh->nlmsg_seq = 1; struct rtattr *rta = (struct rtattr *)(((char *)nlh) + NLMSG_ALIGN(nlh->nlmsg_len)); rta->rta_type = RDMA_NLDEV_ATTR_DEV_NAME; rta->rta_len = RTA_LENGTH(strlen(ibdev_name) + 1); strcpy(RTA_DATA(rta), ibdev_name); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + RTA_ALIGN(rta->rta_len); rta = (struct rtattr *)(((char *)nlh) + NLMSG_ALIGN(nlh->nlmsg_len)); rta->rta_type = RDMA_NLDEV_ATTR_LINK_TYPE; rta->rta_len = RTA_LENGTH(4); strcpy(RTA_DATA(rta), "rxe"); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + RTA_ALIGN(rta->rta_len); rta = (struct rtattr *)(((char *)nlh) + NLMSG_ALIGN(nlh->nlmsg_len)); rta->rta_type = RDMA_NLDEV_ATTR_NDEV_NAME; rta->rta_len = RTA_LENGTH(strlen(ndev_name) + 1); strcpy(RTA_DATA(rta), ndev_name); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + RTA_ALIGN(rta->rta_len); if (send(fd, buf, nlh->nlmsg_len, 0) < 0) { printf("[-] send failed: %s\n", strerror(errno)); close(fd); return -1; } int len = recv(fd, buf, sizeof(buf), 0); if (len < 0) { printf("[-] recv failed: %s\n", strerror(errno)); close(fd); return -1; } struct nlmsghdr *reply = (struct nlmsghdr *)buf; if (reply->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(reply); if (err->error != 0 && err->error != -EEXIST) { printf("[-] netlink error: %s\n", strerror(-err->error)); close(fd); return -1; } } close(fd); return 0; } int set_netdev_flags(const char *name, unsigned int flags, unsigned int mask) { int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd < 0) { return -1; } char buf[1024] = {0}; struct nlmsghdr *nlh = (struct nlmsghdr *)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_flags = flags; ifi->ifi_change = mask; struct rtattr *rta = (struct rtattr *)(((char *)nlh) + NLMSG_ALIGN(nlh->nlmsg_len)); rta->rta_type = IFLA_IFNAME; rta->rta_len = RTA_LENGTH(strlen(name) + 1); strcpy(RTA_DATA(rta), name); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + RTA_ALIGN(rta->rta_len); if (send(fd, buf, nlh->nlmsg_len, 0) < 0) { close(fd); return -1; } int len = recv(fd, buf, sizeof(buf), 0); if (len < 0) { close(fd); return -1; } struct nlmsghdr *reply = (struct nlmsghdr *)buf; if (reply->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(reply); if (err->error != 0) { close(fd); return -1; } } close(fd); return 0; } int create_macvlan(const char *name, const char *link, unsigned int flags, unsigned int change) { int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd < 0) return -1; char buf[1024] = {0}; struct nlmsghdr *nlh = (struct nlmsghdr *)buf; nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); nlh->nlmsg_type = RTM_NEWLINK; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK; nlh->nlmsg_seq = 1; struct ifinfomsg *ifi = (struct ifinfomsg *)NLMSG_DATA(nlh); ifi->ifi_family = AF_UNSPEC; ifi->ifi_flags = flags; ifi->ifi_change = change; struct rtattr *rta = (struct rtattr *)(((char *)nlh) + NLMSG_ALIGN(nlh->nlmsg_len)); rta->rta_type = IFLA_IFNAME; rta->rta_len = RTA_LENGTH(strlen(name) + 1); strcpy(RTA_DATA(rta), name); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + RTA_ALIGN(rta->rta_len); unsigned int link_idx = get_ifindex(link); if (link_idx > 0) { rta = (struct rtattr *)(((char *)nlh) + NLMSG_ALIGN(nlh->nlmsg_len)); rta->rta_type = IFLA_LINK; rta->rta_len = RTA_LENGTH(sizeof(unsigned int)); memcpy(RTA_DATA(rta), &link_idx, sizeof(unsigned int)); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + RTA_ALIGN(rta->rta_len); } struct rtattr *linkinfo = (struct rtattr *)(((char *)nlh) + NLMSG_ALIGN(nlh->nlmsg_len)); linkinfo->rta_type = IFLA_LINKINFO; linkinfo->rta_len = RTA_LENGTH(0); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + RTA_ALIGN(linkinfo->rta_len); rta = (struct rtattr *)(((char *)linkinfo) + RTA_ALIGN(linkinfo->rta_len)); rta->rta_type = IFLA_INFO_KIND; rta->rta_len = RTA_LENGTH(strlen("macvlan") + 1); strcpy(RTA_DATA(rta), "macvlan"); linkinfo->rta_len += RTA_ALIGN(rta->rta_len); nlh->nlmsg_len += RTA_ALIGN(rta->rta_len); send(fd, buf, nlh->nlmsg_len, 0); recv(fd, buf, sizeof(buf), 0); close(fd); return 0; } int delete_netdev(const char *name) { int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd < 0) return -1; char buf[1024] = {0}; struct nlmsghdr *nlh = (struct nlmsghdr *)buf; nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); nlh->nlmsg_type = RTM_DELLINK; 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; struct rtattr *rta = (struct rtattr *)(((char *)nlh) + NLMSG_ALIGN(nlh->nlmsg_len)); rta->rta_type = IFLA_IFNAME; rta->rta_len = RTA_LENGTH(strlen(name) + 1); strcpy(RTA_DATA(rta), name); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + RTA_ALIGN(rta->rta_len); send(fd, buf, nlh->nlmsg_len, 0); recv(fd, buf, sizeof(buf), 0); close(fd); return 0; } void *thread1(void *arg) { PIN_TO_CPU(0); TIMER_START(start); while (TIMER_NOT_EXPIRED(start, 5.0)) { create_macvlan("macvlan0", "dummy0", IFF_UP | IFF_PROMISC, IFF_UP | IFF_PROMISC); delete_netdev("macvlan0"); } return NULL; } void *thread2(void *arg) { PIN_TO_CPU(1); TIMER_START(start); while (TIMER_NOT_EXPIRED(start, 5.0)) { create_macvlan("macvlan1", "dummy0", IFF_UP | IFF_PROMISC, IFF_UP | IFF_PROMISC); delete_netdev("macvlan1"); } return NULL; } void *thread3(void *arg) { PIN_TO_CPU(2); TIMER_START(start); while (TIMER_NOT_EXPIRED(start, 5.0)) { create_macvlan("macvlan2", "dummy0", IFF_UP | IFF_PROMISC, IFF_UP | IFF_PROMISC); delete_netdev("macvlan2"); } return NULL; } void *thread4(void *arg) { PIN_TO_CPU(3); TIMER_START(start); while (TIMER_NOT_EXPIRED(start, 5.0)) { create_macvlan("macvlan3", "dummy0", IFF_UP | IFF_PROMISC, IFF_UP | IFF_PROMISC); delete_netdev("macvlan3"); } return NULL; } int main() { SETUP_UNBUFFERED_IO(); if (create_dummy_netdev("dummy0") < 0) { printf("[-] Failed to create dummy netdev\n"); exit(1); } printf("[+] dummy netdev created successfully.\n"); if (set_netdev_flags("dummy0", IFF_UP, IFF_UP) < 0) { printf("[-] Failed to set IFF_UP on dummy0\n"); exit(1); } printf("[+] dummy netdev set UP successfully.\n"); if (create_rdma_rxe("rxe0", "dummy0") < 0) { printf("[-] Failed to create rdma_rxe device\n"); exit(1); } printf("[+] rdma_rxe device created successfully.\n"); pthread_t t1, t2, t3, t4; pthread_create(&t1, NULL, thread1, NULL); pthread_create(&t2, NULL, thread2, NULL); pthread_create(&t3, NULL, thread3, NULL); pthread_create(&t4, NULL, thread4, NULL); pthread_join(t1, NULL); pthread_join(t2, NULL); pthread_join(t3, NULL); pthread_join(t4, NULL); printf("[+] Race completed.\n"); return 0; }