// https://syzkaller.appspot.com/bug?id=f93dd05a8738e182cf0d076ed89fba50669c2d42 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define NETLINK_RDMA 20 #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 #ifndef RDMA_NL_GET_TYPE #define RDMA_NL_GET_TYPE(client, op) ((client << 10) + op) #endif struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type) { struct rtattr *nest = (struct rtattr *)(((char *)n) + NLMSG_ALIGN(n->nlmsg_len)); int len = RTA_LENGTH(0); if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) return NULL; nest->rta_type = type; nest->rta_len = len; n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); return nest; } void addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest) { nest->rta_len = (char *)n + NLMSG_ALIGN(n->nlmsg_len) - (char *)nest; } void addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen) { int len = RTA_LENGTH(alen); struct rtattr *rta; if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) return; rta = (struct rtattr *)(((char *)n) + NLMSG_ALIGN(n->nlmsg_len)); rta->rta_type = type; rta->rta_len = len; if (alen) memcpy(RTA_DATA(rta), data, alen); n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); } int send_nl_req(int fd, struct nlmsghdr *nlh) { struct sockaddr_nl sa; memset(&sa, 0, sizeof(sa)); sa.nl_family = AF_NETLINK; struct iovec iov = { nlh, nlh->nlmsg_len }; struct msghdr msg = { &sa, sizeof(sa), &iov, 1, NULL, 0, 0 }; if (sendmsg(fd, &msg, 0) < 0) { printf("[-] sendmsg failed: %s\n", strerror(errno)); exit(1); } char buf[4096]; struct iovec riov = { buf, sizeof(buf) }; struct msghdr rmsg = { &sa, sizeof(sa), &riov, 1, NULL, 0, 0 }; int len = recvmsg(fd, &rmsg, 0); if (len < 0) { printf("[-] recvmsg failed: %s\n", strerror(errno)); exit(1); } struct nlmsghdr *rnlh = (struct nlmsghdr *)buf; if (rnlh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(rnlh); if (err->error == 0 || err->error == -EEXIST) return 0; errno = -err->error; return -1; } return 0; } int create_dummy(int id) { int rt_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (rt_fd < 0) { printf("[-] socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) failed: %s\n", strerror(errno)); exit(1); } struct { struct nlmsghdr nlh; struct ifinfomsg ifm; char buf[1024]; } req; memset(&req, 0, sizeof(req)); req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK; req.nlh.nlmsg_type = RTM_NEWLINK; req.ifm.ifi_family = AF_UNSPEC; char name[32]; snprintf(name, sizeof(name), "dummy%d", id); addattr_l(&req.nlh, sizeof(req), IFLA_IFNAME, name, strlen(name) + 1); struct rtattr *linkinfo = addattr_nest(&req.nlh, sizeof(req), IFLA_LINKINFO); addattr_l(&req.nlh, sizeof(req), IFLA_INFO_KIND, "dummy", 6); addattr_nest_end(&req.nlh, linkinfo); int ret = send_nl_req(rt_fd, &req.nlh); if (close(rt_fd) < 0) { printf("[-] close failed: %s\n", strerror(errno)); exit(1); } return ret; } int set_if_up(int id) { int fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) { printf("[-] socket(AF_INET, SOCK_DGRAM, 0) failed: %s\n", strerror(errno)); exit(1); } struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "dummy%d", id); if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { printf("[-] ioctl(SIOCGIFFLAGS) failed: %s\n", strerror(errno)); exit(1); } ifr.ifr_flags |= IFF_UP; if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) { printf("[-] ioctl(SIOCSIFFLAGS) failed: %s\n", strerror(errno)); exit(1); } if (close(fd) < 0) { printf("[-] close failed: %s\n", strerror(errno)); exit(1); } return 0; } int create_siw(int id) { int rdma_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_RDMA); if (rdma_fd < 0) { printf("[-] socket(AF_NETLINK, SOCK_RAW, NETLINK_RDMA) failed: %s\n", strerror(errno)); exit(1); } struct { struct nlmsghdr nlh; char buf[1024]; } rdma_req; memset(&rdma_req, 0, sizeof(rdma_req)); rdma_req.nlh.nlmsg_len = NLMSG_LENGTH(0); rdma_req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; rdma_req.nlh.nlmsg_type = RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_NEWLINK); char dev_name[32], ndev_name[32]; snprintf(dev_name, sizeof(dev_name), "siw%d", id); snprintf(ndev_name, sizeof(ndev_name), "dummy%d", id); addattr_l(&rdma_req.nlh, sizeof(rdma_req), RDMA_NLDEV_ATTR_DEV_NAME, dev_name, strlen(dev_name) + 1); addattr_l(&rdma_req.nlh, sizeof(rdma_req), RDMA_NLDEV_ATTR_LINK_TYPE, "siw", 4); addattr_l(&rdma_req.nlh, sizeof(rdma_req), RDMA_NLDEV_ATTR_NDEV_NAME, ndev_name, strlen(ndev_name) + 1); int ret = send_nl_req(rdma_fd, &rdma_req.nlh); if (close(rdma_fd) < 0) { printf("[-] close failed: %s\n", strerror(errno)); exit(1); } return ret; } void *ns_spammer(void *arg) { time_t start = time(NULL); while (time(NULL) - start < 160) { if (unshare(CLONE_NEWNET) != 0) { if (errno == ENOSPC) { usleep(10000); // Sleep briefly if we hit max_net_namespaces limit continue; } printf("[-] unshare(CLONE_NEWNET) failed in thread: %s\n", strerror(errno)); exit(1); } } return NULL; } int main() { printf("[*] Starting reproducer...\n"); int fd = open("/proc/sys/kernel/hung_task_timeout_secs", O_WRONLY); if (fd < 0) { printf("[-] open(/proc/sys/kernel/hung_task_timeout_secs) failed: %s\n", strerror(errno)); exit(1); } if (write(fd, "15\n", 3) < 0) { printf("[-] write to hung_task_timeout_secs failed: %s\n", strerror(errno)); exit(1); } if (close(fd) < 0) { printf("[-] close failed: %s\n", strerror(errno)); exit(1); } printf("[+] Set hung_task_timeout_secs to 15.\n"); if (unshare(CLONE_NEWNET) < 0) { printf("[-] unshare(CLONE_NEWNET) failed: %s\n", strerror(errno)); exit(1); } printf("[+] unshare(CLONE_NEWNET) successful.\n"); int num_devices = 100; for (int i = 0; i < num_devices; i++) { if (create_dummy(i) < 0) { printf("[-] Failed to create dummy%d: %s\n", i, strerror(errno)); exit(1); } if (set_if_up(i) < 0) { printf("[-] Failed to set dummy%d up: %s\n", i, strerror(errno)); exit(1); } if (create_siw(i) < 0) { printf("[-] Failed to create siw%d: %s\n", i, strerror(errno)); exit(1); } } printf("[+] Created %d dummy and siw devices.\n", num_devices); printf("[*] Spawning threads to cause contention...\n"); int num_threads = 15000; pthread_t *threads = malloc(num_threads * sizeof(pthread_t)); if (!threads) { printf("[-] Failed to allocate memory for threads\n"); exit(1); } pthread_attr_t attr; if (pthread_attr_init(&attr) != 0) { printf("[-] pthread_attr_init failed: %s\n", strerror(errno)); exit(1); } size_t stack_size = 128 * 1024; // 128KB stack to prevent OOM if (pthread_attr_setstacksize(&attr, stack_size) != 0) { printf("[-] pthread_attr_setstacksize failed: %s\n", strerror(errno)); exit(1); } int created = 0; for (int i = 0; i < num_threads; i++) { int ret = pthread_create(&threads[i], &attr, ns_spammer, NULL); if (ret == 0) { created++; } else { printf("[-] pthread_create failed at thread %d: %s\n", i, strerror(ret)); break; } } printf("[+] Spawned %d threads successfully.\n", created); for (int i = 0; i < created; i++) { pthread_join(threads[i], NULL); } printf("[+] Reproducer finished. Waiting for asynchronous cleanup...\n"); sleep(10); free(threads); return 0; }