Permalink
Please
sign in to comment.
| @@ -0,0 +1,2 @@ | ||
| *.norepro | ||
| *.syz |
| @@ -0,0 +1,357 @@ | ||
| // INFO: task hung in nbd_ioctl | ||
| // https://syzkaller.appspot.com/bug?id=0003deaa9f80de950cb6e0d2cb04c59b4f33a928 | ||
| // status:fixed | ||
| // autogenerated by syzkaller (https://github.com/google/syzkaller) | ||
|
|
||
| #define _GNU_SOURCE | ||
|
|
||
| #include <arpa/inet.h> | ||
| #include <dirent.h> | ||
| #include <endian.h> | ||
| #include <errno.h> | ||
| #include <fcntl.h> | ||
| #include <net/if.h> | ||
| #include <netinet/in.h> | ||
| #include <setjmp.h> | ||
| #include <signal.h> | ||
| #include <stdarg.h> | ||
| #include <stdbool.h> | ||
| #include <stdint.h> | ||
| #include <stdio.h> | ||
| #include <stdlib.h> | ||
| #include <string.h> | ||
| #include <sys/prctl.h> | ||
| #include <sys/socket.h> | ||
| #include <sys/stat.h> | ||
| #include <sys/syscall.h> | ||
| #include <sys/types.h> | ||
| #include <sys/wait.h> | ||
| #include <time.h> | ||
| #include <unistd.h> | ||
|
|
||
| #include <linux/genetlink.h> | ||
| #include <linux/if_addr.h> | ||
| #include <linux/if_link.h> | ||
| #include <linux/in6.h> | ||
| #include <linux/neighbour.h> | ||
| #include <linux/net.h> | ||
| #include <linux/netlink.h> | ||
| #include <linux/rtnetlink.h> | ||
| #include <linux/veth.h> | ||
|
|
||
| static __thread int skip_segv; | ||
| static __thread jmp_buf segv_env; | ||
|
|
||
| static void segv_handler(int sig, siginfo_t* info, void* ctx) | ||
| { | ||
| uintptr_t addr = (uintptr_t)info->si_addr; | ||
| const uintptr_t prog_start = 1 << 20; | ||
| const uintptr_t prog_end = 100 << 20; | ||
| if (__atomic_load_n(&skip_segv, __ATOMIC_RELAXED) && | ||
| (addr < prog_start || addr > prog_end)) { | ||
| _longjmp(segv_env, 1); | ||
| } | ||
| exit(sig); | ||
| } | ||
|
|
||
| static void install_segv_handler(void) | ||
| { | ||
| struct sigaction sa; | ||
| memset(&sa, 0, sizeof(sa)); | ||
| sa.sa_handler = SIG_IGN; | ||
| syscall(SYS_rt_sigaction, 0x20, &sa, NULL, 8); | ||
| syscall(SYS_rt_sigaction, 0x21, &sa, NULL, 8); | ||
| memset(&sa, 0, sizeof(sa)); | ||
| sa.sa_sigaction = segv_handler; | ||
| sa.sa_flags = SA_NODEFER | SA_SIGINFO; | ||
| sigaction(SIGSEGV, &sa, NULL); | ||
| sigaction(SIGBUS, &sa, NULL); | ||
| } | ||
|
|
||
| #define NONFAILING(...) \ | ||
| { \ | ||
| __atomic_fetch_add(&skip_segv, 1, __ATOMIC_SEQ_CST); \ | ||
| if (_setjmp(segv_env) == 0) { \ | ||
| __VA_ARGS__; \ | ||
| } \ | ||
| __atomic_fetch_sub(&skip_segv, 1, __ATOMIC_SEQ_CST); \ | ||
| } | ||
|
|
||
| static void sleep_ms(uint64_t ms) | ||
| { | ||
| usleep(ms * 1000); | ||
| } | ||
|
|
||
| static uint64_t current_time_ms(void) | ||
| { | ||
| struct timespec ts; | ||
| if (clock_gettime(CLOCK_MONOTONIC, &ts)) | ||
| exit(1); | ||
| return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000; | ||
| } | ||
|
|
||
| static bool write_file(const char* file, const char* what, ...) | ||
| { | ||
| char buf[1024]; | ||
| va_list args; | ||
| va_start(args, what); | ||
| vsnprintf(buf, sizeof(buf), what, args); | ||
| va_end(args); | ||
| buf[sizeof(buf) - 1] = 0; | ||
| int len = strlen(buf); | ||
| int fd = open(file, O_WRONLY | O_CLOEXEC); | ||
| if (fd == -1) | ||
| return false; | ||
| if (write(fd, buf, len) != len) { | ||
| int err = errno; | ||
| close(fd); | ||
| errno = err; | ||
| return false; | ||
| } | ||
| close(fd); | ||
| return true; | ||
| } | ||
|
|
||
| static struct { | ||
| char* pos; | ||
| int nesting; | ||
| struct nlattr* nested[8]; | ||
| char buf[1024]; | ||
| } nlmsg; | ||
|
|
||
| static void netlink_init(int typ, int flags, const void* data, int size) | ||
| { | ||
| memset(&nlmsg, 0, sizeof(nlmsg)); | ||
| struct nlmsghdr* hdr = (struct nlmsghdr*)nlmsg.buf; | ||
| hdr->nlmsg_type = typ; | ||
| hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags; | ||
| memcpy(hdr + 1, data, size); | ||
| nlmsg.pos = (char*)(hdr + 1) + NLMSG_ALIGN(size); | ||
| } | ||
|
|
||
| static void netlink_attr(int typ, const void* data, int size) | ||
| { | ||
| struct nlattr* attr = (struct nlattr*)nlmsg.pos; | ||
| attr->nla_len = sizeof(*attr) + size; | ||
| attr->nla_type = typ; | ||
| memcpy(attr + 1, data, size); | ||
| nlmsg.pos += NLMSG_ALIGN(attr->nla_len); | ||
| } | ||
|
|
||
| static int netlink_send_ext(int sock, uint16_t reply_type, int* reply_len) | ||
| { | ||
| if (nlmsg.pos > nlmsg.buf + sizeof(nlmsg.buf) || nlmsg.nesting) | ||
| exit(1); | ||
| struct nlmsghdr* hdr = (struct nlmsghdr*)nlmsg.buf; | ||
| hdr->nlmsg_len = nlmsg.pos - nlmsg.buf; | ||
| struct sockaddr_nl addr; | ||
| memset(&addr, 0, sizeof(addr)); | ||
| addr.nl_family = AF_NETLINK; | ||
| unsigned n = sendto(sock, nlmsg.buf, hdr->nlmsg_len, 0, | ||
| (struct sockaddr*)&addr, sizeof(addr)); | ||
| if (n != hdr->nlmsg_len) | ||
| exit(1); | ||
| n = recv(sock, nlmsg.buf, sizeof(nlmsg.buf), 0); | ||
| if (n < sizeof(struct nlmsghdr)) | ||
| exit(1); | ||
| if (reply_len && hdr->nlmsg_type == reply_type) { | ||
| *reply_len = n; | ||
| return 0; | ||
| } | ||
| if (n < sizeof(struct nlmsghdr) + sizeof(struct nlmsgerr)) | ||
| exit(1); | ||
| if (hdr->nlmsg_type != NLMSG_ERROR) | ||
| exit(1); | ||
| return -((struct nlmsgerr*)(hdr + 1))->error; | ||
| } | ||
|
|
||
| static int netlink_send(int sock) | ||
| { | ||
| return netlink_send_ext(sock, 0, NULL); | ||
| } | ||
|
|
||
| const int kInitNetNsFd = 239; | ||
|
|
||
| #define DEVLINK_FAMILY_NAME "devlink" | ||
|
|
||
| #define DEVLINK_CMD_RELOAD 37 | ||
| #define DEVLINK_ATTR_BUS_NAME 1 | ||
| #define DEVLINK_ATTR_DEV_NAME 2 | ||
| #define DEVLINK_ATTR_NETNS_FD 137 | ||
|
|
||
| static void netlink_devlink_netns_move(const char* bus_name, | ||
| const char* dev_name, int netns_fd) | ||
| { | ||
| struct genlmsghdr genlhdr; | ||
| struct nlattr* attr; | ||
| int sock, err, n; | ||
| uint16_t id = 0; | ||
| sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); | ||
| if (sock == -1) | ||
| exit(1); | ||
| memset(&genlhdr, 0, sizeof(genlhdr)); | ||
| genlhdr.cmd = CTRL_CMD_GETFAMILY; | ||
| netlink_init(GENL_ID_CTRL, 0, &genlhdr, sizeof(genlhdr)); | ||
| netlink_attr(CTRL_ATTR_FAMILY_NAME, DEVLINK_FAMILY_NAME, | ||
| strlen(DEVLINK_FAMILY_NAME) + 1); | ||
| err = netlink_send_ext(sock, GENL_ID_CTRL, &n); | ||
| if (err) { | ||
| goto error; | ||
| } | ||
| attr = | ||
| (struct nlattr*)(nlmsg.buf + NLMSG_HDRLEN + NLMSG_ALIGN(sizeof(genlhdr))); | ||
| for (; (char*)attr < nlmsg.buf + n; | ||
| attr = (struct nlattr*)((char*)attr + NLMSG_ALIGN(attr->nla_len))) { | ||
| if (attr->nla_type == CTRL_ATTR_FAMILY_ID) { | ||
| id = *(uint16_t*)(attr + 1); | ||
| break; | ||
| } | ||
| } | ||
| if (!id) { | ||
| goto error; | ||
| } | ||
| recv(sock, nlmsg.buf, sizeof(nlmsg.buf), 0); /* recv ack */ | ||
| memset(&genlhdr, 0, sizeof(genlhdr)); | ||
| genlhdr.cmd = DEVLINK_CMD_RELOAD; | ||
| netlink_init(id, 0, &genlhdr, sizeof(genlhdr)); | ||
| netlink_attr(DEVLINK_ATTR_BUS_NAME, bus_name, strlen(bus_name) + 1); | ||
| netlink_attr(DEVLINK_ATTR_DEV_NAME, dev_name, strlen(dev_name) + 1); | ||
| netlink_attr(DEVLINK_ATTR_NETNS_FD, &netns_fd, sizeof(netns_fd)); | ||
| netlink_send(sock); | ||
| error: | ||
| close(sock); | ||
| } | ||
|
|
||
| static void initialize_devlink_pci(void) | ||
| { | ||
| int netns = open("/proc/self/ns/net", O_RDONLY); | ||
| if (netns == -1) | ||
| exit(1); | ||
| int ret = setns(kInitNetNsFd, 0); | ||
| if (ret == -1) | ||
| exit(1); | ||
| netlink_devlink_netns_move("pci", "0000:00:10.0", netns); | ||
| ret = setns(netns, 0); | ||
| if (ret == -1) | ||
| exit(1); | ||
| close(netns); | ||
| } | ||
|
|
||
| static long syz_open_dev(volatile long a0, volatile long a1, volatile long a2) | ||
| { | ||
| if (a0 == 0xc || a0 == 0xb) { | ||
| char buf[128]; | ||
| sprintf(buf, "/dev/%s/%d:%d", a0 == 0xc ? "char" : "block", (uint8_t)a1, | ||
| (uint8_t)a2); | ||
| return open(buf, O_RDWR, 0); | ||
| } else { | ||
| char buf[1024]; | ||
| char* hash; | ||
| NONFAILING(strncpy(buf, (char*)a0, sizeof(buf) - 1)); | ||
| buf[sizeof(buf) - 1] = 0; | ||
| while ((hash = strchr(buf, '#'))) { | ||
| *hash = '0' + (char)(a1 % 10); | ||
| a1 /= 10; | ||
| } | ||
| return open(buf, a2, 0); | ||
| } | ||
| } | ||
|
|
||
| static void kill_and_wait(int pid, int* status) | ||
| { | ||
| kill(-pid, SIGKILL); | ||
| kill(pid, SIGKILL); | ||
| int i; | ||
| for (i = 0; i < 100; i++) { | ||
| if (waitpid(-1, status, WNOHANG | __WALL) == pid) | ||
| return; | ||
| usleep(1000); | ||
| } | ||
| DIR* dir = opendir("/sys/fs/fuse/connections"); | ||
| if (dir) { | ||
| for (;;) { | ||
| struct dirent* ent = readdir(dir); | ||
| if (!ent) | ||
| break; | ||
| if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) | ||
| continue; | ||
| char abort[300]; | ||
| snprintf(abort, sizeof(abort), "/sys/fs/fuse/connections/%s/abort", | ||
| ent->d_name); | ||
| int fd = open(abort, O_WRONLY); | ||
| if (fd == -1) { | ||
| continue; | ||
| } | ||
| if (write(fd, abort, 1) < 0) { | ||
| } | ||
| close(fd); | ||
| } | ||
| closedir(dir); | ||
| } else { | ||
| } | ||
| while (waitpid(-1, status, __WALL) != pid) { | ||
| } | ||
| } | ||
|
|
||
| static void setup_test() | ||
| { | ||
| prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); | ||
| setpgrp(); | ||
| write_file("/proc/self/oom_score_adj", "1000"); | ||
| } | ||
|
|
||
| static void execute_one(void); | ||
|
|
||
| #define WAIT_FLAGS __WALL | ||
|
|
||
| static void loop(void) | ||
| { | ||
| int iter; | ||
| for (iter = 0;; iter++) { | ||
| int pid = fork(); | ||
| if (pid < 0) | ||
| exit(1); | ||
| if (pid == 0) { | ||
| setup_test(); | ||
| execute_one(); | ||
| exit(0); | ||
| } | ||
| int status = 0; | ||
| uint64_t start = current_time_ms(); | ||
| for (;;) { | ||
| if (waitpid(-1, &status, WNOHANG | WAIT_FLAGS) == pid) | ||
| break; | ||
| sleep_ms(1); | ||
| if (current_time_ms() - start < 5 * 1000) | ||
| continue; | ||
| kill_and_wait(pid, &status); | ||
| break; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| uint64_t r[3] = {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff}; | ||
|
|
||
| void execute_one(void) | ||
| { | ||
| intptr_t res = 0; | ||
| res = syscall(__NR_socket, 0x10, 2, 0); | ||
| if (res != -1) | ||
| r[0] = res; | ||
| NONFAILING(memcpy((void*)0x200001c0, "/dev/nbd#\000", 10)); | ||
| res = syz_open_dev(0x200001c0, 0, 0); | ||
| if (res != -1) | ||
| r[1] = res; | ||
| res = syz_open_dev(0, 0, 0); | ||
| if (res != -1) | ||
| r[2] = res; | ||
| syscall(__NR_ioctl, r[1], 0xab00, r[0]); | ||
| syscall(__NR_ioctl, r[2], 0xab03, 0); | ||
| } | ||
| int main(void) | ||
| { | ||
| syscall(__NR_mmap, 0x20000000, 0x1000000, 3, 0x32, -1, 0); | ||
| install_segv_handler(); | ||
| loop(); | ||
| return 0; | ||
| } |
Oops, something went wrong.
0 comments on commit
6a06992