// https://syzkaller.appspot.com/bug?id=23ff03c13bd76032fd8922ba33c02ad2287ff724 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef NLA_HDRLEN #define NLA_ALIGNTO 4 #define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1)) #define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr))) #endif #define ALIGN_NL(len) (((len) + 3) & ~3) struct nl_msg { struct nlmsghdr nlh; struct nfgenmsg nfmsg; char buf[1024]; }; void add_attr(struct nl_msg *msg, int type, const void *data, int len) { struct nlattr *nla = (struct nlattr *)((char *)msg + ALIGN_NL(msg->nlh.nlmsg_len)); nla->nla_type = type; nla->nla_len = len + NLA_HDRLEN; memcpy((char *)nla + NLA_HDRLEN, data, len); msg->nlh.nlmsg_len = ALIGN_NL(msg->nlh.nlmsg_len) + ALIGN_NL(nla->nla_len); } struct nlattr *start_nested(struct nl_msg *msg, int type) { struct nlattr *nla = (struct nlattr *)((char *)msg + ALIGN_NL(msg->nlh.nlmsg_len)); nla->nla_type = type | NLA_F_NESTED; msg->nlh.nlmsg_len = ALIGN_NL(msg->nlh.nlmsg_len) + NLA_HDRLEN; return nla; } void end_nested(struct nl_msg *msg, struct nlattr *nla) { nla->nla_len = (char *)msg + ALIGN_NL(msg->nlh.nlmsg_len) - (char *)nla; } void add_tuple(struct nl_msg *msg, int type, uint32_t src_ip, uint32_t dst_ip, uint16_t src_port, uint16_t dst_port, uint8_t proto) { struct nlattr *nest_tuple = start_nested(msg, type); struct nlattr *nest_ip = start_nested(msg, CTA_TUPLE_IP); add_attr(msg, CTA_IP_V4_SRC, &src_ip, 4); add_attr(msg, CTA_IP_V4_DST, &dst_ip, 4); end_nested(msg, nest_ip); struct nlattr *nest_proto = start_nested(msg, CTA_TUPLE_PROTO); add_attr(msg, CTA_PROTO_NUM, &proto, 1); add_attr(msg, CTA_PROTO_SRC_PORT, &src_port, 2); add_attr(msg, CTA_PROTO_DST_PORT, &dst_port, 2); end_nested(msg, nest_proto); end_nested(msg, nest_tuple); } void send_ct_new() { int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_NETFILTER); if (fd < 0) { printf("[-] socket failed: %s\n", strerror(errno)); return; } struct nl_msg msg; memset(&msg, 0, sizeof(msg)); msg.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct nfgenmsg)); msg.nlh.nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_NEW; msg.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE; msg.nlh.nlmsg_seq = 1; msg.nfmsg.nfgen_family = AF_INET; msg.nfmsg.version = NFNETLINK_V0; msg.nfmsg.res_id = 0; uint32_t src_ip = inet_addr("1.2.3.4"); uint32_t dst_ip = inet_addr("5.6.7.8"); uint16_t src_port = htons(12345); uint16_t dst_port = htons(80); uint8_t proto = IPPROTO_TCP; add_tuple(&msg, CTA_TUPLE_ORIG, src_ip, dst_ip, src_port, dst_port, proto); add_tuple(&msg, CTA_TUPLE_REPLY, dst_ip, src_ip, dst_port, src_port, proto); uint32_t timeout = htonl(100); add_attr(&msg, CTA_TIMEOUT, &timeout, 4); struct nlattr *nest_help = start_nested(&msg, CTA_HELP); const char *help_name = "dummy"; add_attr(&msg, CTA_HELP_NAME, help_name, strlen(help_name) + 1); end_nested(&msg, nest_help); struct sockaddr_nl snl; memset(&snl, 0, sizeof(snl)); snl.nl_family = AF_NETLINK; if (sendto(fd, &msg, msg.nlh.nlmsg_len, 0, (struct sockaddr *)&snl, sizeof(snl)) < 0) { printf("[-] sendto failed: %s\n", strerror(errno)); } else { printf("[+] send_ct_new sendto successful.\n"); } char buf[4096]; recv(fd, buf, sizeof(buf), 0); close(fd); } void send_ct_get() { int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_NETFILTER); if (fd < 0) { printf("[-] socket failed: %s\n", strerror(errno)); return; } struct nl_msg msg; memset(&msg, 0, sizeof(msg)); msg.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct nfgenmsg)); msg.nlh.nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET; msg.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; msg.nlh.nlmsg_seq = 2; msg.nfmsg.nfgen_family = AF_INET; msg.nfmsg.version = NFNETLINK_V0; msg.nfmsg.res_id = 0; struct sockaddr_nl snl; memset(&snl, 0, sizeof(snl)); snl.nl_family = AF_NETLINK; if (sendto(fd, &msg, msg.nlh.nlmsg_len, 0, (struct sockaddr *)&snl, sizeof(snl)) < 0) { printf("[-] sendto failed: %s\n", strerror(errno)); } else { printf("[+] send_ct_get sendto successful.\n"); } char buf[4096]; recv(fd, buf, sizeof(buf), 0); close(fd); } int main(int argc, char **argv) { if (argc > 1) { // We are executed as modprobe by the kernel. // If the kernel is requesting our dummy helper, trigger the deadlock. if (strstr(argv[argc-1], "dummy") != NULL) { printf("[+] Modprobe called for dummy, sending ct_get\n"); send_ct_get(); } return 0; } printf("[+] Setting hung_task_timeout_secs to 5\n"); int fd_ht = open("/proc/sys/kernel/hung_task_timeout_secs", O_WRONLY); if (fd_ht >= 0) { if (write(fd_ht, "5\n", 2) < 0) { printf("[-] write to hung_task_timeout_secs failed: %s\n", strerror(errno)); } else { printf("[+] Successfully set hung_task_timeout_secs to 5\n"); } close(fd_ht); } else { printf("[-] open /proc/sys/kernel/hung_task_timeout_secs failed: %s\n", strerror(errno)); } printf("[+] Pre-loading nf_conntrack_netlink\n"); send_ct_get(); char path[256]; ssize_t len = readlink("/proc/self/exe", path, sizeof(path) - 1); if (len > 0) { path[len] = '\0'; int fd = open("/proc/sys/kernel/modprobe", O_WRONLY); if (fd >= 0) { if (write(fd, path, strlen(path)) < 0) { printf("[-] write to modprobe failed: %s\n", strerror(errno)); } else { printf("[+] Set modprobe to %s\n", path); } close(fd); } else { printf("[-] open /proc/sys/kernel/modprobe failed: %s\n", strerror(errno)); } } else { printf("[-] readlink failed: %s\n", strerror(errno)); } pid_t pid = fork(); if (pid < 0) { printf("[-] fork failed: %s\n", strerror(errno)); exit(1); } if (pid == 0) { printf("[+] Child sending ct_new to trigger bug\n"); send_ct_new(); printf("[+] Child done\n"); exit(0); } printf("[+] Parent waiting for 20 seconds to let hung task detector fire\n"); for (int i = 0; i < 200; i++) { int status; if (waitpid(pid, &status, WNOHANG) == pid) { printf("[+] Child exited early\n"); break; } usleep(100000); // 100ms } printf("[+] Parent done\n"); return 0; }