// https://syzkaller.appspot.com/bug?id=dc85862ccbc77f5df9ec456ce6b2e59019ef92cb #include #include #include #include #include #include #include #include #include #include #include #include #include struct rtattr *add_rta(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 NULL; rta = (struct rtattr *)(((char *)n) + NLMSG_ALIGN(n->nlmsg_len)); rta->rta_type = type; rta->rta_len = len; if (data) memcpy(RTA_DATA(rta), data, alen); n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); return rta; } struct rtattr *add_rta_nested(struct nlmsghdr *n, int maxlen, int type) { struct rtattr *rta = (struct rtattr *)(((char *)n) + NLMSG_ALIGN(n->nlmsg_len)); rta->rta_type = type; rta->rta_len = RTA_LENGTH(0); n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(rta->rta_len); return rta; } void end_rta_nested(struct nlmsghdr *n, struct rtattr *rta) { rta->rta_len = (char *)n + NLMSG_ALIGN(n->nlmsg_len) - (char *)rta; } static void create_dummy_interface(const char *name) { int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd < 0) { printf("[-] Failed to socket(NETLINK_ROUTE): %s\n", strerror(errno)); return; } char buf[1024] = {0}; struct nlmsghdr *n = (struct nlmsghdr *)buf; n->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); n->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK; n->nlmsg_type = RTM_NEWLINK; struct ifinfomsg *ifi = NLMSG_DATA(n); ifi->ifi_family = AF_UNSPEC; add_rta(n, sizeof(buf), IFLA_IFNAME, name, strlen(name) + 1); struct rtattr *linkinfo = add_rta_nested(n, sizeof(buf), IFLA_LINKINFO); add_rta(n, sizeof(buf), IFLA_INFO_KIND, "dummy", strlen("dummy") + 1); end_rta_nested(n, linkinfo); if (send(fd, n, n->nlmsg_len, 0) < 0) { printf("[-] Failed to send RTM_NEWLINK: %s\n", strerror(errno)); close(fd); return; } char resp[1024]; int len = recv(fd, resp, sizeof(resp), 0); if (len < 0) { printf("[-] Failed to recv RTM_NEWLINK ack: %s\n", strerror(errno)); } else { struct nlmsghdr *nlh = (struct nlmsghdr *)resp; if (nlh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(nlh); if (err->error != 0 && err->error != -EEXIST && err->error != -EOPNOTSUPP) { printf("[-] RTM_NEWLINK failed with error %d\n", err->error); } } } close(fd); printf("[+] Created dummy interface %s\n", name); } static void setup_interface(const char *name, const char *ip, const char *netmask) { int sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) { printf("[-] Failed to socket: %s\n", strerror(errno)); return; } struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, name, IFNAMSIZ - 1); struct sockaddr_in *addr = (struct sockaddr_in *)&ifr.ifr_addr; addr->sin_family = AF_INET; addr->sin_addr.s_addr = inet_addr(ip); if (ioctl(sock, SIOCSIFADDR, &ifr) < 0) { printf("[-] Failed to ioctl(SIOCSIFADDR): %s\n", strerror(errno)); } struct sockaddr_in *nm = (struct sockaddr_in *)&ifr.ifr_netmask; nm->sin_family = AF_INET; nm->sin_addr.s_addr = inet_addr(netmask); if (ioctl(sock, SIOCSIFNETMASK, &ifr) < 0) { printf("[-] Failed to ioctl(SIOCSIFNETMASK): %s\n", strerror(errno)); } if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) { printf("[-] Failed to ioctl(SIOCGIFFLAGS): %s\n", strerror(errno)); } ifr.ifr_flags |= IFF_UP | IFF_RUNNING; if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0) { printf("[-] Failed to ioctl(SIOCSIFFLAGS): %s\n", strerror(errno)); } close(sock); printf("[+] Configured interface %s with IP %s\n", name, ip); } #define RDMA_NL_NLDEV 5 #define RDMA_NLDEV_CMD_NEWLINK 3 #define RDMA_NLDEV_ATTR_DEV_NAME 2 #define RDMA_NLDEV_ATTR_NDEV_NAME 51 #define RDMA_NLDEV_ATTR_LINK_TYPE 65 static void attach_rdma_rxe(const char *ibdev_name, const char *ndev_name) { int fd = socket(AF_NETLINK, SOCK_RAW, 20); // NETLINK_RDMA if (fd < 0) { printf("[-] Failed to socket(NETLINK_RDMA): %s\n", strerror(errno)); return; } char buf[1024] = {0}; struct nlmsghdr *n = (struct nlmsghdr *)buf; n->nlmsg_len = NLMSG_LENGTH(0); n->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; n->nlmsg_type = (RDMA_NL_NLDEV << 10) | RDMA_NLDEV_CMD_NEWLINK; add_rta(n, sizeof(buf), RDMA_NLDEV_ATTR_DEV_NAME, ibdev_name, strlen(ibdev_name) + 1); add_rta(n, sizeof(buf), RDMA_NLDEV_ATTR_LINK_TYPE, "rxe", strlen("rxe") + 1); add_rta(n, sizeof(buf), RDMA_NLDEV_ATTR_NDEV_NAME, ndev_name, strlen(ndev_name) + 1); if (send(fd, n, n->nlmsg_len, 0) < 0) { printf("[-] Failed to send RDMA_NLDEV_CMD_NEWLINK: %s\n", strerror(errno)); close(fd); return; } char resp[1024]; int len = recv(fd, resp, sizeof(resp), 0); if (len < 0) { printf("[-] Failed to recv RDMA_NLDEV_CMD_NEWLINK ack: %s\n", strerror(errno)); } else { struct nlmsghdr *nlh = (struct nlmsghdr *)resp; if (nlh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(nlh); if (err->error != 0 && err->error != -EEXIST) { printf("[-] RDMA_NLDEV_CMD_NEWLINK failed with error %d\n", err->error); } } } close(fd); printf("[+] Attached rdma_rxe %s to %s\n", ibdev_name, ndev_name); } void enable_tracepoint(void) { const char *paths[] = { "/sys/kernel/tracing/events/rtrs_clt/rtrs_rdma_error_recovery/enable", "/sys/kernel/debug/tracing/events/rtrs_clt/rtrs_rdma_error_recovery/enable", "/sys/kernel/tracing/events/rtrs_clt/enable", "/sys/kernel/debug/tracing/events/rtrs_clt/enable", "/sys/kernel/tracing/events/enable", "/sys/kernel/debug/tracing/events/enable" }; int i, fd, res; for (i = 0; i < sizeof(paths)/sizeof(paths[0]); i++) { fd = open(paths[i], O_WRONLY); if (fd >= 0) { res = write(fd, "1\n", 2); if (res < 0) { printf("[-] Failed to write to %s: %s\n", paths[i], strerror(errno)); close(fd); continue; } printf("[+] Enabled tracepoint at %s\n", paths[i]); close(fd); return; } } printf("[-] Failed to enable tracepoint. The crash might not trigger if tracing is disabled.\n"); } int main(void) { int fd; const char *cmd = "sessname=test path=ip:192.168.111.2 device_path=test"; printf("[+] Starting reproducer...\n"); /* 1. Create a dummy interface */ create_dummy_interface("dummy0"); /* 2. Assign IP address and bring it up */ setup_interface("dummy0", "192.168.111.1", "255.255.255.0"); /* 3. Attach Soft RoCE (rdma_rxe) to the dummy interface */ attach_rdma_rxe("rxe0", "dummy0"); /* 4. Enable the tracepoint required to trigger the bug */ enable_tracepoint(); /* 5. Trigger the bug by writing to the RNBD map_device sysfs file */ fd = open("/sys/class/rnbd-client/ctl/map_device", O_WRONLY); if (fd < 0) { printf("[-] Failed to open map_device: %s\n", strerror(errno)); exit(1); } printf("[+] open map_device successful.\n"); /* * Writing to this file triggers rtrs_clt_open() -> init_path(). * The synchronous RDMA resolution will succeed because of the rxe device, * but the asynchronous ARP resolution will fail because 192.168.111.2 does not exist. * This triggers the tracepoint before the kobject is initialized, causing the NULL pointer dereference. */ if (write(fd, cmd, strlen(cmd)) < 0) { printf("[-] Failed to write: %s\n", strerror(errno)); } else { printf("[+] write successful.\n"); } if (close(fd) < 0) { printf("[-] Failed to close: %s\n", strerror(errno)); } else { printf("[+] close successful.\n"); } /* Give the asynchronous CM event handler time to run and trigger the bug */ printf("[+] Waiting for asynchronous CM event to trigger the bug...\n"); sleep(8); return 0; }