// https://syzkaller.appspot.com/bug?id=2d7f080b8f89f7d11239ef3d336f260747c4318e #define _GNU_SOURCE #include #include #include #include #include #include #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 // } #define AOE_MAJOR 152 #define MINOR_DISCOVER 3 #define MINOR_INTERFACES 4 static volatile int running = 1; static int g_syzkaller0_idx = 0; static const uint8_t sta_mac[6] = {0x02, 0x00, 0x00, 0x00, 0x00, 0x01}; static void nla_put(char *buf, size_t *offset, uint16_t type, const void *data, size_t len) { struct nlattr *na = (struct nlattr *)(buf + *offset); na->nla_type = type; na->nla_len = NLA_HDRLEN + len; memcpy((char *)na + NLA_HDRLEN, data, len); *offset += NLA_ALIGN(na->nla_len); } static void nla_put_u32(char *buf, size_t *offset, uint16_t type, uint32_t val) { nla_put(buf, offset, type, &val, sizeof(val)); } static void nla_put_string(char *buf, size_t *offset, uint16_t type, const char *str) { nla_put(buf, offset, type, str, strlen(str) + 1); } static size_t nla_nest_start(char *buf, size_t *offset, uint16_t type) { size_t start = *offset; struct nlattr *na = (struct nlattr *)(buf + start); na->nla_type = type; na->nla_len = 0; *offset += NLA_HDRLEN; return start; } static void nla_nest_end(char *buf, size_t *offset, size_t nest_start) { struct nlattr *na = (struct nlattr *)(buf + nest_start); na->nla_len = *offset - nest_start; *offset = NLA_ALIGN(*offset); } static uint16_t get_genl_family(int nl_fd, const char *name) { struct { struct nlmsghdr n; struct genlmsghdr g; char buf[256]; } req; memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); req.n.nlmsg_type = GENL_ID_CTRL; req.n.nlmsg_flags = NLM_F_REQUEST; req.n.nlmsg_seq = 1; req.g.cmd = CTRL_CMD_GETFAMILY; req.g.version = 1; size_t off = 0; nla_put_string(req.buf, &off, CTRL_ATTR_FAMILY_NAME, name); req.n.nlmsg_len += off; ssize_t sres = send(nl_fd, &req, req.n.nlmsg_len, 0); if (sres < 0) { printf("[-] Failed to send GETFAMILY request: %s\n", strerror(errno)); return 0; } char buf[4096]; ssize_t rres = recv(nl_fd, buf, sizeof(buf), 0); if (rres <= 0) { printf("[-] Failed to recv GETFAMILY reply: %s\n", strerror(errno)); return 0; } struct nlmsghdr *nlh = (struct nlmsghdr *)buf; if (!NLMSG_OK(nlh, (size_t)rres)) { printf("[-] Invalid NLMSG in GETFAMILY reply\n"); return 0; } struct genlmsghdr *ghdr = NLMSG_DATA(nlh); struct nlattr *attr = (struct nlattr *)((char *)ghdr + GENL_HDRLEN); int rem = nlh->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN); while (rem >= (int)sizeof(struct nlattr)) { if (attr->nla_type == CTRL_ATTR_FAMILY_ID) return *(uint16_t *)((char *)attr + NLA_HDRLEN); int aligned = NLA_ALIGN(attr->nla_len); rem -= aligned; attr = (struct nlattr *)((char *)attr + aligned); } return 0; } static void bring_down(const char *name) { int s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { printf("[-] bring_down(%s): socket failed: %s\n", name, strerror(errno)); return; } struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, name, IFNAMSIZ - 1); ifr.ifr_flags = 0; if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) { printf("[-] bring_down(%s): SIOCSIFFLAGS failed: %s\n", name, strerror(errno)); } else { printf("[+] %s brought DOWN successfully.\n", name); } close(s); } static void bring_up(const char *name) { int s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { printf("[-] bring_up(%s): socket failed: %s\n", name, strerror(errno)); return; } struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, name, IFNAMSIZ - 1); ifr.ifr_flags = IFF_UP | IFF_BROADCAST | IFF_MULTICAST; if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) { printf("[-] bring_up(%s): SIOCSIFFLAGS failed: %s\n", name, strerror(errno)); } else { printf("[+] %s brought UP successfully.\n", name); } close(s); } static void get_mac(const char *name, uint8_t *mac) { int s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { printf("[-] get_mac: socket failed: %s\n", strerror(errno)); exit(1); } struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, name, IFNAMSIZ - 1); if (ioctl(s, SIOCGIFHWADDR, &ifr) < 0) { printf("[-] get_mac: SIOCGIFHWADDR failed for %s: %s\n", name, strerror(errno)); close(s); exit(1); } memcpy(mac, ifr.ifr_hwaddr.sa_data, 6); close(s); printf("[+] Interface %s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", name, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); } static int delete_nl80211_interface(int nl_fd, uint16_t fam_id, int ifindex) { struct { struct nlmsghdr n; struct genlmsghdr g; char buf[256]; } req; memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); req.n.nlmsg_type = fam_id; req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; req.n.nlmsg_seq = 10; req.g.cmd = NL80211_CMD_DEL_INTERFACE; req.g.version = 1; size_t off = 0; nla_put_u32(req.buf, &off, NL80211_ATTR_IFINDEX, (uint32_t)ifindex); req.n.nlmsg_len += off; ssize_t sres = send(nl_fd, &req, req.n.nlmsg_len, 0); if (sres < 0) { printf("[-] Failed to send NL80211_CMD_DEL_INTERFACE: %s\n", strerror(errno)); return -1; } char res[1024]; recv(nl_fd, res, sizeof(res), 0); printf("[+] Deleted interface with ifindex %d\n", ifindex); return 0; } static int set_interface_type(int nl_fd, uint16_t fam_id, int ifindex, uint32_t iftype) { struct { struct nlmsghdr n; struct genlmsghdr g; char buf[256]; } req; memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); req.n.nlmsg_type = fam_id; req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; req.n.nlmsg_seq = 20; req.g.cmd = NL80211_CMD_SET_INTERFACE; req.g.version = 1; size_t off = 0; nla_put_u32(req.buf, &off, NL80211_ATTR_IFINDEX, (uint32_t)ifindex); nla_put_u32(req.buf, &off, NL80211_ATTR_IFTYPE, iftype); req.n.nlmsg_len += off; ssize_t sres = send(nl_fd, &req, req.n.nlmsg_len, 0); if (sres < 0) { printf("[-] send NL80211_CMD_SET_INTERFACE failed: %s\n", strerror(errno)); return -1; } char buf[1024]; ssize_t rres = recv(nl_fd, buf, sizeof(buf), 0); if (rres > 0) { struct nlmsghdr *nlh = (struct nlmsghdr *)buf; if (nlh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(nlh); if (err->error != 0) { printf("[-] NL80211_CMD_SET_INTERFACE returned error: %s (%d)\n", strerror(-err->error), err->error); return err->error; } } } printf("[+] Set interface %d to type %u successful.\n", ifindex, iftype); return 0; } static int start_ap(int nl_fd, uint16_t fam_id, int ifindex, const uint8_t *mac) { struct { struct nlmsghdr n; struct genlmsghdr g; char buf[512]; } req; memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); req.n.nlmsg_type = fam_id; req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; req.n.nlmsg_seq = 21; req.g.cmd = NL80211_CMD_START_AP; req.g.version = 1; uint8_t beacon_head[55] = { 0x80, 0x00, /* Frame Control: Beacon */ 0x00, 0x00, /* Duration */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* DA: Broadcast */ 0, 0, 0, 0, 0, 0, /* SA (will be filled) */ 0, 0, 0, 0, 0, 0, /* BSSID (will be filled) */ 0x00, 0x00, /* Seq Ctrl */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Timestamp */ 0x64, 0x00, /* Beacon interval (100) */ 0x01, 0x00, /* Capability info (ESS) */ 0x00, 0x04, 't', 'e', 's', 't', /* SSID "test" */ 0x01, 0x08, 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, /* Supported Rates */ 0x03, 0x01, 0x01 /* DS Param: Ch 1 */ }; memcpy(beacon_head + 10, mac, 6); memcpy(beacon_head + 16, mac, 6); size_t off = 0; nla_put_u32(req.buf, &off, NL80211_ATTR_IFINDEX, (uint32_t)ifindex); nla_put_u32(req.buf, &off, NL80211_ATTR_BEACON_INTERVAL, 100); nla_put_u32(req.buf, &off, NL80211_ATTR_DTIM_PERIOD, 2); nla_put(req.buf, &off, NL80211_ATTR_SSID, "test", 4); nla_put_u32(req.buf, &off, NL80211_ATTR_WIPHY_FREQ, 2412); nla_put(req.buf, &off, NL80211_ATTR_BEACON_HEAD, beacon_head, sizeof(beacon_head)); req.n.nlmsg_len += off; ssize_t sres = send(nl_fd, &req, req.n.nlmsg_len, 0); if (sres < 0) { printf("[-] send NL80211_CMD_START_AP failed: %s\n", strerror(errno)); return -1; } char buf[1024]; ssize_t rres = recv(nl_fd, buf, sizeof(buf), 0); if (rres > 0) { struct nlmsghdr *nlh = (struct nlmsghdr *)buf; if (nlh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(nlh); if (err->error != 0) { printf("[-] NL80211_CMD_START_AP returned error: %s (%d)\n", strerror(-err->error), err->error); return err->error; } } } printf("[+] NL80211_CMD_START_AP successful.\n"); return 0; } static int create_ap_vlan_interface(int nl_fd, uint16_t fam_id, int wlan0_idx, const char *name) { struct { struct nlmsghdr n; struct genlmsghdr g; char buf[512]; } req; memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); req.n.nlmsg_type = fam_id; req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; req.n.nlmsg_seq = 22; req.g.cmd = NL80211_CMD_NEW_INTERFACE; req.g.version = 1; uint8_t use_4addr = 1; size_t off = 0; nla_put_u32(req.buf, &off, NL80211_ATTR_IFINDEX, (uint32_t)wlan0_idx); nla_put_string(req.buf, &off, NL80211_ATTR_IFNAME, name); nla_put_u32(req.buf, &off, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_AP_VLAN); nla_put(req.buf, &off, NL80211_ATTR_4ADDR, &use_4addr, 1); req.n.nlmsg_len += off; ssize_t sres = send(nl_fd, &req, req.n.nlmsg_len, 0); if (sres < 0) { printf("[-] send NL80211_CMD_NEW_INTERFACE failed: %s\n", strerror(errno)); return -1; } char buf[1024]; ssize_t rres = recv(nl_fd, buf, sizeof(buf), 0); if (rres > 0) { struct nlmsghdr *nlh = (struct nlmsghdr *)buf; if (nlh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(nlh); if (err->error != 0) { printf("[-] NL80211_CMD_NEW_INTERFACE returned error: %s (%d)\n", strerror(-err->error), err->error); return err->error; } } } printf("[+] Created AP_VLAN interface %s with 4ADDR enabled.\n", name); return 0; } static int add_new_station(int nl_fd, uint16_t fam_id, int ap_idx, const uint8_t *mac, int vlan_idx) { struct { struct nlmsghdr n; struct genlmsghdr g; char buf[512]; } req; memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); req.n.nlmsg_type = fam_id; req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; req.n.nlmsg_seq = 23; req.g.cmd = NL80211_CMD_NEW_STATION; req.g.version = 1; uint16_t aid = 1; uint16_t listen_interval = 100; uint8_t rates[4] = {0x82, 0x84, 0x8b, 0x96}; struct nl80211_sta_flag_update flags = { .mask = (1 << NL80211_STA_FLAG_AUTHORIZED), .set = (1 << NL80211_STA_FLAG_AUTHORIZED) }; size_t off = 0; nla_put_u32(req.buf, &off, NL80211_ATTR_IFINDEX, (uint32_t)ap_idx); nla_put(req.buf, &off, NL80211_ATTR_MAC, mac, 6); nla_put(req.buf, &off, NL80211_ATTR_STA_AID, &aid, 2); nla_put(req.buf, &off, NL80211_ATTR_STA_LISTEN_INTERVAL, &listen_interval, 2); nla_put(req.buf, &off, NL80211_ATTR_STA_SUPPORTED_RATES, rates, sizeof(rates)); nla_put(req.buf, &off, NL80211_ATTR_STA_FLAGS2, &flags, sizeof(flags)); if (vlan_idx > 0) nla_put_u32(req.buf, &off, NL80211_ATTR_STA_VLAN, (uint32_t)vlan_idx); req.n.nlmsg_len += off; ssize_t sres = send(nl_fd, &req, req.n.nlmsg_len, 0); if (sres < 0) { printf("[-] send NL80211_CMD_NEW_STATION failed: %s\n", strerror(errno)); return -1; } char buf[1024]; ssize_t rres = recv(nl_fd, buf, sizeof(buf), 0); if (rres > 0) { struct nlmsghdr *nlh = (struct nlmsghdr *)buf; if (nlh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(nlh); if (err->error != 0) { printf("[-] NL80211_CMD_NEW_STATION returned error: %s (%d)\n", strerror(-err->error), err->error); return err->error; } } } printf("[+] NL80211_CMD_NEW_STATION successful (vlan_idx: %d).\n", vlan_idx); return 0; } static int set_station_vlan(int nl_fd, uint16_t fam_id, int ap_idx, const uint8_t *mac, int vlan_idx) { struct { struct nlmsghdr n; struct genlmsghdr g; char buf[512]; } req; memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); req.n.nlmsg_type = fam_id; req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; req.n.nlmsg_seq = 24; req.g.cmd = NL80211_CMD_SET_STATION; req.g.version = 1; size_t off = 0; nla_put_u32(req.buf, &off, NL80211_ATTR_IFINDEX, (uint32_t)ap_idx); nla_put(req.buf, &off, NL80211_ATTR_MAC, mac, 6); nla_put_u32(req.buf, &off, NL80211_ATTR_STA_VLAN, (uint32_t)vlan_idx); req.n.nlmsg_len += off; ssize_t sres = send(nl_fd, &req, req.n.nlmsg_len, 0); if (sres < 0) { printf("[-] send NL80211_CMD_SET_STATION failed: %s\n", strerror(errno)); return -1; } char buf[1024]; ssize_t rres = recv(nl_fd, buf, sizeof(buf), 0); if (rres > 0) { struct nlmsghdr *nlh = (struct nlmsghdr *)buf; if (nlh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(nlh); if (err->error != 0) { printf("[-] NL80211_CMD_SET_STATION returned error: %s (%d)\n", strerror(-err->error), err->error); return err->error; } } } printf("[+] NL80211_CMD_SET_STATION successful (vlan_idx: %d).\n", vlan_idx); return 0; } static int create_dummy(int rtnl_fd, const char *name) { struct { struct nlmsghdr n; struct ifinfomsg ifi; char buf[512]; } req; memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); req.n.nlmsg_type = RTM_NEWLINK; req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK; req.n.nlmsg_seq = 100; req.ifi.ifi_family = AF_UNSPEC; size_t off = 0; nla_put_string(req.buf, &off, IFLA_IFNAME, name); size_t linkinfo = nla_nest_start(req.buf, &off, IFLA_LINKINFO); nla_put_string(req.buf, &off, IFLA_INFO_KIND, "dummy"); nla_nest_end(req.buf, &off, linkinfo); req.n.nlmsg_len += off; ssize_t sres = send(rtnl_fd, &req, req.n.nlmsg_len, 0); if (sres < 0) { printf("[-] Failed to send RTM_NEWLINK for %s: %s\n", name, strerror(errno)); return -1; } char buf[1024]; recv(rtnl_fd, buf, sizeof(buf), 0); return if_nametoindex(name); } static int create_ipvlan(int rtnl_fd, const char *name, int master_idx) { struct { struct nlmsghdr n; struct ifinfomsg ifi; char buf[512]; } req; memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); req.n.nlmsg_type = RTM_NEWLINK; req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK; req.n.nlmsg_seq = 101; req.ifi.ifi_family = AF_UNSPEC; size_t off = 0; nla_put_string(req.buf, &off, IFLA_IFNAME, name); nla_put_u32(req.buf, &off, IFLA_LINK, (uint32_t)master_idx); size_t linkinfo = nla_nest_start(req.buf, &off, IFLA_LINKINFO); nla_put_string(req.buf, &off, IFLA_INFO_KIND, "ipvlan"); nla_nest_end(req.buf, &off, linkinfo); req.n.nlmsg_len += off; ssize_t sres = send(rtnl_fd, &req, req.n.nlmsg_len, 0); if (sres < 0) { printf("[-] Failed to send RTM_NEWLINK for %s: %s\n", name, strerror(errno)); return -1; } char buf[1024]; ssize_t rres = recv(rtnl_fd, buf, sizeof(buf), 0); if (rres > 0) { struct nlmsghdr *nlh = (struct nlmsghdr *)buf; if (nlh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(nlh); if (err->error != 0 && err->error != -EEXIST) { printf("[-] RTM_NEWLINK for %s error: %s\n", name, strerror(-err->error)); return -1; } } } printf("[+] Created %s on master index %d successfully.\n", name, master_idx); return 0; } static void *thr_aoe_discover(void *arg) { (void)arg; PIN_TO_CPU(0); int disc_fd = open("/dev/etherd/discover", O_WRONLY); if (disc_fd < 0) { printf("[-] thr_aoe_discover: Failed to open /dev/etherd/discover: %s\n", strerror(errno)); return NULL; } printf("[+] thr_aoe_discover started on CPU 0.\n"); while (__atomic_load_n(&running, __ATOMIC_RELAXED)) { write(disc_fd, "1", 1); usleep(1000); } close(disc_fd); return NULL; } static void *thr_pkt_sender(void *arg) { (void)arg; PIN_TO_CPU(1); int sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (sock < 0) { printf("[-] thr_pkt_sender: Failed to open AF_PACKET socket: %s\n", strerror(errno)); return NULL; } struct sockaddr_ll sll; memset(&sll, 0, sizeof(sll)); sll.sll_family = AF_PACKET; sll.sll_ifindex = g_syzkaller0_idx; sll.sll_protocol = htons(ETH_P_ALL); sll.sll_halen = 6; memcpy(sll.sll_addr, sta_mac, 6); uint8_t pkt[60]; memset(pkt, 0, sizeof(pkt)); memcpy(pkt, sta_mac, 6); /* DA: Station MAC */ memset(pkt + 6, 0x02, 6); /* SA */ *(uint16_t *)(pkt + 12) = htons(0x88a2); /* AoE EtherType */ uint8_t bcast_pkt[60]; memset(bcast_pkt, 0, sizeof(bcast_pkt)); memset(bcast_pkt, 0xff, 6); /* DA: Broadcast */ memset(bcast_pkt + 6, 0x02, 6); /* SA */ *(uint16_t *)(bcast_pkt + 12) = htons(0x88a2); printf("[+] thr_pkt_sender started on CPU 1 (transmitting on ifindex %d).\n", g_syzkaller0_idx); while (__atomic_load_n(&running, __ATOMIC_RELAXED)) { sendto(sock, pkt, sizeof(pkt), MSG_DONTWAIT, (struct sockaddr *)&sll, sizeof(sll)); sendto(sock, bcast_pkt, sizeof(bcast_pkt), MSG_DONTWAIT, (struct sockaddr *)&sll, sizeof(sll)); usleep(500); } close(sock); return NULL; } int main(void) { setvbuf(stdout, NULL, _IONBF, 0); SETUP_UNBUFFERED_IO(); printf("[*] Starting Bug Reproducer for WARNING in ieee80211_queue_skb...\n"); /* 1. Ensure /dev/etherd character devices exist */ if (mkdir("/dev/etherd", 0755) < 0 && errno != EEXIST) { printf("[-] mkdir /dev/etherd failed: %s\n", strerror(errno)); exit(1); } if (mknod("/dev/etherd/discover", S_IFCHR | 0666, makedev(AOE_MAJOR, MINOR_DISCOVER)) < 0 && errno != EEXIST) { printf("[-] mknod /dev/etherd/discover failed: %s\n", strerror(errno)); exit(1); } if (mknod("/dev/etherd/interfaces", S_IFCHR | 0666, makedev(AOE_MAJOR, MINOR_INTERFACES)) < 0 && errno != EEXIST) { printf("[-] mknod /dev/etherd/interfaces failed: %s\n", strerror(errno)); exit(1); } printf("[+] Ensured /dev/etherd character device nodes exist.\n"); /* 2. Setup RTNETLINK socket and create ipvlan0 and ipvlan1 */ int rtnl_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (rtnl_fd < 0) { printf("[-] Failed to open NETLINK_ROUTE socket: %s\n", strerror(errno)); exit(1); } int master_idx = if_nametoindex("eth0"); if (master_idx <= 0) { master_idx = if_nametoindex("wlan0"); if (master_idx <= 0) { master_idx = create_dummy(rtnl_fd, "dummy0"); bring_up("dummy0"); } } printf("[+] Master link index: %d\n", master_idx); int idx0 = if_nametoindex("ipvlan0"); if (idx0 <= 0) create_ipvlan(rtnl_fd, "ipvlan0", master_idx); bring_up("ipvlan0"); int idx1 = if_nametoindex("ipvlan1"); if (idx1 <= 0) create_ipvlan(rtnl_fd, "ipvlan1", master_idx); bring_up("ipvlan1"); close(rtnl_fd); /* 3. Setup Generic Netlink socket for nl80211 */ int nl_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); if (nl_fd < 0) { printf("[-] Failed to open NETLINK_GENERIC socket: %s\n", strerror(errno)); exit(1); } uint16_t nl80211_id = get_genl_family(nl_fd, "nl80211"); if (!nl80211_id) { printf("[-] Failed to resolve nl80211 family id\n"); exit(1); } printf("[+] Resolved nl80211 generic netlink family id: %d\n", nl80211_id); /* 4. Configure wlan0 as master AP */ int wlan0_idx = if_nametoindex("wlan0"); if (wlan0_idx <= 0) { printf("[-] wlan0 interface not found\n"); exit(1); } printf("[+] Found wlan0 with ifindex: %d\n", wlan0_idx); bring_down("wlan0"); set_interface_type(nl_fd, nl80211_id, wlan0_idx, NL80211_IFTYPE_AP); bring_up("wlan0"); uint8_t wlan0_mac[6]; get_mac("wlan0", wlan0_mac); start_ap(nl_fd, nl80211_id, wlan0_idx, wlan0_mac); /* 5. Create AP_VLAN interface syzkaller0 */ int old_syz_idx = if_nametoindex("syzkaller0"); if (old_syz_idx > 0) { bring_down("syzkaller0"); delete_nl80211_interface(nl_fd, nl80211_id, old_syz_idx); usleep(50000); } create_ap_vlan_interface(nl_fd, nl80211_id, wlan0_idx, "syzkaller0"); g_syzkaller0_idx = if_nametoindex("syzkaller0"); if (g_syzkaller0_idx <= 0) { printf("[-] Failed to obtain ifindex for syzkaller0\n"); exit(1); } printf("[+] Created syzkaller0 with ifindex: %d\n", g_syzkaller0_idx); bring_up("syzkaller0"); /* 6. Add station and bind 4-address mode to syzkaller0 */ add_new_station(nl_fd, nl80211_id, wlan0_idx, sta_mac, g_syzkaller0_idx); set_station_vlan(nl_fd, nl80211_id, wlan0_idx, sta_mac, wlan0_idx); set_station_vlan(nl_fd, nl80211_id, wlan0_idx, sta_mac, g_syzkaller0_idx); close(nl_fd); /* 7. Launch AoE discover and packet sender threads */ pthread_t th_aoe, th_pkt; int res = pthread_create(&th_aoe, NULL, thr_aoe_discover, NULL); if (res != 0) { printf("[-] Failed to create thr_aoe_discover: %s\n", strerror(res)); exit(1); } res = pthread_create(&th_pkt, NULL, thr_pkt_sender, NULL); if (res != 0) { printf("[-] Failed to create thr_pkt_sender: %s\n", strerror(res)); exit(1); } printf("[+] Workers running. Triggering warning for 10 seconds...\n"); TIMER_START(t); while (TIMER_NOT_EXPIRED(t, 10.0)) { usleep(200000); } printf("[*] Stopping workers...\n"); __atomic_store_n(&running, 0, __ATOMIC_RELAXED); pthread_join(th_aoe, NULL); pthread_join(th_pkt, NULL); sleep(1); printf("[+] Reproducer finished.\n"); return 0; }