// https://syzkaller.appspot.com/bug?id=6b5073f1052fa5fda49b4d39978a244c0c3d1bb0 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define NLMSG_TAIL(nmsg) \ ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) static int 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 -1; rta = NLMSG_TAIL(n); 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); return 0; } static struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type) { struct rtattr *nest = NLMSG_TAIL(n); addattr_l(n, maxlen, type, NULL, 0); return nest; } static int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest) { nest->rta_len = (void *)NLMSG_TAIL(n) - (void *)nest; return n->nlmsg_len; } static int netlink_recv_ack(int nl_fd) { char buf[4096]; while (1) { ssize_t len = recv(nl_fd, buf, sizeof(buf), 0); if (len < 0) { if (errno == EINTR) continue; return -errno; } for (struct nlmsghdr *nlh = (struct nlmsghdr *)buf; NLMSG_OK(nlh, (size_t)len); nlh = NLMSG_NEXT(nlh, len)) { if (nlh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(nlh); return err->error; } } } } static void delete_link(int nl_fd, const char *name) { struct { struct nlmsghdr n; struct ifinfomsg ifi; char buf[256]; } req; memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; req.n.nlmsg_type = RTM_DELLINK; req.ifi.ifi_family = AF_UNSPEC; addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, strlen(name) + 1); int res = send(nl_fd, &req, req.n.nlmsg_len, 0); if (res >= 0) { netlink_recv_ack(nl_fd); } } static int create_gretap(int nl_fd, const char *name, in_addr_t local, in_addr_t remote) { struct { struct nlmsghdr n; struct ifinfomsg ifi; char buf[1024]; } req; memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK; req.n.nlmsg_type = RTM_NEWLINK; req.ifi.ifi_family = AF_UNSPEC; addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, strlen(name) + 1); struct rtattr *linkinfo = addattr_nest(&req.n, sizeof(req), IFLA_LINKINFO); addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, "gretap", strlen("gretap")); struct rtattr *data = addattr_nest(&req.n, sizeof(req), IFLA_INFO_DATA); addattr_l(&req.n, sizeof(req), IFLA_GRE_LOCAL, &local, 4); addattr_l(&req.n, sizeof(req), IFLA_GRE_REMOTE, &remote, 4); addattr_nest_end(&req.n, data); addattr_nest_end(&req.n, linkinfo); int res = send(nl_fd, &req, req.n.nlmsg_len, 0); if (res < 0) { printf("[-] Failed to send RTM_NEWLINK for %s: %s\n", name, strerror(errno)); return -1; } int ack_err = netlink_recv_ack(nl_fd); if (ack_err < 0) { printf("[-] RTM_NEWLINK for %s failed: %s\n", name, strerror(-ack_err)); return ack_err; } printf("[+] Successfully created gretap %s.\n", name); return 0; } static uint32_t get_lapb_mask(void) { uint32_t mask = 0; for (int i = 0; i < 32; i++) { char devname[IFNAMSIZ]; snprintf(devname, sizeof(devname), "lapb%d", i); if (if_nametoindex(devname) > 0) mask |= (1U << i); } return mask; } static int find_new_lapb(uint32_t old_mask, char *out_name, size_t out_len) { for (int retry = 0; retry < 50; retry++) { for (int i = 0; i < 32; i++) { if (!(old_mask & (1U << i))) { char devname[IFNAMSIZ]; snprintf(devname, sizeof(devname), "lapb%d", i); if (if_nametoindex(devname) > 0) { strncpy(out_name, devname, out_len - 1); out_name[out_len - 1] = '\0'; return i; } } } usleep(10000); } return -1; } static int set_if_addr(int inet_fd, const char *dev_name, const char *ip_str, const char *mask_str) { struct ifreq ifr; struct sockaddr_in sin; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, dev_name, IFNAMSIZ - 1); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = inet_addr(ip_str); memcpy(&ifr.ifr_addr, &sin, sizeof(sin)); if (ioctl(inet_fd, SIOCSIFADDR, &ifr) < 0) return -errno; sin.sin_addr.s_addr = inet_addr(mask_str); memcpy(&ifr.ifr_netmask, &sin, sizeof(sin)); if (ioctl(inet_fd, SIOCSIFNETMASK, &ifr) < 0) return -errno; return 0; } static int set_if_up(int inet_fd, const char *dev_name, bool up) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, dev_name, IFNAMSIZ - 1); if (up) ifr.ifr_flags = IFF_UP | IFF_RUNNING; else ifr.ifr_flags = 0; if (ioctl(inet_fd, SIOCSIFFLAGS, &ifr) < 0) return -errno; return 0; } static int add_host_route(int inet_fd, const char *dst_ip_str, const char *dev_name) { struct rtentry rt; memset(&rt, 0, sizeof(rt)); struct sockaddr_in *dst = (struct sockaddr_in *)&rt.rt_dst; dst->sin_family = AF_INET; dst->sin_addr.s_addr = inet_addr(dst_ip_str); rt.rt_flags = RTF_UP | RTF_HOST; rt.rt_dev = (char *)dev_name; int res = ioctl(inet_fd, SIOCADDRT, &rt); if (res < 0) { if (errno == EEXIST) return 0; return -errno; } return 0; } static void send_x25_connect(int packet_fd, int ifindex) { struct sockaddr_ll sll; memset(&sll, 0, sizeof(sll)); sll.sll_family = AF_PACKET; sll.sll_protocol = htons(ETH_P_X25); sll.sll_ifindex = ifindex; unsigned char payload[1] = { 0x01 }; // X25_IFACE_CONNECT ssize_t res = sendto(packet_fd, payload, sizeof(payload), 0, (struct sockaddr *)&sll, sizeof(sll)); if (res < 0) { printf("[-] sendto AF_PACKET (index %d) failed: %s\n", ifindex, strerror(errno)); } else { printf("[+] sendto AF_PACKET (index %d) sent %zd bytes.\n", ifindex, res); } } static void try_x25_connect(const char *dev_name) { int fd = socket(AF_X25, SOCK_SEQPACKET, 0); if (fd < 0) { printf("[-] socket(AF_X25): %s\n", strerror(errno)); return; } struct x25_route_struct xrt; memset(&xrt, 0, sizeof(xrt)); strcpy(xrt.address.x25_addr, "1111111111"); xrt.sigdigits = 0; strncpy(xrt.device, dev_name, sizeof(xrt.device) - 1); int res = ioctl(fd, SIOCADDRT, &xrt); if (res < 0) { printf("[-] SIOCADDRT AF_X25 on %s: %s\n", dev_name, strerror(errno)); } else { printf("[+] SIOCADDRT AF_X25 on %s successful.\n", dev_name); } struct sockaddr_x25 sx25; memset(&sx25, 0, sizeof(sx25)); sx25.sx25_family = AF_X25; strcpy(sx25.sx25_addr.x25_addr, "1111111111"); connect(fd, (struct sockaddr *)&sx25, sizeof(sx25)); printf("[+] connect(AF_X25) on %s attempted.\n", dev_name); close(fd); } int main(void) { setvbuf(stdout, NULL, _IONBF, 0); printf("[*] Starting reproducer for possible deadlock in lapb_connect_request...\n"); int nl_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (nl_fd < 0) { printf("[-] Failed to open netlink socket: %s\n", strerror(errno)); exit(1); } printf("[+] socket(AF_NETLINK) successful.\n"); int inet_fd = socket(AF_INET, SOCK_DGRAM, 0); if (inet_fd < 0) { printf("[-] Failed to open inet socket: %s\n", strerror(errno)); exit(1); } printf("[+] socket(AF_INET) successful.\n"); int packet_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_X25)); if (packet_fd < 0) { printf("[-] Failed to open packet socket: %s\n", strerror(errno)); exit(1); } printf("[+] socket(AF_PACKET) successful.\n"); // Clean up old tunnels delete_link(nl_fd, "gt0"); delete_link(nl_fd, "gt1"); printf("[+] Cleaned up previous tunnels.\n"); // Assign IP 192.168.99.1/24 to lo and bring it UP int res = set_if_addr(inet_fd, "lo", "192.168.99.1", "255.255.255.0"); if (res < 0) { printf("[-] Failed to set IP on lo: %s\n", strerror(-res)); exit(1); } res = set_if_up(inet_fd, "lo", true); if (res < 0) { printf("[-] Failed to bring lo UP: %s\n", strerror(-res)); exit(1); } printf("[+] Assigned 192.168.99.1/24 to lo and brought lo UP.\n"); // Snapshot existing LAPB interfaces uint32_t mask_before_gt0 = get_lapb_mask(); printf("[*] Existing LAPB mask before gt0: 0x%08x\n", mask_before_gt0); // If lapb0 already exists, configure it if (if_nametoindex("lapb0") > 0) { set_if_addr(inet_fd, "lapb0", "192.168.99.100", "255.255.255.0"); set_if_up(inet_fd, "lapb0", true); add_host_route(inet_fd, "192.168.99.2", "lapb0"); printf("[+] Configured pre-existing lapb0 with route to 192.168.99.2\n"); } // Create GRE-Tap tunnel gt0 res = create_gretap(nl_fd, "gt0", inet_addr("192.168.99.1"), inet_addr("192.168.99.2")); if (res < 0) { printf("[-] create_gretap gt0 failed: %s\n", strerror(-res)); exit(1); } // Bring gt0 UP to trigger lapbeth_new_device res = set_if_up(inet_fd, "gt0", true); if (res < 0) { printf("[-] Failed to bring gt0 UP: %s\n", strerror(-res)); exit(1); } printf("[+] Brought gt0 UP.\n"); // Discover the LAPB device created for gt0 char lapb_name[IFNAMSIZ] = {0}; int lapb_idx = find_new_lapb(mask_before_gt0, lapb_name, sizeof(lapb_name)); if (lapb_idx < 0) { printf("[-] Failed to discover new LAPB device for gt0!\n"); exit(1); } int lapb_ifindex = if_nametoindex(lapb_name); printf("[+] Discovered LAPB device for gt0: %s (ifindex %d)\n", lapb_name, lapb_ifindex); // Configure lapb_name res = set_if_addr(inet_fd, lapb_name, "192.168.99.10", "255.255.255.0"); if (res < 0) { printf("[-] Failed to assign IP to %s: %s\n", lapb_name, strerror(-res)); exit(1); } printf("[+] Assigned IP 192.168.99.10/24 to %s.\n", lapb_name); res = set_if_up(inet_fd, lapb_name, true); if (res < 0) { printf("[-] Failed to bring %s UP: %s\n", lapb_name, strerror(-res)); exit(1); } printf("[+] Brought %s UP.\n", lapb_name); res = add_host_route(inet_fd, "192.168.99.2", lapb_name); if (res < 0) { printf("[-] Failed to add route for 192.168.99.2 via %s: %s\n", lapb_name, strerror(-res)); exit(1); } printf("[+] Added host route for 192.168.99.2 via %s.\n", lapb_name); // Snapshot mask before gt1 uint32_t mask_before_gt1 = get_lapb_mask(); // Create a second GRE-Tap tunnel gt1 pointing to 192.168.99.3 res = create_gretap(nl_fd, "gt1", inet_addr("192.168.99.1"), inet_addr("192.168.99.3")); if (res == 0) { set_if_up(inet_fd, "gt1", true); char lapb2_name[IFNAMSIZ] = {0}; int lapb2_idx = find_new_lapb(mask_before_gt1, lapb2_name, sizeof(lapb2_name)); if (lapb2_idx >= 0) { int lapb2_ifindex = if_nametoindex(lapb2_name); printf("[+] Discovered second LAPB device: %s (ifindex %d)\n", lapb2_name, lapb2_ifindex); // Route gt1 destination 192.168.99.3 via lapb_name add_host_route(inet_fd, "192.168.99.3", lapb_name); set_if_addr(inet_fd, lapb2_name, "192.168.99.20", "255.255.255.0"); // Bringing lapb2 UP triggers SABM to gt1 -> encapsulated to 192.168.99.3 -> routed to lapb_name! set_if_up(inet_fd, lapb2_name, true); printf("[+] Brought %s UP, triggering route traversal.\n", lapb2_name); } } // Toggle lapb_name DOWN and UP again to trigger NETDEV_UP with established route printf("[*] Toggling %s DOWN and UP to trigger lockdep registration...\n", lapb_name); set_if_up(inet_fd, lapb_name, false); set_if_up(inet_fd, lapb_name, true); add_host_route(inet_fd, "192.168.99.2", lapb_name); printf("[+] %s cycled UP and route restored.\n", lapb_name); // Trigger the deadlock cycle by sending X25_IFACE_CONNECT frames printf("[*] Sending X25_IFACE_CONNECT on %s via AF_PACKET...\n", lapb_name); send_x25_connect(packet_fd, lapb_ifindex); if (if_nametoindex("lapb0") > 0) { printf("[*] Sending X25_IFACE_CONNECT on lapb0 via AF_PACKET...\n"); send_x25_connect(packet_fd, if_nametoindex("lapb0")); } // Complementary trigger via AF_X25 connect() printf("[*] Triggering via AF_X25 connect()...\n"); try_x25_connect(lapb_name); printf("[+] Operations complete. Waiting for lockdep output...\n"); sleep(1); close(packet_fd); close(inet_fd); close(nl_fd); printf("[+] Reproducer finished.\n"); return 0; }