// https://syzkaller.appspot.com/bug?id=4cd2fc2a744de3c4c30d1ede43e8c807aa67fda7 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #if __has_include() #include #endif #if __has_include() #include #elif __has_include() #include #endif #if __has_include() #include #endif #ifndef VETH_INFO_PEER #define VETH_INFO_PEER 1 #endif /* Fallback definitions for AF_RDS and SOL_RDS */ #ifndef AF_RDS #define AF_RDS 21 #endif #ifndef SOL_RDS #define SOL_RDS 276 #endif #ifndef SO_RDS_TRANSPORT #define SO_RDS_TRANSPORT 8 #endif #ifndef RDS_TRANS_IB #define RDS_TRANS_IB 0 #endif /* Fallback definitions for RDMA netlink */ #ifndef NETLINK_RDMA #define NETLINK_RDMA 20 #endif #ifndef RDMA_NL_NLDEV #define RDMA_NL_NLDEV 5 #endif #ifndef RDMA_NLDEV_CMD_NEWLINK #define RDMA_NLDEV_CMD_NEWLINK 3 #endif #ifndef RDMA_NL_GET_TYPE #define RDMA_NL_GET_TYPE(client, op) (((client) << 10) + (op)) #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 static void rta_add_attr(struct nlmsghdr *nlh, int maxlen, int type, const void *data, int datalen) { int len = RTA_LENGTH(datalen); if (NLMSG_ALIGN(nlh->nlmsg_len) + RTA_ALIGN(len) > maxlen) { printf("[-] rta_add_attr: buffer overflow\n"); exit(1); } struct rtattr *rta = (struct rtattr *)((char *)nlh + NLMSG_ALIGN(nlh->nlmsg_len)); rta->rta_type = type; rta->rta_len = len; if (datalen > 0 && data) memcpy(RTA_DATA(rta), data, datalen); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + RTA_ALIGN(len); } static struct rtattr *rta_nest_start(struct nlmsghdr *nlh, int maxlen, int type) { int len = RTA_LENGTH(0); if (NLMSG_ALIGN(nlh->nlmsg_len) + RTA_ALIGN(len) > maxlen) { printf("[-] rta_nest_start: buffer overflow\n"); exit(1); } struct rtattr *rta = (struct rtattr *)((char *)nlh + NLMSG_ALIGN(nlh->nlmsg_len)); rta->rta_type = type; rta->rta_len = len; nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + RTA_ALIGN(len); return rta; } static void rta_nest_end(struct nlmsghdr *nlh, struct rtattr *rta) { rta->rta_len = (char *)nlh + nlh->nlmsg_len - (char *)rta; } static void nla_put_str(struct nlmsghdr *nlh, uint16_t type, const char *str) { uint16_t len = (uint16_t)(strlen(str) + 1); uint16_t total = sizeof(struct nlattr) + len; struct nlattr *nla = (struct nlattr *)((char *)nlh + NLMSG_ALIGN(nlh->nlmsg_len)); nla->nla_type = type; nla->nla_len = total; memcpy((char *)nla + sizeof(struct nlattr), str, len); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + NLA_ALIGN(total); } static void delete_link(const char *name) { int nlfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (nlfd < 0) return; struct timeval tv = { .tv_sec = 1, .tv_usec = 0 }; setsockopt(nlfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); char buf[512]; memset(buf, 0, sizeof(buf)); struct nlmsghdr *nlh = (struct nlmsghdr *)buf; nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; nlh->nlmsg_type = RTM_DELLINK; struct ifinfomsg *ifi = (struct ifinfomsg *)NLMSG_DATA(nlh); ifi->ifi_family = AF_UNSPEC; rta_add_attr(nlh, sizeof(buf), IFLA_IFNAME, name, (int)(strlen(name) + 1)); struct sockaddr_nl sa; memset(&sa, 0, sizeof(sa)); sa.nl_family = AF_NETLINK; sendto(nlfd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&sa, sizeof(sa)); char ack_buf[512]; recv(nlfd, ack_buf, sizeof(ack_buf), 0); close(nlfd); } static int create_veth_pair(const char *name0, const char *name1) { int nlfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (nlfd < 0) { printf("[-] Failed to create NETLINK_ROUTE socket: %s\n", strerror(errno)); return -1; } struct timeval tv = { .tv_sec = 3, .tv_usec = 0 }; if (setsockopt(nlfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { printf("[-] setsockopt(SO_RCVTIMEO) failed: %s\n", strerror(errno)); close(nlfd); return -1; } char buf[1024]; memset(buf, 0, sizeof(buf)); struct nlmsghdr *nlh = (struct nlmsghdr *)buf; nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK; nlh->nlmsg_type = RTM_NEWLINK; struct ifinfomsg *ifi = (struct ifinfomsg *)NLMSG_DATA(nlh); ifi->ifi_family = AF_UNSPEC; rta_add_attr(nlh, sizeof(buf), IFLA_IFNAME, name0, (int)(strlen(name0) + 1)); struct rtattr *linkinfo = rta_nest_start(nlh, sizeof(buf), IFLA_LINKINFO); rta_add_attr(nlh, sizeof(buf), 1 /* IFLA_INFO_KIND */, "veth", (int)(strlen("veth") + 1)); struct rtattr *infodata = rta_nest_start(nlh, sizeof(buf), 2 /* IFLA_INFO_DATA */); struct rtattr *peer = rta_nest_start(nlh, sizeof(buf), VETH_INFO_PEER); struct ifinfomsg ifi_peer; memset(&ifi_peer, 0, sizeof(ifi_peer)); ifi_peer.ifi_family = AF_UNSPEC; memcpy((char *)nlh + nlh->nlmsg_len, &ifi_peer, sizeof(ifi_peer)); nlh->nlmsg_len += sizeof(ifi_peer); rta_add_attr(nlh, sizeof(buf), IFLA_IFNAME, name1, (int)(strlen(name1) + 1)); rta_nest_end(nlh, peer); rta_nest_end(nlh, infodata); rta_nest_end(nlh, linkinfo); struct sockaddr_nl sa; memset(&sa, 0, sizeof(sa)); sa.nl_family = AF_NETLINK; int res = sendto(nlfd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&sa, sizeof(sa)); if (res < 0) { printf("[-] Failed to send RTM_NEWLINK for veth pair: %s\n", strerror(errno)); close(nlfd); return -1; } char ack_buf[512]; int ack_len = recv(nlfd, ack_buf, sizeof(ack_buf), 0); close(nlfd); if (ack_len >= (int)sizeof(struct nlmsghdr)) { struct nlmsghdr *ack = (struct nlmsghdr *)ack_buf; if (ack->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *errmsg = (struct nlmsgerr *)NLMSG_DATA(ack); if (errmsg->error != 0 && errmsg->error != -EEXIST) { printf("[-] RTM_NEWLINK returned error: %s (%d)\n", strerror(-errmsg->error), errmsg->error); return -1; } } } printf("[+] Veth pair (%s <-> %s) created successfully.\n", name0, name1); return 0; } static int setup_netif(const char *name, const char *ip_str, const char *mask_str) { int inet_sock = socket(AF_INET, SOCK_DGRAM, 0); if (inet_sock < 0) { printf("[-] Failed to create AF_INET socket: %s\n", strerror(errno)); return -1; } struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, name, IFNAMSIZ - 1); struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr; sin->sin_family = AF_INET; if (inet_pton(AF_INET, ip_str, &sin->sin_addr) <= 0) { printf("[-] Failed to parse IP %s: %s\n", ip_str, strerror(errno)); close(inet_sock); return -1; } int res = ioctl(inet_sock, SIOCSIFADDR, &ifr); if (res < 0) { printf("[-] ioctl(SIOCSIFADDR) for %s failed: %s\n", name, strerror(errno)); close(inet_sock); return -1; } memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, name, IFNAMSIZ - 1); sin = (struct sockaddr_in *)&ifr.ifr_netmask; sin->sin_family = AF_INET; if (inet_pton(AF_INET, mask_str, &sin->sin_addr) <= 0) { printf("[-] Failed to parse netmask %s: %s\n", mask_str, strerror(errno)); close(inet_sock); return -1; } res = ioctl(inet_sock, SIOCSIFNETMASK, &ifr); if (res < 0) { printf("[-] ioctl(SIOCSIFNETMASK) for %s failed: %s\n", name, strerror(errno)); close(inet_sock); return -1; } memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, name, IFNAMSIZ - 1); res = ioctl(inet_sock, SIOCGIFFLAGS, &ifr); if (res < 0) { printf("[-] ioctl(SIOCGIFFLAGS) for %s failed: %s\n", name, strerror(errno)); close(inet_sock); return -1; } ifr.ifr_flags |= IFF_UP | IFF_RUNNING; res = ioctl(inet_sock, SIOCSIFFLAGS, &ifr); if (res < 0) { printf("[-] ioctl(SIOCSIFFLAGS) for %s failed: %s\n", name, strerror(errno)); close(inet_sock); return -1; } close(inet_sock); printf("[+] Interface %s configured with IP %s and brought UP successfully.\n", name, ip_str); return 0; } static int setup_rxe_attempt(const char *ibdev_name, const char *netdev_name) { int nlfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_RDMA); if (nlfd < 0) { printf("[-] Failed to create NETLINK_RDMA socket: %s\n", strerror(errno)); return -errno; } struct timeval tv = { .tv_sec = 3, .tv_usec = 0 }; if (setsockopt(nlfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { printf("[-] setsockopt(SO_RCVTIMEO) failed: %s\n", strerror(errno)); close(nlfd); return -errno; } char buf[512]; memset(buf, 0, sizeof(buf)); struct nlmsghdr *nlh = (struct nlmsghdr *)buf; nlh->nlmsg_len = NLMSG_LENGTH(0); nlh->nlmsg_type = RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_NEWLINK); nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; nla_put_str(nlh, RDMA_NLDEV_ATTR_DEV_NAME, ibdev_name); nla_put_str(nlh, RDMA_NLDEV_ATTR_LINK_TYPE, "rxe"); nla_put_str(nlh, RDMA_NLDEV_ATTR_NDEV_NAME, netdev_name); struct sockaddr_nl sa; memset(&sa, 0, sizeof(sa)); sa.nl_family = AF_NETLINK; int res = sendto(nlfd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&sa, sizeof(sa)); if (res < 0) { int err = -errno; printf("[-] Failed to send RDMA_NLDEV_CMD_NEWLINK: %s\n", strerror(errno)); close(nlfd); return err; } char ack_buf[512]; int ack_len = recv(nlfd, ack_buf, sizeof(ack_buf), 0); close(nlfd); if (ack_len >= (int)sizeof(struct nlmsghdr)) { struct nlmsghdr *ack = (struct nlmsghdr *)ack_buf; if (ack->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *errmsg = (struct nlmsgerr *)NLMSG_DATA(ack); if (errmsg->error != 0) { return errmsg->error; } } } return 0; } static int setup_rxe(const char *netdev_name) { const char *names[] = { "rxe0", "rxe1", "rxe2", "rxe3" }; int err = 0; for (size_t i = 0; i < sizeof(names) / sizeof(names[0]); i++) { err = setup_rxe_attempt(names[i], netdev_name); if (err == 0) { printf("[+] RDMA device %s configured on %s successfully.\n", names[i], netdev_name); return 0; } if (err == -EEXIST) { printf("[+] RDMA device already configured on %s.\n", netdev_name); return 0; } printf("[*] setup_rxe with name %s returned %d (%s), trying next candidate...\n", names[i], err, strerror(-err)); } printf("[-] Failed to configure RDMA device on %s (last error: %s)\n", netdev_name, strerror(-err)); return -1; } int main(void) { setvbuf(stdout, NULL, _IONBF, 0); printf("[*] Starting reproducer for circular locking dependency in rds_trans_get_preferred...\n"); /* Step 1: Clean up any stale network devices from prior runs */ printf("[*] Cleaning up stale network devices...\n"); delete_link("dummy0"); delete_link("veth0"); delete_link("veth1"); usleep(100000); /* Step 2: Create veth0 <-> veth1 pair */ int res = create_veth_pair("veth0", "veth1"); if (res < 0) { printf("[-] Failed to create veth pair: %s\n", strerror(errno)); exit(1); } printf("[+] Veth network interface pair created.\n"); usleep(50000); /* Step 3: Assign IP addresses to veth interfaces and bring them UP */ res = setup_netif("veth0", "172.20.20.170", "255.255.255.0"); if (res < 0) { printf("[-] Failed to configure veth0 interface.\n"); exit(1); } res = setup_netif("veth1", "172.20.20.171", "255.255.255.0"); if (res < 0) { printf("[-] Failed to configure veth1 interface.\n"); exit(1); } printf("[+] IP addresses configured and interfaces brought UP.\n"); usleep(50000); /* Step 4: Configure Soft-RoCE (rxe) device on veth0 */ res = setup_rxe("veth0"); if (res < 0) { printf("[-] Failed to configure soft-RoCE device on veth0.\n"); exit(1); } printf("[+] Soft-RoCE device on veth0 verified.\n"); usleep(200000); /* Step 5: Ensure rds_rdma module is loaded and RDMA listener is initialized */ int init_sock = socket(AF_RDS, SOCK_SEQPACKET, 0); if (init_sock < 0) { printf("[-] socket(AF_RDS) failed: %s\n", strerror(errno)); exit(1); } int trans = RDS_TRANS_IB; res = setsockopt(init_sock, SOL_RDS, SO_RDS_TRANSPORT, &trans, sizeof(trans)); if (res < 0) { printf("[-] setsockopt(SOL_RDS, SO_RDS_TRANSPORT, RDS_TRANS_IB) failed: %s\n", strerror(errno)); close(init_sock); exit(1); } printf("[+] Initialized RDS RDMA transport and verified module load.\n"); close(init_sock); usleep(200000); /* * Step 6: Create and bind RDS sockets without explicitly setting SO_RDS_TRANSPORT. * When rds_bind() runs with rs_transport == NULL, it invokes rds_trans_get_preferred(), * which takes down_read(&rds_trans_sem), calls rds_ib_laddr_check() -> rds_ib_laddr_check_cm(), * and rdma_destroy_id(), taking &id_priv->handler_mutex. * This registers the lock dependency edge: rds_trans_sem -> &id_priv->handler_mutex. */ int sock_srv = socket(AF_RDS, SOCK_SEQPACKET, 0); if (sock_srv < 0) { printf("[-] Failed to create server AF_RDS socket: %s\n", strerror(errno)); exit(1); } struct sockaddr_in sin_srv; memset(&sin_srv, 0, sizeof(sin_srv)); sin_srv.sin_family = AF_INET; sin_srv.sin_addr.s_addr = inet_addr("172.20.20.170"); sin_srv.sin_port = htons(2000); res = bind(sock_srv, (struct sockaddr *)&sin_srv, sizeof(sin_srv)); if (res < 0) { printf("[-] bind() for server AF_RDS socket failed: %s\n", strerror(errno)); close(sock_srv); exit(1); } printf("[+] Server AF_RDS socket bound to 172.20.20.170:2000 (established rds_trans_sem -> handler_mutex edge).\n"); int sock_cli = socket(AF_RDS, SOCK_SEQPACKET, 0); if (sock_cli < 0) { printf("[-] Failed to create client AF_RDS socket: %s\n", strerror(errno)); close(sock_srv); exit(1); } struct sockaddr_in sin_cli; memset(&sin_cli, 0, sizeof(sin_cli)); sin_cli.sin_family = AF_INET; sin_cli.sin_addr.s_addr = inet_addr("172.20.20.170"); sin_cli.sin_port = htons(2001); res = bind(sock_cli, (struct sockaddr *)&sin_cli, sizeof(sin_cli)); if (res < 0) { printf("[-] bind() for client AF_RDS socket failed: %s\n", strerror(errno)); close(sock_cli); close(sock_srv); exit(1); } printf("[+] Client AF_RDS socket bound to 172.20.20.170:2001.\n"); /* * Step 7: Send datagrams to 172.20.20.170 to initiate an RDMA CM connection. * When rds_connect_worker initiates an RDMA connection over Soft-RoCE loopback, * the incoming connection request triggers cma_ib_req_handler in the ib_cm workqueue thread. * cma_ib_req_handler holds &id_priv->handler_mutex and calls: * rds_rdma_cm_event_handler_cmn -> rds_ib_cm_handle_connect -> rds_conn_create -> * __rds_conn_create -> rds_trans_get_preferred -> down_read(&rds_trans_sem). * This completes the cycle and triggers the lockdep circular locking dependency warning. */ printf("[*] Transmitting datagrams to trigger incoming RDMA CM connect request...\n"); for (int i = 0; i < 5; i++) { struct sockaddr_in dest; memset(&dest, 0, sizeof(dest)); dest.sin_family = AF_INET; dest.sin_addr.s_addr = inet_addr("172.20.20.170"); dest.sin_port = htons(2000); res = sendto(sock_cli, "test", 4, 0, (struct sockaddr *)&dest, sizeof(dest)); if (res < 0) { printf("[-] sendto from client to server failed: %s\n", strerror(errno)); close(sock_cli); close(sock_srv); exit(1); } printf("[+] Transmitted message iteration %d (%d bytes sent).\n", i + 1, res); dest.sin_port = htons(2001); res = sendto(sock_srv, "resp", 4, 0, (struct sockaddr *)&dest, sizeof(dest)); if (res < 0) { printf("[-] sendto from server to client failed: %s\n", strerror(errno)); close(sock_cli); close(sock_srv); exit(1); } usleep(100000); } /* Step 8: Allow sufficient delay for the kernel workqueue to process events */ printf("[*] Sleeping 3 seconds to allow asynchronous kernel workers (ib_cm cm_work_handler) to run...\n"); sleep(3); close(sock_cli); close(sock_srv); printf("[+] Reproducer completed successfully.\n"); return 0; }