// https://syzkaller.appspot.com/bug?id=e80a7ad3b983ea425b8ecef5add4110bf7e2dc19 #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 #include #include #ifndef NBD_MAJOR #define NBD_MAJOR 43 #endif #ifndef NBD_GENL_FAMILY_NAME #define NBD_GENL_FAMILY_NAME "nbd" #endif #ifndef NBD_ATTR_INDEX #define NBD_ATTR_INDEX 1 #endif #ifndef NBD_ATTR_SIZE_BYTES #define NBD_ATTR_SIZE_BYTES 2 #endif #ifndef NBD_ATTR_BLOCK_SIZE_BYTES #define NBD_ATTR_BLOCK_SIZE_BYTES 3 #endif #ifndef NBD_ATTR_TIMEOUT #define NBD_ATTR_TIMEOUT 4 #endif #ifndef NBD_ATTR_SERVER_FLAGS #define NBD_ATTR_SERVER_FLAGS 5 #endif #ifndef NBD_ATTR_CLIENT_FLAGS #define NBD_ATTR_CLIENT_FLAGS 6 #endif #ifndef NBD_ATTR_SOCKETS #define NBD_ATTR_SOCKETS 7 #endif #ifndef NBD_SOCK_ITEM #define NBD_SOCK_ITEM 1 #endif #ifndef NBD_SOCK_FD #define NBD_SOCK_FD 1 #endif #ifndef NBD_CMD_CONNECT #define NBD_CMD_CONNECT 1 #endif #ifndef NBD_CMD_DISCONNECT #define NBD_CMD_DISCONNECT 2 #endif #ifndef NBD_CMD_RECONFIGURE #define NBD_CMD_RECONFIGURE 3 #endif #ifndef NBD_REQUEST_MAGIC #define NBD_REQUEST_MAGIC 0x25609513 #endif #ifndef NBD_REPLY_MAGIC #define NBD_REPLY_MAGIC 0x67446698 #endif #ifndef NBD_CMD_READ #define NBD_CMD_READ 0 #endif #ifndef NBD_CMD_WRITE #define NBD_CMD_WRITE 1 #endif #ifndef NBD_CMD_DISC #define NBD_CMD_DISC 2 #endif #ifndef NBD_FLAG_CAN_MULTI_CONN #define NBD_FLAG_CAN_MULTI_CONN (1 << 8) #endif #ifndef NFSD_FAMILY_NAME #define NFSD_FAMILY_NAME "nfsd" #endif #ifndef NFSD_CMD_THREADS_SET #define NFSD_CMD_THREADS_SET 2 #endif #ifndef NFSD_A_SERVER_THREADS #define NFSD_A_SERVER_THREADS 1 #endif #ifndef HWSIM_CMD_NEW_RADIO #define HWSIM_CMD_NEW_RADIO 4 #endif #ifndef HWSIM_ATTR_CHANNELS #define HWSIM_ATTR_CHANNELS 9 #endif #ifndef NL80211_GENL_NAME #define NL80211_GENL_NAME "nl80211" #endif #ifndef NL80211_CMD_SET_INTERFACE #define NL80211_CMD_SET_INTERFACE 6 #endif #ifndef NL80211_ATTR_IFINDEX #define NL80211_ATTR_IFINDEX 3 #endif #ifndef NL80211_ATTR_IFTYPE #define NL80211_ATTR_IFTYPE 5 #endif #ifndef NL80211_IFTYPE_MESH_POINT #define NL80211_IFTYPE_MESH_POINT 7 #endif static uint32_t nl_seq = 1; static void nl_attr_put(struct nlmsghdr *nlh, uint16_t type, const void *data, uint16_t len) { struct nlattr *nla = (struct nlattr *)((char *)nlh + NLMSG_ALIGN(nlh->nlmsg_len)); nla->nla_type = type; nla->nla_len = NLA_HDRLEN + len; if (len > 0 && data) memcpy((char *)nla + NLA_HDRLEN, data, len); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + NLA_ALIGN(nla->nla_len); } static void nl_attr_put_u32(struct nlmsghdr *nlh, uint16_t type, uint32_t val) { nl_attr_put(nlh, type, &val, sizeof(val)); } static void nl_attr_put_u64(struct nlmsghdr *nlh, uint16_t type, uint64_t val) { nl_attr_put(nlh, type, &val, sizeof(val)); } static struct nlattr *nl_attr_nest_start(struct nlmsghdr *nlh, uint16_t type) { struct nlattr *nest = (struct nlattr *)((char *)nlh + NLMSG_ALIGN(nlh->nlmsg_len)); nest->nla_type = type | NLA_F_NESTED; nest->nla_len = NLA_HDRLEN; nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + NLA_HDRLEN; return nest; } static void nl_attr_nest_end(struct nlmsghdr *nlh, struct nlattr *nest) { nest->nla_len = (char *)nlh + nlh->nlmsg_len - (char *)nest; nlh->nlmsg_len = (char *)nest + NLA_ALIGN(nest->nla_len) - (char *)nlh; } static int recv_nl_ack(int nl_fd) { char buf[8192]; while (1) { ssize_t len = recv(nl_fd, buf, sizeof(buf), 0); if (len < 0) { if (errno == EINTR) continue; return -errno; } if (len == 0) return -EPIPE; struct nlmsghdr *nlh = (struct nlmsghdr *)buf; while (NLMSG_OK(nlh, (size_t)len)) { if (nlh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(nlh); return err->error; } if (nlh->nlmsg_type == NLMSG_DONE) return 0; nlh = NLMSG_NEXT(nlh, len); } } } static int resolve_genl_family(int nl_fd, const char *name) { char req_buf[512]; memset(req_buf, 0, sizeof(req_buf)); struct nlmsghdr *nlh = (struct nlmsghdr *)req_buf; nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); nlh->nlmsg_type = GENL_ID_CTRL; nlh->nlmsg_flags = NLM_F_REQUEST; nlh->nlmsg_seq = ++nl_seq; struct genlmsghdr *gh = (struct genlmsghdr *)NLMSG_DATA(nlh); gh->cmd = CTRL_CMD_GETFAMILY; gh->version = 1; int nla_len = NLA_HDRLEN + strlen(name) + 1; struct nlattr *nla = (struct nlattr *)((char *)gh + GENL_HDRLEN); nla->nla_len = nla_len; nla->nla_type = CTRL_ATTR_FAMILY_NAME; strcpy((char *)nla + NLA_HDRLEN, name); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len + NLA_ALIGN(nla_len)); ssize_t s_res = send(nl_fd, req_buf, nlh->nlmsg_len, 0); if (s_res < 0) { printf("[-] Failed to send GETFAMILY request for %s: %s\n", name, strerror(errno)); return -1; } static char resp_buf[65536]; memset(resp_buf, 0, sizeof(resp_buf)); ssize_t r_len = recv(nl_fd, resp_buf, sizeof(resp_buf), 0); if (r_len < 0) { printf("[-] Failed to recv GETFAMILY reply for %s: %s\n", name, strerror(errno)); return -1; } nlh = (struct nlmsghdr *)resp_buf; if (!NLMSG_OK(nlh, (size_t)r_len) || nlh->nlmsg_type == NLMSG_ERROR) { printf("[-] NLMSG not OK or ERROR for %s (r_len=%zd, nlmsg_len=%u, type=%u)\n", name, r_len, nlh->nlmsg_len, nlh->nlmsg_type); return -1; } gh = (struct genlmsghdr *)NLMSG_DATA(nlh); int rem = nlh->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN); nla = (struct nlattr *)((char *)gh + GENL_HDRLEN); while (rem >= (int)sizeof(struct nlattr)) { if (nla->nla_type == CTRL_ATTR_FAMILY_ID) { uint16_t fam_id = *(uint16_t *)((char *)nla + NLA_HDRLEN); return fam_id; } int aligned = NLA_ALIGN(nla->nla_len); if (aligned <= 0 || aligned > rem) break; rem -= aligned; nla = (struct nlattr *)((char *)nla + aligned); } printf("[-] Family '%s' not found in GETFAMILY response\n", name); return -1; } static void *nbd_server_worker(void *arg) { int fd = (int)(long)arg; char buf[4096]; char zeroes[4096]; memset(zeroes, 0, sizeof(zeroes)); while (1) { struct nbd_request req; ssize_t n = recv(fd, &req, sizeof(req), MSG_WAITALL); if (n != (ssize_t)sizeof(req)) break; if (ntohl(req.magic) != NBD_REQUEST_MAGIC) break; uint32_t type = ntohl(req.type) & 0xffff; uint32_t len = ntohl(req.len); struct nbd_reply rep; memset(&rep, 0, sizeof(rep)); rep.magic = htonl(NBD_REPLY_MAGIC); rep.error = 0; rep.cookie = req.cookie; if (type == NBD_CMD_WRITE) { uint32_t rem = len; int failed = 0; while (rem > 0) { uint32_t chunk = rem > sizeof(buf) ? sizeof(buf) : rem; ssize_t r = recv(fd, buf, chunk, MSG_WAITALL); if (r <= 0) { failed = 1; break; } rem -= (uint32_t)r; } if (failed) break; send(fd, &rep, sizeof(rep), MSG_NOSIGNAL); } else if (type == NBD_CMD_READ) { send(fd, &rep, sizeof(rep), MSG_NOSIGNAL); uint32_t rem = len; int failed = 0; while (rem > 0) { uint32_t chunk = rem > sizeof(zeroes) ? sizeof(zeroes) : rem; ssize_t s = send(fd, zeroes, chunk, MSG_NOSIGNAL); if (s <= 0) { failed = 1; break; } rem -= (uint32_t)s; } if (failed) break; } else if (type == NBD_CMD_DISC) { break; } else { send(fd, &rep, sizeof(rep), MSG_NOSIGNAL); } } return NULL; } int main(void) { setvbuf(stdout, NULL, _IONBF, 0); printf("[*] Starting reproducer for circular locking dependency in nfsd_file_cache_shutdown...\n"); int nl_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); if (nl_fd < 0) { printf("[-] Failed to create NETLINK_GENERIC socket: %s\n", strerror(errno)); exit(1); } printf("[+] NETLINK_GENERIC socket created successfully (fd=%d).\n", nl_fd); struct sockaddr_nl sa; memset(&sa, 0, sizeof(sa)); sa.nl_family = AF_NETLINK; int res = bind(nl_fd, (struct sockaddr *)&sa, sizeof(sa)); if (res < 0) { printf("[-] Failed to bind NETLINK_GENERIC socket: %s\n", strerror(errno)); exit(1); } printf("[+] NETLINK_GENERIC socket bound successfully.\n"); struct timeval tv; tv.tv_sec = 3; tv.tv_usec = 0; setsockopt(nl_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); int nbd_id = resolve_genl_family(nl_fd, NBD_GENL_FAMILY_NAME); if (nbd_id < 0) { printf("[-] Generic netlink family '%s' not found: %s\n", NBD_GENL_FAMILY_NAME, strerror(errno)); exit(1); } printf("[+] Resolved generic netlink family '%s' -> ID %d.\n", NBD_GENL_FAMILY_NAME, nbd_id); int nfsd_id = resolve_genl_family(nl_fd, NFSD_FAMILY_NAME); if (nfsd_id < 0) { printf("[-] Generic netlink family '%s' not found: %s\n", NFSD_FAMILY_NAME, strerror(errno)); exit(1); } printf("[+] Resolved generic netlink family '%s' -> ID %d.\n", NFSD_FAMILY_NAME, nfsd_id); int hwsim_id = resolve_genl_family(nl_fd, "MAC80211_HWSIM"); if (hwsim_id < 0) { printf("[*] Generic netlink family 'MAC80211_HWSIM' not found or built-in without netlink.\n"); } else { printf("[+] Resolved generic netlink family 'MAC80211_HWSIM' -> ID %d.\n", hwsim_id); } int nl80211_id = resolve_genl_family(nl_fd, NL80211_GENL_NAME); if (nl80211_id < 0) { printf("[-] Generic netlink family '%s' not found: %s\n", NL80211_GENL_NAME, strerror(errno)); exit(1); } printf("[+] Resolved generic netlink family '%s' -> ID %d.\n", NL80211_GENL_NAME, nl80211_id); // ========================================================================= // Phase 1: Setup Radio (wlan0) Early (Ensures radio sysfs is created cleanly) // ========================================================================= if (hwsim_id > 0) { char hwsim_buf[512]; memset(hwsim_buf, 0, sizeof(hwsim_buf)); struct nlmsghdr *nlh = (struct nlmsghdr *)hwsim_buf; nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); nlh->nlmsg_type = (uint16_t)hwsim_id; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; nlh->nlmsg_seq = ++nl_seq; struct genlmsghdr *gh = (struct genlmsghdr *)NLMSG_DATA(nlh); gh->cmd = HWSIM_CMD_NEW_RADIO; gh->version = 1; nl_attr_put_u32(nlh, HWSIM_ATTR_CHANNELS, 1); res = send(nl_fd, nlh, nlh->nlmsg_len, 0); if (res < 0) { printf("[-] Failed to send HWSIM_CMD_NEW_RADIO: %s\n", strerror(errno)); } else { int hw_ack = recv_nl_ack(nl_fd); printf("[+] HWSIM_CMD_NEW_RADIO returned: %d\n", hw_ack); } } // Locate wlan* interface int wlan_idx = 0; char wlan_name[IFNAMSIZ] = {0}; for (int attempt = 0; attempt < 10; attempt++) { struct if_nameindex *if_list = if_nameindex(); if (if_list) { for (int i = 0; if_list[i].if_index != 0; i++) { if (strncmp(if_list[i].if_name, "wlan", 4) == 0) { wlan_idx = if_list[i].if_index; strncpy(wlan_name, if_list[i].if_name, sizeof(wlan_name) - 1); break; } } if_freenameindex(if_list); } if (wlan_idx > 0) break; usleep(50000); } if (wlan_idx > 0) { printf("[+] Found wireless interface '%s' (index %d)\n", wlan_name, wlan_idx); int sk = socket(AF_INET, SOCK_DGRAM, 0); if (sk >= 0) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, wlan_name, IFNAMSIZ - 1); if (ioctl(sk, SIOCGIFFLAGS, &ifr) >= 0) { ifr.ifr_flags &= ~IFF_UP; if (ioctl(sk, SIOCSIFFLAGS, &ifr) >= 0) { printf("[+] Brought down interface %s\n", wlan_name); } } close(sk); } } else { printf("[*] Warning: No wlan* interface detected.\n"); } // ========================================================================= // Phase 2: NFSD Shutdown (Link #7: cpu_hotplug_lock -> slab_mutex) // Run BEFORE NBD_CMD_RECONFIGURE so RPC sysfs directory registration // does not collide with cpu_hotplug_lock / kernfs_rwsem. // ========================================================================= { char nfsd_buf[512]; memset(nfsd_buf, 0, sizeof(nfsd_buf)); struct nlmsghdr *nlh = (struct nlmsghdr *)nfsd_buf; nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); nlh->nlmsg_type = (uint16_t)nfsd_id; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; nlh->nlmsg_seq = ++nl_seq; struct genlmsghdr *gh = (struct genlmsghdr *)NLMSG_DATA(nlh); gh->cmd = NFSD_CMD_THREADS_SET; gh->version = 1; nl_attr_put_u32(nlh, NFSD_A_SERVER_THREADS, 1); res = send(nl_fd, nlh, nlh->nlmsg_len, 0); if (res < 0) { printf("[-] Failed to send NFSD_CMD_THREADS_SET: %s\n", strerror(errno)); exit(1); } int ack = recv_nl_ack(nl_fd); if (ack < 0 && ack != -EIO) { printf("[-] NFSD_CMD_THREADS_SET unexpected error: %s\n", strerror(-ack)); exit(1); } printf("[+] NFSD_CMD_THREADS_SET completed (established cpu_hotplug_lock -> slab_mutex via nfsd_file_cache_shutdown, ret=%d).\n", ack); usleep(50000); } // ========================================================================= // Phase 3: NBD Connect with 2 Sockets (Links #2 & #3: q_usage_counter -> elevator_lock -> set->srcu) // ========================================================================= struct stat st; res = stat("/dev/nbd0", &st); if (res < 0) { printf("[*] /dev/nbd0 not found, creating device node...\n"); res = mknod("/dev/nbd0", S_IFBLK | 0660, makedev(NBD_MAJOR, 0)); if (res < 0) { printf("[-] Failed to mknod /dev/nbd0: %s\n", strerror(errno)); exit(1); } printf("[+] mknod /dev/nbd0 successful.\n"); } else { printf("[+] /dev/nbd0 exists.\n"); } // Disconnect /dev/nbd0 if previously active { char disc_buf[256]; memset(disc_buf, 0, sizeof(disc_buf)); struct nlmsghdr *nlh = (struct nlmsghdr *)disc_buf; nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); nlh->nlmsg_type = (uint16_t)nbd_id; nlh->nlmsg_flags = NLM_F_REQUEST; nlh->nlmsg_seq = ++nl_seq; struct genlmsghdr *gh = (struct genlmsghdr *)NLMSG_DATA(nlh); gh->cmd = NBD_CMD_DISCONNECT; gh->version = 1; nl_attr_put_u32(nlh, NBD_ATTR_INDEX, 0); send(nl_fd, nlh, nlh->nlmsg_len, 0); usleep(50000); char drain_buf[1024]; recv(nl_fd, drain_buf, sizeof(drain_buf), MSG_DONTWAIT); } int srv = socket(AF_INET, SOCK_STREAM, 0); if (srv < 0) { printf("[-] Failed to create listening TCP socket: %s\n", strerror(errno)); exit(1); } int opt = 1; setsockopt(srv, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); sin.sin_port = 0; res = bind(srv, (struct sockaddr *)&sin, sizeof(sin)); if (res < 0) { printf("[-] Failed to bind TCP server: %s\n", strerror(errno)); exit(1); } socklen_t slen = sizeof(sin); res = getsockname(srv, (struct sockaddr *)&sin, &slen); if (res < 0) { printf("[-] Failed to getsockname: %s\n", strerror(errno)); exit(1); } res = listen(srv, 8); if (res < 0) { printf("[-] Failed to listen: %s\n", strerror(errno)); exit(1); } printf("[+] TCP server listening on port %d.\n", ntohs(sin.sin_port)); int cli1a = socket(AF_INET, SOCK_STREAM, 0); if (cli1a < 0) { printf("[-] Failed to create cli1a socket: %s\n", strerror(errno)); exit(1); } res = connect(cli1a, (struct sockaddr *)&sin, sizeof(sin)); if (res < 0) { printf("[-] Failed to connect cli1a: %s\n", strerror(errno)); exit(1); } int acc1a = accept(srv, NULL, NULL); if (acc1a < 0) { printf("[-] Failed to accept acc1a: %s\n", strerror(errno)); exit(1); } printf("[+] Established TCP connection 1a (cli1a=%d, acc1a=%d).\n", cli1a, acc1a); pthread_t srv_tid1a; res = pthread_create(&srv_tid1a, NULL, nbd_server_worker, (void *)(long)acc1a); if (res != 0) { printf("[-] Failed to create worker 1a: %s\n", strerror(res)); exit(1); } int cli1b = socket(AF_INET, SOCK_STREAM, 0); if (cli1b < 0) { printf("[-] Failed to create cli1b socket: %s\n", strerror(errno)); exit(1); } res = connect(cli1b, (struct sockaddr *)&sin, sizeof(sin)); if (res < 0) { printf("[-] Failed to connect cli1b: %s\n", strerror(errno)); exit(1); } int acc1b = accept(srv, NULL, NULL); if (acc1b < 0) { printf("[-] Failed to accept acc1b: %s\n", strerror(errno)); exit(1); } printf("[+] Established TCP connection 1b (cli1b=%d, acc1b=%d).\n", cli1b, acc1b); pthread_t srv_tid1b; res = pthread_create(&srv_tid1b, NULL, nbd_server_worker, (void *)(long)acc1b); if (res != 0) { printf("[-] Failed to create worker 1b: %s\n", strerror(res)); exit(1); } printf("[+] Started background NBD server worker threads 1a and 1b.\n"); char conn_buf[1024]; memset(conn_buf, 0, sizeof(conn_buf)); struct nlmsghdr *nlh = (struct nlmsghdr *)conn_buf; nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); nlh->nlmsg_type = (uint16_t)nbd_id; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; nlh->nlmsg_seq = ++nl_seq; struct genlmsghdr *gh = (struct genlmsghdr *)NLMSG_DATA(nlh); gh->cmd = NBD_CMD_CONNECT; gh->version = 1; nl_attr_put_u32(nlh, NBD_ATTR_INDEX, 0); nl_attr_put_u64(nlh, NBD_ATTR_SIZE_BYTES, 1024 * 1024); nl_attr_put_u64(nlh, NBD_ATTR_BLOCK_SIZE_BYTES, 4096); nl_attr_put_u64(nlh, NBD_ATTR_SERVER_FLAGS, NBD_FLAG_CAN_MULTI_CONN); struct nlattr *nest_socks = nl_attr_nest_start(nlh, NBD_ATTR_SOCKETS); struct nlattr *item1a = nl_attr_nest_start(nlh, NBD_SOCK_ITEM); nl_attr_put_u32(nlh, NBD_SOCK_FD, (uint32_t)cli1a); nl_attr_nest_end(nlh, item1a); struct nlattr *item1b = nl_attr_nest_start(nlh, NBD_SOCK_ITEM); nl_attr_put_u32(nlh, NBD_SOCK_FD, (uint32_t)cli1b); nl_attr_nest_end(nlh, item1b); nl_attr_nest_end(nlh, nest_socks); res = send(nl_fd, nlh, nlh->nlmsg_len, 0); if (res < 0) { printf("[-] Failed to send NBD_CMD_CONNECT: %s\n", strerror(errno)); exit(1); } int ack = recv_nl_ack(nl_fd); if (ack < 0) { printf("[-] NBD_CMD_CONNECT returned error: %s\n", strerror(-ack)); exit(1); } printf("[+] NBD_CMD_CONNECT with 2 sockets successful (established q_usage_counter -> elevator_lock -> set->srcu).\n"); usleep(50000); // ========================================================================= // Phase 4: Mark Connection 1 Dead & Trigger Dead I/O // (Establishes Links #4 & #5: set->srcu -> &cmd->lock -> &nsock->tx_lock // cleanly without calling nbd_send_cmd or touching sk_lock-AF_INET) // ========================================================================= shutdown(acc1a, SHUT_RDWR); close(cli1a); pthread_join(srv_tid1a, NULL); close(acc1a); shutdown(acc1b, SHUT_RDWR); close(cli1b); pthread_join(srv_tid1b, NULL); close(acc1b); printf("[+] Closed TCP connections 1a and 1b to trigger nsock->dead cleanly.\n"); usleep(150000); // 150ms delay for recv_work to set nsock->dead = true on both queues // Issue a read request to /dev/nbd0 while sockets are dead: // nbd_handle_cmd() acquires &nsock->tx_lock under &cmd->lock, sees nsock->dead == true, // immediately unlocks &nsock->tx_lock, and returns BLK_STS_IOERR without calling tcp_sendmsg(). int dead_fd = open("/dev/nbd0", O_RDONLY | O_DIRECT | O_NONBLOCK); if (dead_fd < 0) dead_fd = open("/dev/nbd0", O_RDONLY | O_NONBLOCK); if (dead_fd >= 0) { char b[4096] __attribute__((aligned(4096))); ssize_t rd = read(dead_fd, b, sizeof(b)); printf("[+] Read from dead /dev/nbd0 returned %zd (errno=%s)\n", rd, strerror(errno)); close(dead_fd); } else { printf("[-] Failed to open /dev/nbd0 for dead read: %s\n", strerror(errno)); } usleep(100000); printf("[+] Dead I/O dispatched (established set->srcu -> cmd->lock -> nsock->tx_lock).\n"); // ========================================================================= // Phase 5: NBD Reconfigure (Link #6: &nsock->tx_lock -> cpu_hotplug_lock) // ========================================================================= int cli2a = socket(AF_INET, SOCK_STREAM, 0); if (cli2a < 0) { printf("[-] Failed to create cli2a socket: %s\n", strerror(errno)); exit(1); } res = connect(cli2a, (struct sockaddr *)&sin, sizeof(sin)); if (res < 0) { printf("[-] Failed to connect cli2a: %s\n", strerror(errno)); exit(1); } int acc2a = accept(srv, NULL, NULL); if (acc2a < 0) { printf("[-] Failed to accept acc2a: %s\n", strerror(errno)); exit(1); } printf("[+] Established TCP connection 2a (cli2a=%d, acc2a=%d).\n", cli2a, acc2a); pthread_t srv_tid2a; res = pthread_create(&srv_tid2a, NULL, nbd_server_worker, (void *)(long)acc2a); if (res != 0) { printf("[-] Failed to create worker 2a: %s\n", strerror(res)); exit(1); } int cli2b = socket(AF_INET, SOCK_STREAM, 0); if (cli2b < 0) { printf("[-] Failed to create cli2b socket: %s\n", strerror(errno)); exit(1); } res = connect(cli2b, (struct sockaddr *)&sin, sizeof(sin)); if (res < 0) { printf("[-] Failed to connect cli2b: %s\n", strerror(errno)); exit(1); } int acc2b = accept(srv, NULL, NULL); if (acc2b < 0) { printf("[-] Failed to accept acc2b: %s\n", strerror(errno)); exit(1); } printf("[+] Established TCP connection 2b (cli2b=%d, acc2b=%d).\n", cli2b, acc2b); pthread_t srv_tid2b; res = pthread_create(&srv_tid2b, NULL, nbd_server_worker, (void *)(long)acc2b); if (res != 0) { printf("[-] Failed to create worker 2b: %s\n", strerror(res)); exit(1); } printf("[+] Started background NBD server worker threads 2a and 2b.\n"); char reconf_buf[1024]; memset(reconf_buf, 0, sizeof(reconf_buf)); nlh = (struct nlmsghdr *)reconf_buf; nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); nlh->nlmsg_type = (uint16_t)nbd_id; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; nlh->nlmsg_seq = ++nl_seq; gh = (struct genlmsghdr *)NLMSG_DATA(nlh); gh->cmd = NBD_CMD_RECONFIGURE; gh->version = 1; nl_attr_put_u32(nlh, NBD_ATTR_INDEX, 0); nest_socks = nl_attr_nest_start(nlh, NBD_ATTR_SOCKETS); struct nlattr *item2a = nl_attr_nest_start(nlh, NBD_SOCK_ITEM); nl_attr_put_u32(nlh, NBD_SOCK_FD, (uint32_t)cli2a); nl_attr_nest_end(nlh, item2a); struct nlattr *item2b = nl_attr_nest_start(nlh, NBD_SOCK_ITEM); nl_attr_put_u32(nlh, NBD_SOCK_FD, (uint32_t)cli2b); nl_attr_nest_end(nlh, item2b); nl_attr_nest_end(nlh, nest_socks); res = send(nl_fd, nlh, nlh->nlmsg_len, 0); if (res < 0) { printf("[-] Failed to send NBD_CMD_RECONFIGURE: %s\n", strerror(errno)); exit(1); } ack = recv_nl_ack(nl_fd); if (ack < 0) { printf("[-] NBD_CMD_RECONFIGURE returned error: %s\n", strerror(-ack)); exit(1); } printf("[+] NBD_CMD_RECONFIGURE successful (established nsock->tx_lock -> cpu_hotplug_lock).\n"); // CRITICAL: DO NOT open or write to /dev/nbd0 after reconnecting! Keep sockets open. // ========================================================================= // Phase 6: Trigger Target Deadlock (Link #0: slab_mutex -> fs_reclaim) // Switch existing wlan0 to MESH_POINT. // ========================================================================= if (wlan_idx > 0) { char set_buf[512]; memset(set_buf, 0, sizeof(set_buf)); nlh = (struct nlmsghdr *)set_buf; nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); nlh->nlmsg_type = (uint16_t)nl80211_id; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; nlh->nlmsg_seq = ++nl_seq; gh = (struct genlmsghdr *)NLMSG_DATA(nlh); gh->cmd = NL80211_CMD_SET_INTERFACE; gh->version = 1; nl_attr_put_u32(nlh, NL80211_ATTR_IFINDEX, (uint32_t)wlan_idx); nl_attr_put_u32(nlh, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_MESH_POINT); printf("[*] Sending NL80211_CMD_SET_INTERFACE to switch %s to MESH_POINT...\n", wlan_name); res = send(nl_fd, nlh, nlh->nlmsg_len, 0); if (res > 0) { int set_ack = recv_nl_ack(nl_fd); printf("[*] NL80211_CMD_SET_INTERFACE returned: %d\n", set_ack); } } else { printf("[-] No wlan* interface available for SET_INTERFACE\n"); } sleep(2); close(nl_fd); close(srv); printf("[+] Reproducer execution completed successfully.\n"); return 0; }