// https://syzkaller.appspot.com/bug?id=1687893c5b2789ca4b4f0100710fa9f8a636b6f8 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #ifndef NLA_ALIGN #define NLA_ALIGN(len) (((len) + 3) & ~3) #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 #ifndef NLA_NEXT #define NLA_NEXT(nla,len) ((len) -= NLA_ALIGN((nla)->nla_len), \ (struct nlattr*)((char*)(nla) + NLA_ALIGN((nla)->nla_len))) #endif #ifndef NLA_OK #define NLA_OK(nla,len) ((len) >= (int)sizeof(struct nlattr) && \ (nla)->nla_len >= sizeof(struct nlattr) && \ (nla)->nla_len <= (len)) #endif #ifndef GENLMSG_DATA #define GENLMSG_DATA(glh) ((void *)((char *)NLMSG_DATA(glh) + GENL_HDRLEN)) #endif #ifndef NL80211_CMD_SET_WIPHY_NETNS #define NL80211_CMD_SET_WIPHY_NETNS 49 #endif #ifndef NL80211_ATTR_WIPHY #define NL80211_ATTR_WIPHY 1 #endif #ifndef NL80211_ATTR_PID #define NL80211_ATTR_PID 82 #endif static void write_file(const char *path, const char *val) { int fd = open(path, O_WRONLY); if (fd >= 0) { if (write(fd, val, strlen(val)) < 0) { // ignore } close(fd); } } // Helper to resolve the generic netlink family ID static int resolve_family_id(int sock, const char *family_name) { struct { struct nlmsghdr nlh; struct genlmsghdr gnlh; struct nlattr nla; char name[32]; } req; struct sockaddr_nl nladdr; char buf[4096]; struct nlmsghdr *nlh; struct genlmsghdr *gnlh; struct nlattr *nla; int len; memset(&req, 0, sizeof(req)); req.nlh.nlmsg_len = sizeof(req); req.nlh.nlmsg_type = GENL_ID_CTRL; req.nlh.nlmsg_flags = NLM_F_REQUEST; req.gnlh.cmd = CTRL_CMD_GETFAMILY; req.gnlh.version = 1; req.nla.nla_type = CTRL_ATTR_FAMILY_NAME; req.nla.nla_len = sizeof(struct nlattr) + strlen(family_name) + 1; strcpy(req.name, family_name); req.nlh.nlmsg_len = NLMSG_SPACE(sizeof(struct genlmsghdr)) + NLMSG_ALIGN(req.nla.nla_len); memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; if (sendto(sock, &req, req.nlh.nlmsg_len, 0, (struct sockaddr *)&nladdr, sizeof(nladdr)) < 0) return -1; len = recv(sock, buf, sizeof(buf), 0); if (len < 0) return -1; nlh = (struct nlmsghdr *)buf; if (nlh->nlmsg_type == NLMSG_ERROR) return -1; gnlh = (struct genlmsghdr *)NLMSG_DATA(nlh); nla = (struct nlattr *)((char *)gnlh + GENL_HDRLEN); len = nlh->nlmsg_len - NLMSG_SPACE(GENL_HDRLEN); while (NLA_OK(nla, len)) { if (nla->nla_type == CTRL_ATTR_FAMILY_ID) { return *(uint16_t *)NLA_DATA(nla); } nla = NLA_NEXT(nla, len); } return -1; } // Helper to create a new hwsim radio and return its index static int create_hwsim_radio(int sock, int hwsim_family_id) { struct { struct nlmsghdr nlh; struct genlmsghdr gnlh; char buf[256]; } req; struct nlattr *na; struct sockaddr_nl nladdr; memset(&req, 0, sizeof(req)); req.nlh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); req.nlh.nlmsg_type = hwsim_family_id; req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; req.gnlh.cmd = 4; // HWSIM_CMD_NEW_RADIO req.gnlh.version = 1; // Add HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE (16) na = (struct nlattr *) GENLMSG_DATA(&req); na->nla_type = 16; na->nla_len = NLA_HDRLEN; req.nlh.nlmsg_len += NLMSG_ALIGN(na->nla_len); memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; if (sendto(sock, &req, req.nlh.nlmsg_len, 0, (struct sockaddr *)&nladdr, sizeof(nladdr)) < 0) return -1; char buf[4096]; int len = recv(sock, buf, sizeof(buf), 0); if (len < 0) return -1; struct nlmsghdr *nlh = (struct nlmsghdr *)buf; if (nlh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(nlh); if (err->error > 0) { return err->error; // Positive value is the radio ID } } return -1; } // Helper to send the NL80211_CMD_SET_WIPHY_NETNS command static int send_nl80211_msg(int sock, int family_id, int cmd, int wiphy_idx, int pid) { struct { struct nlmsghdr nlh; struct genlmsghdr gnlh; char buf[256]; } req; struct nlattr *na; struct sockaddr_nl nladdr; memset(&req, 0, sizeof(req)); req.nlh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); req.nlh.nlmsg_type = family_id; req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; req.gnlh.cmd = cmd; req.gnlh.version = 1; na = (struct nlattr *) GENLMSG_DATA(&req); na->nla_type = NL80211_ATTR_WIPHY; na->nla_len = NLA_HDRLEN + sizeof(uint32_t); *(uint32_t *)NLA_DATA(na) = wiphy_idx; req.nlh.nlmsg_len += NLMSG_ALIGN(na->nla_len); na = (struct nlattr *) ((char *)na + NLMSG_ALIGN(na->nla_len)); na->nla_type = NL80211_ATTR_PID; na->nla_len = NLA_HDRLEN + sizeof(uint32_t); *(uint32_t *)NLA_DATA(na) = pid; req.nlh.nlmsg_len += NLMSG_ALIGN(na->nla_len); memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; return sendto(sock, &req, req.nlh.nlmsg_len, 0, (struct sockaddr *)&nladdr, sizeof(nladdr)); } int main() { int sock; int nl80211_id, hwsim_id; sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); if (sock < 0) { printf("[-] socket failed: %s\n", strerror(errno)); return 1; } printf("[+] socket successful.\n"); nl80211_id = resolve_family_id(sock, "nl80211"); hwsim_id = resolve_family_id(sock, "MAC80211_HWSIM"); if (nl80211_id < 0) { printf("[-] Failed to resolve nl80211 family ID\n"); return 1; } printf("[+] nl80211 family ID: %d\n", nl80211_id); if (hwsim_id < 0) { printf("[-] Failed to resolve MAC80211_HWSIM family ID\n"); } else { printf("[+] MAC80211_HWSIM family ID: %d\n", hwsim_id); } // Ensure fault injection doesn't ignore GFP_KERNEL allocations write_file("/sys/kernel/debug/failslab/ignore-gfp-wait", "N\n"); write_file("/sys/kernel/debug/fail_page_alloc/ignore-gfp-wait", "N\n"); // Systematically inject faults into the N-th allocation for (int i = 1; i < 100; i++) { int wiphy_idx = 0; if (hwsim_id >= 0) { wiphy_idx = create_hwsim_radio(sock, hwsim_id); if (wiphy_idx < 0) { wiphy_idx = 0; // Fallback } } pid_t pid = fork(); if (pid < 0) { printf("[-] fork failed: %s\n", strerror(errno)); exit(1); } if (pid == 0) { // Child process: unshare to create a new target namespace if (unshare(CLONE_NEWNET) < 0) { printf("[-] unshare failed: %s\n", strerror(errno)); exit(1); } // Enable fault injection for the i-th allocation in this task int fail_nth = open("/proc/self/fail-nth", O_RDWR); if (fail_nth >= 0) { char buf[32]; sprintf(buf, "%d", i); if (write(fail_nth, buf, strlen(buf)) < 0) { // ignore } close(fail_nth); } // Trigger the target code path send_nl80211_msg(sock, nl80211_id, NL80211_CMD_SET_WIPHY_NETNS, wiphy_idx, getpid()); exit(0); } wait(NULL); } close(sock); printf("[+] Done.\n"); return 0; }