From 7c8830468f69e03460461c728e52e2c0ff7bb050 Mon Sep 17 00:00:00 2001 From: Jukka Kaartinen Date: Fri, 7 Feb 2020 15:45:23 +0200 Subject: Add reproducers from local builds Linux reproducers updates from https://github.com/dvyukov/syzkaller-repros Signed-off-by: Jukka Kaartinen Signed-off-by: Shuah Khan --- .../12258f4d6fea5a0c6cc7028134a73a3a2beb3b7b.c | 659 ++++++++++ .../43537acbdf9ca352a10059a98600f94214260f92.c | 499 ++++++++ .../45226c10813804338740a3aaf3d855a701325165.c | 902 ++++++++++++++ .../bb5b0d3aebeb504324b3758b03b8820b10416f87.c | 1268 ++++++++++++++++++++ .../ccef92ea0c38c3e4def623d61d57f625c3d6d9a4.c | 488 ++++++++ .../e9813c22cf50f109dfe6ce3c4901795c6158a791.c | 536 +++++++++ 6 files changed, 4352 insertions(+) create mode 100644 syzkaller-repros/linux/12258f4d6fea5a0c6cc7028134a73a3a2beb3b7b.c create mode 100644 syzkaller-repros/linux/43537acbdf9ca352a10059a98600f94214260f92.c create mode 100644 syzkaller-repros/linux/45226c10813804338740a3aaf3d855a701325165.c create mode 100644 syzkaller-repros/linux/bb5b0d3aebeb504324b3758b03b8820b10416f87.c create mode 100644 syzkaller-repros/linux/ccef92ea0c38c3e4def623d61d57f625c3d6d9a4.c create mode 100644 syzkaller-repros/linux/e9813c22cf50f109dfe6ce3c4901795c6158a791.c diff --git a/syzkaller-repros/linux/12258f4d6fea5a0c6cc7028134a73a3a2beb3b7b.c b/syzkaller-repros/linux/12258f4d6fea5a0c6cc7028134a73a3a2beb3b7b.c new file mode 100644 index 0000000..9ec9014 --- /dev/null +++ b/syzkaller-repros/linux/12258f4d6fea5a0c6cc7028134a73a3a2beb3b7b.c @@ -0,0 +1,659 @@ +// autogenerated by syzkaller (https://github.com/google/syzkaller) + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} + +struct nlmsg { + char* pos; + int nesting; + struct nlattr* nested[8]; + char buf[1024]; +}; + +static struct nlmsg nlmsg; + +static void netlink_init(struct nlmsg* nlmsg, 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(struct nlmsg* nlmsg, 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(struct nlmsg* nlmsg, 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 (hdr->nlmsg_type == NLMSG_DONE) { + *reply_len = 0; + return 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(struct nlmsg* nlmsg, int sock) +{ + return netlink_send_ext(nlmsg, sock, 0, NULL); +} + +static int netlink_next_msg(struct nlmsg* nlmsg, unsigned int offset, + unsigned int total_len) +{ + struct nlmsghdr* hdr = (struct nlmsghdr*)(nlmsg->buf + offset); + if (offset == total_len || offset + hdr->nlmsg_len > total_len) + return -1; + return hdr->nlmsg_len; +} + +static void netlink_device_change(struct nlmsg* nlmsg, int sock, + const char* name, bool up, const char* master, + const void* mac, int macsize, + const char* new_name) +{ + struct ifinfomsg hdr; + memset(&hdr, 0, sizeof(hdr)); + if (up) + hdr.ifi_flags = hdr.ifi_change = IFF_UP; + hdr.ifi_index = if_nametoindex(name); + netlink_init(nlmsg, RTM_NEWLINK, 0, &hdr, sizeof(hdr)); + if (new_name) + netlink_attr(nlmsg, IFLA_IFNAME, new_name, strlen(new_name)); + if (master) { + int ifindex = if_nametoindex(master); + netlink_attr(nlmsg, IFLA_MASTER, &ifindex, sizeof(ifindex)); + } + if (macsize) + netlink_attr(nlmsg, IFLA_ADDRESS, mac, macsize); + int err = netlink_send(nlmsg, sock); + (void)err; +} + +static int netlink_add_addr(struct nlmsg* nlmsg, int sock, const char* dev, + const void* addr, int addrsize) +{ + struct ifaddrmsg hdr; + memset(&hdr, 0, sizeof(hdr)); + hdr.ifa_family = addrsize == 4 ? AF_INET : AF_INET6; + hdr.ifa_prefixlen = addrsize == 4 ? 24 : 120; + hdr.ifa_scope = RT_SCOPE_UNIVERSE; + hdr.ifa_index = if_nametoindex(dev); + netlink_init(nlmsg, RTM_NEWADDR, NLM_F_CREATE | NLM_F_REPLACE, &hdr, + sizeof(hdr)); + netlink_attr(nlmsg, IFA_LOCAL, addr, addrsize); + netlink_attr(nlmsg, IFA_ADDRESS, addr, addrsize); + return netlink_send(nlmsg, sock); +} + +static void netlink_add_addr4(struct nlmsg* nlmsg, int sock, const char* dev, + const char* addr) +{ + struct in_addr in_addr; + inet_pton(AF_INET, addr, &in_addr); + int err = netlink_add_addr(nlmsg, sock, dev, &in_addr, sizeof(in_addr)); + (void)err; +} + +static void netlink_add_addr6(struct nlmsg* nlmsg, int sock, const char* dev, + const char* addr) +{ + struct in6_addr in6_addr; + inet_pton(AF_INET6, addr, &in6_addr); + int err = netlink_add_addr(nlmsg, sock, dev, &in6_addr, sizeof(in6_addr)); + (void)err; +} + +static void netlink_add_neigh(struct nlmsg* nlmsg, int sock, const char* name, + const void* addr, int addrsize, const void* mac, + int macsize) +{ + struct ndmsg hdr; + memset(&hdr, 0, sizeof(hdr)); + hdr.ndm_family = addrsize == 4 ? AF_INET : AF_INET6; + hdr.ndm_ifindex = if_nametoindex(name); + hdr.ndm_state = NUD_PERMANENT; + netlink_init(nlmsg, RTM_NEWNEIGH, NLM_F_EXCL | NLM_F_CREATE, &hdr, + sizeof(hdr)); + netlink_attr(nlmsg, NDA_DST, addr, addrsize); + netlink_attr(nlmsg, NDA_LLADDR, mac, macsize); + int err = netlink_send(nlmsg, sock); + (void)err; +} + +static int tunfd = -1; +static int tun_frags_enabled; + +#define TUN_IFACE "syz_tun" + +#define LOCAL_MAC 0xaaaaaaaaaaaa +#define REMOTE_MAC 0xaaaaaaaaaabb + +#define LOCAL_IPV4 "172.20.20.170" +#define REMOTE_IPV4 "172.20.20.187" + +#define LOCAL_IPV6 "fe80::aa" +#define REMOTE_IPV6 "fe80::bb" + +#define IFF_NAPI 0x0010 +#define IFF_NAPI_FRAGS 0x0020 + +static void initialize_tun(void) +{ + tunfd = open("/dev/net/tun", O_RDWR | O_NONBLOCK); + if (tunfd == -1) { + printf("tun: can't open /dev/net/tun: please enable CONFIG_TUN=y\n"); + printf("otherwise fuzzing or reproducing might not work as intended\n"); + return; + } + const int kTunFd = 240; + if (dup2(tunfd, kTunFd) < 0) + exit(1); + close(tunfd); + tunfd = kTunFd; + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, TUN_IFACE, IFNAMSIZ); + ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_NAPI | IFF_NAPI_FRAGS; + if (ioctl(tunfd, TUNSETIFF, (void*)&ifr) < 0) { + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + if (ioctl(tunfd, TUNSETIFF, (void*)&ifr) < 0) + exit(1); + } + if (ioctl(tunfd, TUNGETIFF, (void*)&ifr) < 0) + exit(1); + tun_frags_enabled = (ifr.ifr_flags & IFF_NAPI_FRAGS) != 0; + char sysctl[64]; + sprintf(sysctl, "/proc/sys/net/ipv6/conf/%s/accept_dad", TUN_IFACE); + write_file(sysctl, "0"); + sprintf(sysctl, "/proc/sys/net/ipv6/conf/%s/router_solicitations", TUN_IFACE); + write_file(sysctl, "0"); + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock == -1) + exit(1); + netlink_add_addr4(&nlmsg, sock, TUN_IFACE, LOCAL_IPV4); + netlink_add_addr6(&nlmsg, sock, TUN_IFACE, LOCAL_IPV6); + uint64_t macaddr = REMOTE_MAC; + struct in_addr in_addr; + inet_pton(AF_INET, REMOTE_IPV4, &in_addr); + netlink_add_neigh(&nlmsg, sock, TUN_IFACE, &in_addr, sizeof(in_addr), + &macaddr, ETH_ALEN); + struct in6_addr in6_addr; + inet_pton(AF_INET6, REMOTE_IPV6, &in6_addr); + netlink_add_neigh(&nlmsg, sock, TUN_IFACE, &in6_addr, sizeof(in6_addr), + &macaddr, ETH_ALEN); + macaddr = LOCAL_MAC; + netlink_device_change(&nlmsg, sock, TUN_IFACE, true, 0, &macaddr, ETH_ALEN, + NULL); + close(sock); +} + +const int kInitNetNsFd = 239; + +#define DEVLINK_FAMILY_NAME "devlink" + +#define DEVLINK_CMD_PORT_GET 5 +#define DEVLINK_CMD_RELOAD 37 +#define DEVLINK_ATTR_BUS_NAME 1 +#define DEVLINK_ATTR_DEV_NAME 2 +#define DEVLINK_ATTR_NETDEV_NAME 7 +#define DEVLINK_ATTR_NETNS_FD 138 + +static int netlink_devlink_id_get(struct nlmsg* nlmsg, int sock) +{ + struct genlmsghdr genlhdr; + struct nlattr* attr; + int err, n; + uint16_t id = 0; + memset(&genlhdr, 0, sizeof(genlhdr)); + genlhdr.cmd = CTRL_CMD_GETFAMILY; + netlink_init(nlmsg, GENL_ID_CTRL, 0, &genlhdr, sizeof(genlhdr)); + netlink_attr(nlmsg, CTRL_ATTR_FAMILY_NAME, DEVLINK_FAMILY_NAME, + strlen(DEVLINK_FAMILY_NAME) + 1); + err = netlink_send_ext(nlmsg, sock, GENL_ID_CTRL, &n); + if (err) { + return -1; + } + 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) { + return -1; + } + recv(sock, nlmsg->buf, sizeof(nlmsg->buf), 0); /* recv ack */ + return id; +} + +static void netlink_devlink_netns_move(const char* bus_name, + const char* dev_name, int netns_fd) +{ + struct genlmsghdr genlhdr; + int sock; + int id, err; + sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (sock == -1) + exit(1); + id = netlink_devlink_id_get(&nlmsg, sock); + if (id == -1) + goto error; + memset(&genlhdr, 0, sizeof(genlhdr)); + genlhdr.cmd = DEVLINK_CMD_RELOAD; + netlink_init(&nlmsg, id, 0, &genlhdr, sizeof(genlhdr)); + netlink_attr(&nlmsg, DEVLINK_ATTR_BUS_NAME, bus_name, strlen(bus_name) + 1); + netlink_attr(&nlmsg, DEVLINK_ATTR_DEV_NAME, dev_name, strlen(dev_name) + 1); + netlink_attr(&nlmsg, DEVLINK_ATTR_NETNS_FD, &netns_fd, sizeof(netns_fd)); + err = netlink_send(&nlmsg, sock); + if (err) { + } +error: + close(sock); +} + +static struct nlmsg nlmsg2; + +static void initialize_devlink_ports(const char* bus_name, const char* dev_name, + const char* netdev_prefix) +{ + struct genlmsghdr genlhdr; + int len, total_len, id, err, offset; + uint16_t netdev_index; + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (sock == -1) + exit(1); + int rtsock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (rtsock == -1) + exit(1); + id = netlink_devlink_id_get(&nlmsg, sock); + if (id == -1) + goto error; + memset(&genlhdr, 0, sizeof(genlhdr)); + genlhdr.cmd = DEVLINK_CMD_PORT_GET; + netlink_init(&nlmsg, id, NLM_F_DUMP, &genlhdr, sizeof(genlhdr)); + netlink_attr(&nlmsg, DEVLINK_ATTR_BUS_NAME, bus_name, strlen(bus_name) + 1); + netlink_attr(&nlmsg, DEVLINK_ATTR_DEV_NAME, dev_name, strlen(dev_name) + 1); + err = netlink_send_ext(&nlmsg, sock, id, &total_len); + if (err) { + goto error; + } + offset = 0; + netdev_index = 0; + while ((len = netlink_next_msg(&nlmsg, offset, total_len)) != -1) { + struct nlattr* attr = (struct nlattr*)(nlmsg.buf + offset + NLMSG_HDRLEN + + NLMSG_ALIGN(sizeof(genlhdr))); + for (; (char*)attr < nlmsg.buf + offset + len; + attr = (struct nlattr*)((char*)attr + NLMSG_ALIGN(attr->nla_len))) { + if (attr->nla_type == DEVLINK_ATTR_NETDEV_NAME) { + char* port_name; + char netdev_name[IFNAMSIZ]; + port_name = (char*)(attr + 1); + snprintf(netdev_name, sizeof(netdev_name), "%s%d", netdev_prefix, + netdev_index); + netlink_device_change(&nlmsg2, rtsock, port_name, true, 0, 0, 0, + netdev_name); + break; + } + } + offset += len; + netdev_index++; + } +error: + close(rtsock); + 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); + initialize_devlink_ports("pci", "0000:00:10.0", "netpci"); +} + +static void setup_common() +{ + if (mount(0, "/sys/fs/fuse/connections", "fusectl", 0, 0)) { + } +} + +static void loop(); + +static void sandbox_common() +{ + prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); + setpgrp(); + setsid(); + int netns = open("/proc/self/ns/net", O_RDONLY); + if (netns == -1) + exit(1); + if (dup2(netns, kInitNetNsFd) < 0) + exit(1); + close(netns); + struct rlimit rlim; + rlim.rlim_cur = rlim.rlim_max = (200 << 20); + setrlimit(RLIMIT_AS, &rlim); + rlim.rlim_cur = rlim.rlim_max = 32 << 20; + setrlimit(RLIMIT_MEMLOCK, &rlim); + rlim.rlim_cur = rlim.rlim_max = 136 << 20; + setrlimit(RLIMIT_FSIZE, &rlim); + rlim.rlim_cur = rlim.rlim_max = 1 << 20; + setrlimit(RLIMIT_STACK, &rlim); + rlim.rlim_cur = rlim.rlim_max = 0; + setrlimit(RLIMIT_CORE, &rlim); + rlim.rlim_cur = rlim.rlim_max = 256; + setrlimit(RLIMIT_NOFILE, &rlim); + if (unshare(CLONE_NEWNS)) { + } + if (unshare(CLONE_NEWIPC)) { + } + if (unshare(0x02000000)) { + } + if (unshare(CLONE_NEWUTS)) { + } + if (unshare(CLONE_SYSVSEM)) { + } + typedef struct { + const char* name; + const char* value; + } sysctl_t; + static const sysctl_t sysctls[] = { + {"/proc/sys/kernel/shmmax", "16777216"}, + {"/proc/sys/kernel/shmall", "536870912"}, + {"/proc/sys/kernel/shmmni", "1024"}, + {"/proc/sys/kernel/msgmax", "8192"}, + {"/proc/sys/kernel/msgmni", "1024"}, + {"/proc/sys/kernel/msgmnb", "1024"}, + {"/proc/sys/kernel/sem", "1024 1048576 500 1024"}, + }; + unsigned i; + for (i = 0; i < sizeof(sysctls) / sizeof(sysctls[0]); i++) + write_file(sysctls[i].name, sysctls[i].value); +} + +int wait_for_loop(int pid) +{ + if (pid < 0) + exit(1); + int status = 0; + while (waitpid(-1, &status, __WALL) != pid) { + } + return WEXITSTATUS(status); +} + +static void drop_caps(void) +{ + struct __user_cap_header_struct cap_hdr = {}; + struct __user_cap_data_struct cap_data[2] = {}; + cap_hdr.version = _LINUX_CAPABILITY_VERSION_3; + cap_hdr.pid = getpid(); + if (syscall(SYS_capget, &cap_hdr, &cap_data)) + exit(1); + const int drop = (1 << CAP_SYS_PTRACE) | (1 << CAP_SYS_NICE); + cap_data[0].effective &= ~drop; + cap_data[0].permitted &= ~drop; + cap_data[0].inheritable &= ~drop; + if (syscall(SYS_capset, &cap_hdr, &cap_data)) + exit(1); +} + +static int do_sandbox_none(void) +{ + if (unshare(CLONE_NEWPID)) { + } + int pid = fork(); + if (pid != 0) + return wait_for_loop(pid); + setup_common(); + sandbox_common(); + drop_caps(); + if (unshare(CLONE_NEWNET)) { + } + initialize_devlink_pci(); + initialize_tun(); + loop(); + exit(1); +} + +static int inject_fault(int nth) +{ + int fd; + fd = open("/proc/thread-self/fail-nth", O_RDWR); + if (fd == -1) + exit(1); + char buf[16]; + sprintf(buf, "%d", nth + 1); + if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) + exit(1); + return fd; +} + +static void setup_fault() +{ + static struct { + const char* file; + const char* val; + bool fatal; + } files[] = { + {"/sys/kernel/debug/failslab/ignore-gfp-wait", "N", true}, + {"/sys/kernel/debug/fail_futex/ignore-private", "N", false}, + {"/sys/kernel/debug/fail_page_alloc/ignore-gfp-highmem", "N", false}, + {"/sys/kernel/debug/fail_page_alloc/ignore-gfp-wait", "N", false}, + {"/sys/kernel/debug/fail_page_alloc/min-order", "0", false}, + }; + unsigned i; + for (i = 0; i < sizeof(files) / sizeof(files[0]); i++) { + if (!write_file(files[i].file, files[i].val)) { + if (files[i].fatal) + exit(1); + } + } +} + +#define KMEMLEAK_FILE "/sys/kernel/debug/kmemleak" + +static void setup_leak() +{ + if (!write_file(KMEMLEAK_FILE, "scan")) + exit(1); + sleep(5); + if (!write_file(KMEMLEAK_FILE, "scan")) + exit(1); + if (!write_file(KMEMLEAK_FILE, "clear")) + exit(1); +} + +static void check_leaks(void) +{ + int fd = open(KMEMLEAK_FILE, O_RDWR); + if (fd == -1) + exit(1); + uint64_t start = current_time_ms(); + if (write(fd, "scan", 4) != 4) + exit(1); + sleep(1); + while (current_time_ms() - start < 4 * 1000) + sleep(1); + if (write(fd, "scan", 4) != 4) + exit(1); + static char buf[128 << 10]; + ssize_t n = read(fd, buf, sizeof(buf) - 1); + if (n < 0) + exit(1); + int nleaks = 0; + if (n != 0) { + sleep(1); + if (write(fd, "scan", 4) != 4) + exit(1); + if (lseek(fd, 0, SEEK_SET) < 0) + exit(1); + n = read(fd, buf, sizeof(buf) - 1); + if (n < 0) + exit(1); + buf[n] = 0; + char* pos = buf; + char* end = buf + n; + while (pos < end) { + char* next = strstr(pos + 1, "unreferenced object"); + if (!next) + next = end; + char prev = *next; + *next = 0; + fprintf(stderr, "BUG: memory leak\n%s\n", pos); + *next = prev; + pos = next; + nleaks++; + } + } + if (write(fd, "clear", 5) != 5) + exit(1); + close(fd); + if (nleaks) + exit(1); +} + +uint64_t r[2] = {0xffffffffffffffff, 0xffffffffffffffff}; + +void loop(void) +{ + intptr_t res = 0; + memcpy((void*)0x20000840, "/dev/net/tun\000", 13); + res = syscall(__NR_openat, 0xffffffffffffff9cul, 0x20000840ul, 0ul, 0ul); + if (res != -1) + r[0] = res; + res = syscall(__NR_socket, 0x10ul, 3ul, 0ul); + if (res != -1) + r[1] = res; + syscall(__NR_sendmsg, r[1], 0ul, 0ul); + syscall(__NR_getsockname, r[1], 0ul, 0ul); + syscall(__NR_ioctl, r[0], 0x400454daul, 0ul); + memcpy((void*)0x20000880, + "nr0\000\000\000\000\000\000\000\000\000\000\000\000\000", 16); + *(uint16_t*)0x20000890 = 0x1001; + inject_fault(81); + syscall(__NR_ioctl, r[0], 0x400454caul, 0x20000880ul); +} +int main(void) +{ + syscall(__NR_mmap, 0x20000000ul, 0x1000000ul, 3ul, 0x32ul, -1, 0); + setup_leak(); + setup_fault(); + do_sandbox_none(); + check_leaks(); + return 0; +} diff --git a/syzkaller-repros/linux/43537acbdf9ca352a10059a98600f94214260f92.c b/syzkaller-repros/linux/43537acbdf9ca352a10059a98600f94214260f92.c new file mode 100644 index 0000000..f04eed6 --- /dev/null +++ b/syzkaller-repros/linux/43537acbdf9ca352a10059a98600f94214260f92.c @@ -0,0 +1,499 @@ +// autogenerated by syzkaller (https://github.com/google/syzkaller) + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} + +struct nlmsg { + char* pos; + int nesting; + struct nlattr* nested[8]; + char buf[1024]; +}; + +static struct nlmsg nlmsg; + +static void netlink_init(struct nlmsg* nlmsg, 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(struct nlmsg* nlmsg, 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(struct nlmsg* nlmsg, 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 (hdr->nlmsg_type == NLMSG_DONE) { + *reply_len = 0; + return 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(struct nlmsg* nlmsg, int sock) +{ + return netlink_send_ext(nlmsg, sock, 0, NULL); +} + +static int netlink_next_msg(struct nlmsg* nlmsg, unsigned int offset, + unsigned int total_len) +{ + struct nlmsghdr* hdr = (struct nlmsghdr*)(nlmsg->buf + offset); + if (offset == total_len || offset + hdr->nlmsg_len > total_len) + return -1; + return hdr->nlmsg_len; +} + +static void netlink_device_change(struct nlmsg* nlmsg, int sock, + const char* name, bool up, const char* master, + const void* mac, int macsize, + const char* new_name) +{ + struct ifinfomsg hdr; + memset(&hdr, 0, sizeof(hdr)); + if (up) + hdr.ifi_flags = hdr.ifi_change = IFF_UP; + hdr.ifi_index = if_nametoindex(name); + netlink_init(nlmsg, RTM_NEWLINK, 0, &hdr, sizeof(hdr)); + if (new_name) + netlink_attr(nlmsg, IFLA_IFNAME, new_name, strlen(new_name)); + if (master) { + int ifindex = if_nametoindex(master); + netlink_attr(nlmsg, IFLA_MASTER, &ifindex, sizeof(ifindex)); + } + if (macsize) + netlink_attr(nlmsg, IFLA_ADDRESS, mac, macsize); + int err = netlink_send(nlmsg, sock); + (void)err; +} + +const int kInitNetNsFd = 239; + +#define DEVLINK_FAMILY_NAME "devlink" + +#define DEVLINK_CMD_PORT_GET 5 +#define DEVLINK_CMD_RELOAD 37 +#define DEVLINK_ATTR_BUS_NAME 1 +#define DEVLINK_ATTR_DEV_NAME 2 +#define DEVLINK_ATTR_NETDEV_NAME 7 +#define DEVLINK_ATTR_NETNS_FD 138 + +static int netlink_devlink_id_get(struct nlmsg* nlmsg, int sock) +{ + struct genlmsghdr genlhdr; + struct nlattr* attr; + int err, n; + uint16_t id = 0; + memset(&genlhdr, 0, sizeof(genlhdr)); + genlhdr.cmd = CTRL_CMD_GETFAMILY; + netlink_init(nlmsg, GENL_ID_CTRL, 0, &genlhdr, sizeof(genlhdr)); + netlink_attr(nlmsg, CTRL_ATTR_FAMILY_NAME, DEVLINK_FAMILY_NAME, + strlen(DEVLINK_FAMILY_NAME) + 1); + err = netlink_send_ext(nlmsg, sock, GENL_ID_CTRL, &n); + if (err) { + return -1; + } + 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) { + return -1; + } + recv(sock, nlmsg->buf, sizeof(nlmsg->buf), 0); /* recv ack */ + return id; +} + +static void netlink_devlink_netns_move(const char* bus_name, + const char* dev_name, int netns_fd) +{ + struct genlmsghdr genlhdr; + int sock; + int id, err; + sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (sock == -1) + exit(1); + id = netlink_devlink_id_get(&nlmsg, sock); + if (id == -1) + goto error; + memset(&genlhdr, 0, sizeof(genlhdr)); + genlhdr.cmd = DEVLINK_CMD_RELOAD; + netlink_init(&nlmsg, id, 0, &genlhdr, sizeof(genlhdr)); + netlink_attr(&nlmsg, DEVLINK_ATTR_BUS_NAME, bus_name, strlen(bus_name) + 1); + netlink_attr(&nlmsg, DEVLINK_ATTR_DEV_NAME, dev_name, strlen(dev_name) + 1); + netlink_attr(&nlmsg, DEVLINK_ATTR_NETNS_FD, &netns_fd, sizeof(netns_fd)); + err = netlink_send(&nlmsg, sock); + if (err) { + } +error: + close(sock); +} + +static struct nlmsg nlmsg2; + +static void initialize_devlink_ports(const char* bus_name, const char* dev_name, + const char* netdev_prefix) +{ + struct genlmsghdr genlhdr; + int len, total_len, id, err, offset; + uint16_t netdev_index; + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (sock == -1) + exit(1); + int rtsock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (rtsock == -1) + exit(1); + id = netlink_devlink_id_get(&nlmsg, sock); + if (id == -1) + goto error; + memset(&genlhdr, 0, sizeof(genlhdr)); + genlhdr.cmd = DEVLINK_CMD_PORT_GET; + netlink_init(&nlmsg, id, NLM_F_DUMP, &genlhdr, sizeof(genlhdr)); + netlink_attr(&nlmsg, DEVLINK_ATTR_BUS_NAME, bus_name, strlen(bus_name) + 1); + netlink_attr(&nlmsg, DEVLINK_ATTR_DEV_NAME, dev_name, strlen(dev_name) + 1); + err = netlink_send_ext(&nlmsg, sock, id, &total_len); + if (err) { + goto error; + } + offset = 0; + netdev_index = 0; + while ((len = netlink_next_msg(&nlmsg, offset, total_len)) != -1) { + struct nlattr* attr = (struct nlattr*)(nlmsg.buf + offset + NLMSG_HDRLEN + + NLMSG_ALIGN(sizeof(genlhdr))); + for (; (char*)attr < nlmsg.buf + offset + len; + attr = (struct nlattr*)((char*)attr + NLMSG_ALIGN(attr->nla_len))) { + if (attr->nla_type == DEVLINK_ATTR_NETDEV_NAME) { + char* port_name; + char netdev_name[IFNAMSIZ]; + port_name = (char*)(attr + 1); + snprintf(netdev_name, sizeof(netdev_name), "%s%d", netdev_prefix, + netdev_index); + netlink_device_change(&nlmsg2, rtsock, port_name, true, 0, 0, 0, + netdev_name); + break; + } + } + offset += len; + netdev_index++; + } +error: + close(rtsock); + 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); + initialize_devlink_ports("pci", "0000:00:10.0", "netpci"); +} + +static int inject_fault(int nth) +{ + int fd; + fd = open("/proc/thread-self/fail-nth", O_RDWR); + if (fd == -1) + exit(1); + char buf[16]; + sprintf(buf, "%d", nth + 1); + if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) + exit(1); + return fd; +} + +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 setup_fault() +{ + static struct { + const char* file; + const char* val; + bool fatal; + } files[] = { + {"/sys/kernel/debug/failslab/ignore-gfp-wait", "N", true}, + {"/sys/kernel/debug/fail_futex/ignore-private", "N", false}, + {"/sys/kernel/debug/fail_page_alloc/ignore-gfp-highmem", "N", false}, + {"/sys/kernel/debug/fail_page_alloc/ignore-gfp-wait", "N", false}, + {"/sys/kernel/debug/fail_page_alloc/min-order", "0", false}, + }; + unsigned i; + for (i = 0; i < sizeof(files) / sizeof(files[0]); i++) { + if (!write_file(files[i].file, files[i].val)) { + if (files[i].fatal) + exit(1); + } + } +} + +#define KMEMLEAK_FILE "/sys/kernel/debug/kmemleak" + +static void setup_leak() +{ + if (!write_file(KMEMLEAK_FILE, "scan")) + exit(1); + sleep(5); + if (!write_file(KMEMLEAK_FILE, "scan")) + exit(1); + if (!write_file(KMEMLEAK_FILE, "clear")) + exit(1); +} + +static void check_leaks(void) +{ + int fd = open(KMEMLEAK_FILE, O_RDWR); + if (fd == -1) + exit(1); + uint64_t start = current_time_ms(); + if (write(fd, "scan", 4) != 4) + exit(1); + sleep(1); + while (current_time_ms() - start < 4 * 1000) + sleep(1); + if (write(fd, "scan", 4) != 4) + exit(1); + static char buf[128 << 10]; + ssize_t n = read(fd, buf, sizeof(buf) - 1); + if (n < 0) + exit(1); + int nleaks = 0; + if (n != 0) { + sleep(1); + if (write(fd, "scan", 4) != 4) + exit(1); + if (lseek(fd, 0, SEEK_SET) < 0) + exit(1); + n = read(fd, buf, sizeof(buf) - 1); + if (n < 0) + exit(1); + buf[n] = 0; + char* pos = buf; + char* end = buf + n; + while (pos < end) { + char* next = strstr(pos + 1, "unreferenced object"); + if (!next) + next = end; + char prev = *next; + *next = 0; + fprintf(stderr, "BUG: memory leak\n%s\n", pos); + *next = prev; + pos = next; + nleaks++; + } + } + if (write(fd, "clear", 5) != 5) + exit(1); + close(fd); + if (nleaks) + exit(1); +} + +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; + } + check_leaks(); + } +} + +uint64_t r[1] = {0xffffffffffffffff}; + +void execute_one(void) +{ + intptr_t res = 0; + syscall(__NR_perf_event_open, 0ul, 0, -1ul, -1, 0ul); + memcpy((void*)0x20000040, "/dev/ptmx\000", 10); + res = syscall(__NR_openat, 0xffffffffffffff9cul, 0x20000040ul, 0ul, 0ul); + if (res != -1) + r[0] = res; + *(uint32_t*)0x20000280 = 0x11; + inject_fault(10); + syscall(__NR_ioctl, r[0], 0x5423ul, 0x20000280ul); +} +int main(void) +{ + syscall(__NR_mmap, 0x20000000ul, 0x1000000ul, 3ul, 0x32ul, -1, 0); + setup_leak(); + setup_fault(); + loop(); + return 0; +} diff --git a/syzkaller-repros/linux/45226c10813804338740a3aaf3d855a701325165.c b/syzkaller-repros/linux/45226c10813804338740a3aaf3d855a701325165.c new file mode 100644 index 0000000..33eeadf --- /dev/null +++ b/syzkaller-repros/linux/45226c10813804338740a3aaf3d855a701325165.c @@ -0,0 +1,902 @@ +// autogenerated by syzkaller (https://github.com/google/syzkaller) + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +unsigned long long procid; + +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; +} + +struct nlmsg { + char* pos; + int nesting; + struct nlattr* nested[8]; + char buf[1024]; +}; + +static struct nlmsg nlmsg; + +static void netlink_init(struct nlmsg* nlmsg, 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(struct nlmsg* nlmsg, 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 void netlink_nest(struct nlmsg* nlmsg, int typ) +{ + struct nlattr* attr = (struct nlattr*)nlmsg->pos; + attr->nla_type = typ; + nlmsg->pos += sizeof(*attr); + nlmsg->nested[nlmsg->nesting++] = attr; +} + +static void netlink_done(struct nlmsg* nlmsg) +{ + struct nlattr* attr = nlmsg->nested[--nlmsg->nesting]; + attr->nla_len = nlmsg->pos - (char*)attr; +} + +static int netlink_send_ext(struct nlmsg* nlmsg, 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 (hdr->nlmsg_type == NLMSG_DONE) { + *reply_len = 0; + return 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(struct nlmsg* nlmsg, int sock) +{ + return netlink_send_ext(nlmsg, sock, 0, NULL); +} + +static int netlink_next_msg(struct nlmsg* nlmsg, unsigned int offset, + unsigned int total_len) +{ + struct nlmsghdr* hdr = (struct nlmsghdr*)(nlmsg->buf + offset); + if (offset == total_len || offset + hdr->nlmsg_len > total_len) + return -1; + return hdr->nlmsg_len; +} + +static void netlink_add_device_impl(struct nlmsg* nlmsg, const char* type, + const char* name) +{ + struct ifinfomsg hdr; + memset(&hdr, 0, sizeof(hdr)); + netlink_init(nlmsg, RTM_NEWLINK, NLM_F_EXCL | NLM_F_CREATE, &hdr, + sizeof(hdr)); + if (name) + netlink_attr(nlmsg, IFLA_IFNAME, name, strlen(name)); + netlink_nest(nlmsg, IFLA_LINKINFO); + netlink_attr(nlmsg, IFLA_INFO_KIND, type, strlen(type)); +} + +static void netlink_add_device(struct nlmsg* nlmsg, int sock, const char* type, + const char* name) +{ + netlink_add_device_impl(nlmsg, type, name); + netlink_done(nlmsg); + int err = netlink_send(nlmsg, sock); + (void)err; +} + +static void netlink_add_veth(struct nlmsg* nlmsg, int sock, const char* name, + const char* peer) +{ + netlink_add_device_impl(nlmsg, "veth", name); + netlink_nest(nlmsg, IFLA_INFO_DATA); + netlink_nest(nlmsg, VETH_INFO_PEER); + nlmsg->pos += sizeof(struct ifinfomsg); + netlink_attr(nlmsg, IFLA_IFNAME, peer, strlen(peer)); + netlink_done(nlmsg); + netlink_done(nlmsg); + netlink_done(nlmsg); + int err = netlink_send(nlmsg, sock); + (void)err; +} + +static void netlink_add_hsr(struct nlmsg* nlmsg, int sock, const char* name, + const char* slave1, const char* slave2) +{ + netlink_add_device_impl(nlmsg, "hsr", name); + netlink_nest(nlmsg, IFLA_INFO_DATA); + int ifindex1 = if_nametoindex(slave1); + netlink_attr(nlmsg, IFLA_HSR_SLAVE1, &ifindex1, sizeof(ifindex1)); + int ifindex2 = if_nametoindex(slave2); + netlink_attr(nlmsg, IFLA_HSR_SLAVE2, &ifindex2, sizeof(ifindex2)); + netlink_done(nlmsg); + netlink_done(nlmsg); + int err = netlink_send(nlmsg, sock); + (void)err; +} + +static void netlink_add_linked(struct nlmsg* nlmsg, int sock, const char* type, + const char* name, const char* link) +{ + netlink_add_device_impl(nlmsg, type, name); + netlink_done(nlmsg); + int ifindex = if_nametoindex(link); + netlink_attr(nlmsg, IFLA_LINK, &ifindex, sizeof(ifindex)); + int err = netlink_send(nlmsg, sock); + (void)err; +} + +static void netlink_add_vlan(struct nlmsg* nlmsg, int sock, const char* name, + const char* link, uint16_t id, uint16_t proto) +{ + netlink_add_device_impl(nlmsg, "vlan", name); + netlink_nest(nlmsg, IFLA_INFO_DATA); + netlink_attr(nlmsg, IFLA_VLAN_ID, &id, sizeof(id)); + netlink_attr(nlmsg, IFLA_VLAN_PROTOCOL, &proto, sizeof(proto)); + netlink_done(nlmsg); + netlink_done(nlmsg); + int ifindex = if_nametoindex(link); + netlink_attr(nlmsg, IFLA_LINK, &ifindex, sizeof(ifindex)); + int err = netlink_send(nlmsg, sock); + (void)err; +} + +static void netlink_add_macvlan(struct nlmsg* nlmsg, int sock, const char* name, + const char* link) +{ + netlink_add_device_impl(nlmsg, "macvlan", name); + netlink_nest(nlmsg, IFLA_INFO_DATA); + uint32_t mode = MACVLAN_MODE_BRIDGE; + netlink_attr(nlmsg, IFLA_MACVLAN_MODE, &mode, sizeof(mode)); + netlink_done(nlmsg); + netlink_done(nlmsg); + int ifindex = if_nametoindex(link); + netlink_attr(nlmsg, IFLA_LINK, &ifindex, sizeof(ifindex)); + int err = netlink_send(nlmsg, sock); + (void)err; +} + +static void netlink_add_geneve(struct nlmsg* nlmsg, int sock, const char* name, + uint32_t vni, struct in_addr* addr4, + struct in6_addr* addr6) +{ + netlink_add_device_impl(nlmsg, "geneve", name); + netlink_nest(nlmsg, IFLA_INFO_DATA); + netlink_attr(nlmsg, IFLA_GENEVE_ID, &vni, sizeof(vni)); + if (addr4) + netlink_attr(nlmsg, IFLA_GENEVE_REMOTE, addr4, sizeof(*addr4)); + if (addr6) + netlink_attr(nlmsg, IFLA_GENEVE_REMOTE6, addr6, sizeof(*addr6)); + netlink_done(nlmsg); + netlink_done(nlmsg); + int err = netlink_send(nlmsg, sock); + (void)err; +} + +#define IFLA_IPVLAN_FLAGS 2 +#define IPVLAN_MODE_L3S 2 +#undef IPVLAN_F_VEPA +#define IPVLAN_F_VEPA 2 + +static void netlink_add_ipvlan(struct nlmsg* nlmsg, int sock, const char* name, + const char* link, uint16_t mode, uint16_t flags) +{ + netlink_add_device_impl(nlmsg, "ipvlan", name); + netlink_nest(nlmsg, IFLA_INFO_DATA); + netlink_attr(nlmsg, IFLA_IPVLAN_MODE, &mode, sizeof(mode)); + netlink_attr(nlmsg, IFLA_IPVLAN_FLAGS, &flags, sizeof(flags)); + netlink_done(nlmsg); + netlink_done(nlmsg); + int ifindex = if_nametoindex(link); + netlink_attr(nlmsg, IFLA_LINK, &ifindex, sizeof(ifindex)); + int err = netlink_send(nlmsg, sock); + (void)err; +} + +static void netlink_device_change(struct nlmsg* nlmsg, int sock, + const char* name, bool up, const char* master, + const void* mac, int macsize, + const char* new_name) +{ + struct ifinfomsg hdr; + memset(&hdr, 0, sizeof(hdr)); + if (up) + hdr.ifi_flags = hdr.ifi_change = IFF_UP; + hdr.ifi_index = if_nametoindex(name); + netlink_init(nlmsg, RTM_NEWLINK, 0, &hdr, sizeof(hdr)); + if (new_name) + netlink_attr(nlmsg, IFLA_IFNAME, new_name, strlen(new_name)); + if (master) { + int ifindex = if_nametoindex(master); + netlink_attr(nlmsg, IFLA_MASTER, &ifindex, sizeof(ifindex)); + } + if (macsize) + netlink_attr(nlmsg, IFLA_ADDRESS, mac, macsize); + int err = netlink_send(nlmsg, sock); + (void)err; +} + +static int netlink_add_addr(struct nlmsg* nlmsg, int sock, const char* dev, + const void* addr, int addrsize) +{ + struct ifaddrmsg hdr; + memset(&hdr, 0, sizeof(hdr)); + hdr.ifa_family = addrsize == 4 ? AF_INET : AF_INET6; + hdr.ifa_prefixlen = addrsize == 4 ? 24 : 120; + hdr.ifa_scope = RT_SCOPE_UNIVERSE; + hdr.ifa_index = if_nametoindex(dev); + netlink_init(nlmsg, RTM_NEWADDR, NLM_F_CREATE | NLM_F_REPLACE, &hdr, + sizeof(hdr)); + netlink_attr(nlmsg, IFA_LOCAL, addr, addrsize); + netlink_attr(nlmsg, IFA_ADDRESS, addr, addrsize); + return netlink_send(nlmsg, sock); +} + +static void netlink_add_addr4(struct nlmsg* nlmsg, int sock, const char* dev, + const char* addr) +{ + struct in_addr in_addr; + inet_pton(AF_INET, addr, &in_addr); + int err = netlink_add_addr(nlmsg, sock, dev, &in_addr, sizeof(in_addr)); + (void)err; +} + +static void netlink_add_addr6(struct nlmsg* nlmsg, int sock, const char* dev, + const char* addr) +{ + struct in6_addr in6_addr; + inet_pton(AF_INET6, addr, &in6_addr); + int err = netlink_add_addr(nlmsg, sock, dev, &in6_addr, sizeof(in6_addr)); + (void)err; +} + +#define DEVLINK_FAMILY_NAME "devlink" + +#define DEVLINK_CMD_PORT_GET 5 +#define DEVLINK_ATTR_BUS_NAME 1 +#define DEVLINK_ATTR_DEV_NAME 2 +#define DEVLINK_ATTR_NETDEV_NAME 7 + +static int netlink_devlink_id_get(struct nlmsg* nlmsg, int sock) +{ + struct genlmsghdr genlhdr; + struct nlattr* attr; + int err, n; + uint16_t id = 0; + memset(&genlhdr, 0, sizeof(genlhdr)); + genlhdr.cmd = CTRL_CMD_GETFAMILY; + netlink_init(nlmsg, GENL_ID_CTRL, 0, &genlhdr, sizeof(genlhdr)); + netlink_attr(nlmsg, CTRL_ATTR_FAMILY_NAME, DEVLINK_FAMILY_NAME, + strlen(DEVLINK_FAMILY_NAME) + 1); + err = netlink_send_ext(nlmsg, sock, GENL_ID_CTRL, &n); + if (err) { + return -1; + } + 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) { + return -1; + } + recv(sock, nlmsg->buf, sizeof(nlmsg->buf), 0); /* recv ack */ + return id; +} + +static struct nlmsg nlmsg2; + +static void initialize_devlink_ports(const char* bus_name, const char* dev_name, + const char* netdev_prefix) +{ + struct genlmsghdr genlhdr; + int len, total_len, id, err, offset; + uint16_t netdev_index; + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (sock == -1) + exit(1); + int rtsock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (rtsock == -1) + exit(1); + id = netlink_devlink_id_get(&nlmsg, sock); + if (id == -1) + goto error; + memset(&genlhdr, 0, sizeof(genlhdr)); + genlhdr.cmd = DEVLINK_CMD_PORT_GET; + netlink_init(&nlmsg, id, NLM_F_DUMP, &genlhdr, sizeof(genlhdr)); + netlink_attr(&nlmsg, DEVLINK_ATTR_BUS_NAME, bus_name, strlen(bus_name) + 1); + netlink_attr(&nlmsg, DEVLINK_ATTR_DEV_NAME, dev_name, strlen(dev_name) + 1); + err = netlink_send_ext(&nlmsg, sock, id, &total_len); + if (err) { + goto error; + } + offset = 0; + netdev_index = 0; + while ((len = netlink_next_msg(&nlmsg, offset, total_len)) != -1) { + struct nlattr* attr = (struct nlattr*)(nlmsg.buf + offset + NLMSG_HDRLEN + + NLMSG_ALIGN(sizeof(genlhdr))); + for (; (char*)attr < nlmsg.buf + offset + len; + attr = (struct nlattr*)((char*)attr + NLMSG_ALIGN(attr->nla_len))) { + if (attr->nla_type == DEVLINK_ATTR_NETDEV_NAME) { + char* port_name; + char netdev_name[IFNAMSIZ]; + port_name = (char*)(attr + 1); + snprintf(netdev_name, sizeof(netdev_name), "%s%d", netdev_prefix, + netdev_index); + netlink_device_change(&nlmsg2, rtsock, port_name, true, 0, 0, 0, + netdev_name); + break; + } + } + offset += len; + netdev_index++; + } +error: + close(rtsock); + close(sock); +} + +#define DEV_IPV4 "172.20.20.%d" +#define DEV_IPV6 "fe80::%02x" +#define DEV_MAC 0x00aaaaaaaaaa + +static void netdevsim_add(unsigned int addr, unsigned int port_count) +{ + char buf[16]; + sprintf(buf, "%u %u", addr, port_count); + if (write_file("/sys/bus/netdevsim/new_device", buf)) { + snprintf(buf, sizeof(buf), "netdevsim%d", addr); + initialize_devlink_ports("netdevsim", buf, "netdevsim"); + } +} +static void initialize_netdevices(void) +{ + char netdevsim[16]; + sprintf(netdevsim, "netdevsim%d", (int)procid); + struct { + const char* type; + const char* dev; + } devtypes[] = { + {"ip6gretap", "ip6gretap0"}, {"bridge", "bridge0"}, + {"vcan", "vcan0"}, {"bond", "bond0"}, + {"team", "team0"}, {"dummy", "dummy0"}, + {"nlmon", "nlmon0"}, {"caif", "caif0"}, + {"batadv", "batadv0"}, {"vxcan", "vxcan1"}, + {"netdevsim", netdevsim}, {"veth", 0}, + {"xfrm", "xfrm0"}, + }; + const char* devmasters[] = {"bridge", "bond", "team", "batadv"}; + struct { + const char* name; + int macsize; + bool noipv6; + } devices[] = { + {"lo", ETH_ALEN}, + {"sit0", 0}, + {"bridge0", ETH_ALEN}, + {"vcan0", 0, true}, + {"tunl0", 0}, + {"gre0", 0}, + {"gretap0", ETH_ALEN}, + {"ip_vti0", 0}, + {"ip6_vti0", 0}, + {"ip6tnl0", 0}, + {"ip6gre0", 0}, + {"ip6gretap0", ETH_ALEN}, + {"erspan0", ETH_ALEN}, + {"bond0", ETH_ALEN}, + {"veth0", ETH_ALEN}, + {"veth1", ETH_ALEN}, + {"team0", ETH_ALEN}, + {"veth0_to_bridge", ETH_ALEN}, + {"veth1_to_bridge", ETH_ALEN}, + {"veth0_to_bond", ETH_ALEN}, + {"veth1_to_bond", ETH_ALEN}, + {"veth0_to_team", ETH_ALEN}, + {"veth1_to_team", ETH_ALEN}, + {"veth0_to_hsr", ETH_ALEN}, + {"veth1_to_hsr", ETH_ALEN}, + {"hsr0", 0}, + {"dummy0", ETH_ALEN}, + {"nlmon0", 0}, + {"vxcan0", 0, true}, + {"vxcan1", 0, true}, + {"caif0", ETH_ALEN}, + {"batadv0", ETH_ALEN}, + {netdevsim, ETH_ALEN}, + {"xfrm0", ETH_ALEN}, + {"veth0_virt_wifi", ETH_ALEN}, + {"veth1_virt_wifi", ETH_ALEN}, + {"virt_wifi0", ETH_ALEN}, + {"veth0_vlan", ETH_ALEN}, + {"veth1_vlan", ETH_ALEN}, + {"vlan0", ETH_ALEN}, + {"vlan1", ETH_ALEN}, + {"macvlan0", ETH_ALEN}, + {"macvlan1", ETH_ALEN}, + {"ipvlan0", ETH_ALEN}, + {"ipvlan1", ETH_ALEN}, + {"veth0_macvtap", ETH_ALEN}, + {"veth1_macvtap", ETH_ALEN}, + {"macvtap0", ETH_ALEN}, + {"macsec0", ETH_ALEN}, + {"veth0_to_batadv", ETH_ALEN}, + {"veth1_to_batadv", ETH_ALEN}, + {"batadv_slave_0", ETH_ALEN}, + {"batadv_slave_1", ETH_ALEN}, + {"geneve0", ETH_ALEN}, + {"geneve1", ETH_ALEN}, + }; + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock == -1) + exit(1); + unsigned i; + for (i = 0; i < sizeof(devtypes) / sizeof(devtypes[0]); i++) + netlink_add_device(&nlmsg, sock, devtypes[i].type, devtypes[i].dev); + for (i = 0; i < sizeof(devmasters) / (sizeof(devmasters[0])); i++) { + char master[32], slave0[32], veth0[32], slave1[32], veth1[32]; + sprintf(slave0, "%s_slave_0", devmasters[i]); + sprintf(veth0, "veth0_to_%s", devmasters[i]); + netlink_add_veth(&nlmsg, sock, slave0, veth0); + sprintf(slave1, "%s_slave_1", devmasters[i]); + sprintf(veth1, "veth1_to_%s", devmasters[i]); + netlink_add_veth(&nlmsg, sock, slave1, veth1); + sprintf(master, "%s0", devmasters[i]); + netlink_device_change(&nlmsg, sock, slave0, false, master, 0, 0, NULL); + netlink_device_change(&nlmsg, sock, slave1, false, master, 0, 0, NULL); + } + netlink_device_change(&nlmsg, sock, "bridge_slave_0", true, 0, 0, 0, NULL); + netlink_device_change(&nlmsg, sock, "bridge_slave_1", true, 0, 0, 0, NULL); + netlink_add_veth(&nlmsg, sock, "hsr_slave_0", "veth0_to_hsr"); + netlink_add_veth(&nlmsg, sock, "hsr_slave_1", "veth1_to_hsr"); + netlink_add_hsr(&nlmsg, sock, "hsr0", "hsr_slave_0", "hsr_slave_1"); + netlink_device_change(&nlmsg, sock, "hsr_slave_0", true, 0, 0, 0, NULL); + netlink_device_change(&nlmsg, sock, "hsr_slave_1", true, 0, 0, 0, NULL); + netlink_add_veth(&nlmsg, sock, "veth0_virt_wifi", "veth1_virt_wifi"); + netlink_add_linked(&nlmsg, sock, "virt_wifi", "virt_wifi0", + "veth1_virt_wifi"); + netlink_add_veth(&nlmsg, sock, "veth0_vlan", "veth1_vlan"); + netlink_add_vlan(&nlmsg, sock, "vlan0", "veth0_vlan", 0, htons(ETH_P_8021Q)); + netlink_add_vlan(&nlmsg, sock, "vlan1", "veth0_vlan", 1, htons(ETH_P_8021AD)); + netlink_add_macvlan(&nlmsg, sock, "macvlan0", "veth1_vlan"); + netlink_add_macvlan(&nlmsg, sock, "macvlan1", "veth1_vlan"); + netlink_add_ipvlan(&nlmsg, sock, "ipvlan0", "veth0_vlan", IPVLAN_MODE_L2, 0); + netlink_add_ipvlan(&nlmsg, sock, "ipvlan1", "veth0_vlan", IPVLAN_MODE_L3S, + IPVLAN_F_VEPA); + netlink_add_veth(&nlmsg, sock, "veth0_macvtap", "veth1_macvtap"); + netlink_add_linked(&nlmsg, sock, "macvtap", "macvtap0", "veth0_macvtap"); + netlink_add_linked(&nlmsg, sock, "macsec", "macsec0", "veth1_macvtap"); + char addr[32]; + sprintf(addr, DEV_IPV4, 14 + 10); + struct in_addr geneve_addr4; + if (inet_pton(AF_INET, addr, &geneve_addr4) <= 0) + exit(1); + struct in6_addr geneve_addr6; + if (inet_pton(AF_INET6, "fc00::01", &geneve_addr6) <= 0) + exit(1); + netlink_add_geneve(&nlmsg, sock, "geneve0", 0, &geneve_addr4, 0); + netlink_add_geneve(&nlmsg, sock, "geneve1", 1, 0, &geneve_addr6); + netdevsim_add((int)procid, 4); + for (i = 0; i < sizeof(devices) / (sizeof(devices[0])); i++) { + char addr[32]; + sprintf(addr, DEV_IPV4, i + 10); + netlink_add_addr4(&nlmsg, sock, devices[i].name, addr); + if (!devices[i].noipv6) { + sprintf(addr, DEV_IPV6, i + 10); + netlink_add_addr6(&nlmsg, sock, devices[i].name, addr); + } + uint64_t macaddr = DEV_MAC + ((i + 10ull) << 40); + netlink_device_change(&nlmsg, sock, devices[i].name, true, 0, &macaddr, + devices[i].macsize, NULL); + } + close(sock); +} +static void initialize_netdevices_init(void) +{ + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock == -1) + exit(1); + struct { + const char* type; + int macsize; + bool noipv6; + bool noup; + } devtypes[] = { + {"nr", 7, true}, + {"rose", 5, true, true}, + }; + unsigned i; + for (i = 0; i < sizeof(devtypes) / sizeof(devtypes[0]); i++) { + char dev[32], addr[32]; + sprintf(dev, "%s%d", devtypes[i].type, (int)procid); + sprintf(addr, "172.30.%d.%d", i, (int)procid + 1); + netlink_add_addr4(&nlmsg, sock, dev, addr); + if (!devtypes[i].noipv6) { + sprintf(addr, "fe88::%02x:%02x", i, (int)procid + 1); + netlink_add_addr6(&nlmsg, sock, dev, addr); + } + int macsize = devtypes[i].macsize; + uint64_t macaddr = 0xbbbbbb + + ((unsigned long long)i << (8 * (macsize - 2))) + + (procid << (8 * (macsize - 1))); + netlink_device_change(&nlmsg, sock, dev, !devtypes[i].noup, 0, &macaddr, + macsize, NULL); + } + close(sock); +} + +#define MAX_FDS 30 + +static void setup_common() +{ + if (mount(0, "/sys/fs/fuse/connections", "fusectl", 0, 0)) { + } +} + +static void loop(); + +static void sandbox_common() +{ + prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); + setpgrp(); + setsid(); + struct rlimit rlim; + rlim.rlim_cur = rlim.rlim_max = (200 << 20); + setrlimit(RLIMIT_AS, &rlim); + rlim.rlim_cur = rlim.rlim_max = 32 << 20; + setrlimit(RLIMIT_MEMLOCK, &rlim); + rlim.rlim_cur = rlim.rlim_max = 136 << 20; + setrlimit(RLIMIT_FSIZE, &rlim); + rlim.rlim_cur = rlim.rlim_max = 1 << 20; + setrlimit(RLIMIT_STACK, &rlim); + rlim.rlim_cur = rlim.rlim_max = 0; + setrlimit(RLIMIT_CORE, &rlim); + rlim.rlim_cur = rlim.rlim_max = 256; + setrlimit(RLIMIT_NOFILE, &rlim); + if (unshare(CLONE_NEWNS)) { + } + if (unshare(CLONE_NEWIPC)) { + } + if (unshare(0x02000000)) { + } + if (unshare(CLONE_NEWUTS)) { + } + if (unshare(CLONE_SYSVSEM)) { + } + typedef struct { + const char* name; + const char* value; + } sysctl_t; + static const sysctl_t sysctls[] = { + {"/proc/sys/kernel/shmmax", "16777216"}, + {"/proc/sys/kernel/shmall", "536870912"}, + {"/proc/sys/kernel/shmmni", "1024"}, + {"/proc/sys/kernel/msgmax", "8192"}, + {"/proc/sys/kernel/msgmni", "1024"}, + {"/proc/sys/kernel/msgmnb", "1024"}, + {"/proc/sys/kernel/sem", "1024 1048576 500 1024"}, + }; + unsigned i; + for (i = 0; i < sizeof(sysctls) / sizeof(sysctls[0]); i++) + write_file(sysctls[i].name, sysctls[i].value); +} + +int wait_for_loop(int pid) +{ + if (pid < 0) + exit(1); + int status = 0; + while (waitpid(-1, &status, __WALL) != pid) { + } + return WEXITSTATUS(status); +} + +static void drop_caps(void) +{ + struct __user_cap_header_struct cap_hdr = {}; + struct __user_cap_data_struct cap_data[2] = {}; + cap_hdr.version = _LINUX_CAPABILITY_VERSION_3; + cap_hdr.pid = getpid(); + if (syscall(SYS_capget, &cap_hdr, &cap_data)) + exit(1); + const int drop = (1 << CAP_SYS_PTRACE) | (1 << CAP_SYS_NICE); + cap_data[0].effective &= ~drop; + cap_data[0].permitted &= ~drop; + cap_data[0].inheritable &= ~drop; + if (syscall(SYS_capset, &cap_hdr, &cap_data)) + exit(1); +} + +static int do_sandbox_none(void) +{ + if (unshare(CLONE_NEWPID)) { + } + int pid = fork(); + if (pid != 0) + return wait_for_loop(pid); + setup_common(); + sandbox_common(); + drop_caps(); + initialize_netdevices_init(); + if (unshare(CLONE_NEWNET)) { + } + initialize_netdevices(); + loop(); + exit(1); +} + +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 close_fds() +{ + int fd; + for (fd = 3; fd < MAX_FDS; fd++) + close(fd); +} + +#define KMEMLEAK_FILE "/sys/kernel/debug/kmemleak" + +static void setup_leak() +{ + if (!write_file(KMEMLEAK_FILE, "scan")) + exit(1); + sleep(5); + if (!write_file(KMEMLEAK_FILE, "scan")) + exit(1); + if (!write_file(KMEMLEAK_FILE, "clear")) + exit(1); +} + +static void check_leaks(void) +{ + int fd = open(KMEMLEAK_FILE, O_RDWR); + if (fd == -1) + exit(1); + uint64_t start = current_time_ms(); + if (write(fd, "scan", 4) != 4) + exit(1); + sleep(1); + while (current_time_ms() - start < 4 * 1000) + sleep(1); + if (write(fd, "scan", 4) != 4) + exit(1); + static char buf[128 << 10]; + ssize_t n = read(fd, buf, sizeof(buf) - 1); + if (n < 0) + exit(1); + int nleaks = 0; + if (n != 0) { + sleep(1); + if (write(fd, "scan", 4) != 4) + exit(1); + if (lseek(fd, 0, SEEK_SET) < 0) + exit(1); + n = read(fd, buf, sizeof(buf) - 1); + if (n < 0) + exit(1); + buf[n] = 0; + char* pos = buf; + char* end = buf + n; + while (pos < end) { + char* next = strstr(pos + 1, "unreferenced object"); + if (!next) + next = end; + char prev = *next; + *next = 0; + fprintf(stderr, "BUG: memory leak\n%s\n", pos); + *next = prev; + pos = next; + nleaks++; + } + } + if (write(fd, "clear", 5) != 5) + exit(1); + close(fd); + if (nleaks) + exit(1); +} + +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(); + close_fds(); + 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; + } + check_leaks(); + } +} + +void execute_one(void) +{ + syscall(__NR_creat, 0ul, 0ul); +} +int main(void) +{ + syscall(__NR_mmap, 0x20000000ul, 0x1000000ul, 3ul, 0x32ul, -1, 0); + setup_leak(); + for (procid = 0; procid < 8; procid++) { + if (fork() == 0) { + do_sandbox_none(); + } + } + sleep(1000000); + return 0; +} diff --git a/syzkaller-repros/linux/bb5b0d3aebeb504324b3758b03b8820b10416f87.c b/syzkaller-repros/linux/bb5b0d3aebeb504324b3758b03b8820b10416f87.c new file mode 100644 index 0000000..db1e42d --- /dev/null +++ b/syzkaller-repros/linux/bb5b0d3aebeb504324b3758b03b8820b10416f87.c @@ -0,0 +1,1268 @@ +// autogenerated by syzkaller (https://github.com/google/syzkaller) + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +unsigned long long procid; + +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 void use_temporary_dir(void) +{ + char tmpdir_template[] = "./syzkaller.XXXXXX"; + char* tmpdir = mkdtemp(tmpdir_template); + if (!tmpdir) + exit(1); + if (chmod(tmpdir, 0777)) + exit(1); + if (chdir(tmpdir)) + exit(1); +} + +static void thread_start(void* (*fn)(void*), void* arg) +{ + pthread_t th; + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setstacksize(&attr, 128 << 10); + int i; + for (i = 0; i < 100; i++) { + if (pthread_create(&th, &attr, fn, arg) == 0) { + pthread_attr_destroy(&attr); + return; + } + if (errno == EAGAIN) { + usleep(50); + continue; + } + break; + } + exit(1); +} + +typedef struct { + int state; +} event_t; + +static void event_init(event_t* ev) +{ + ev->state = 0; +} + +static void event_reset(event_t* ev) +{ + ev->state = 0; +} + +static void event_set(event_t* ev) +{ + if (ev->state) + exit(1); + __atomic_store_n(&ev->state, 1, __ATOMIC_RELEASE); + syscall(SYS_futex, &ev->state, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1000000); +} + +static void event_wait(event_t* ev) +{ + while (!__atomic_load_n(&ev->state, __ATOMIC_ACQUIRE)) + syscall(SYS_futex, &ev->state, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, 0, 0); +} + +static int event_isset(event_t* ev) +{ + return __atomic_load_n(&ev->state, __ATOMIC_ACQUIRE); +} + +static int event_timedwait(event_t* ev, uint64_t timeout) +{ + uint64_t start = current_time_ms(); + uint64_t now = start; + for (;;) { + uint64_t remain = timeout - (now - start); + struct timespec ts; + ts.tv_sec = remain / 1000; + ts.tv_nsec = (remain % 1000) * 1000 * 1000; + syscall(SYS_futex, &ev->state, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, 0, &ts); + if (__atomic_load_n(&ev->state, __ATOMIC_RELAXED)) + return 1; + now = current_time_ms(); + if (now - start > timeout) + return 0; + } +} + +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; +} + +struct nlmsg { + char* pos; + int nesting; + struct nlattr* nested[8]; + char buf[1024]; +}; + +static struct nlmsg nlmsg; + +static void netlink_init(struct nlmsg* nlmsg, 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(struct nlmsg* nlmsg, 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 void netlink_nest(struct nlmsg* nlmsg, int typ) +{ + struct nlattr* attr = (struct nlattr*)nlmsg->pos; + attr->nla_type = typ; + nlmsg->pos += sizeof(*attr); + nlmsg->nested[nlmsg->nesting++] = attr; +} + +static void netlink_done(struct nlmsg* nlmsg) +{ + struct nlattr* attr = nlmsg->nested[--nlmsg->nesting]; + attr->nla_len = nlmsg->pos - (char*)attr; +} + +static int netlink_send_ext(struct nlmsg* nlmsg, 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 (hdr->nlmsg_type == NLMSG_DONE) { + *reply_len = 0; + return 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(struct nlmsg* nlmsg, int sock) +{ + return netlink_send_ext(nlmsg, sock, 0, NULL); +} + +static int netlink_next_msg(struct nlmsg* nlmsg, unsigned int offset, + unsigned int total_len) +{ + struct nlmsghdr* hdr = (struct nlmsghdr*)(nlmsg->buf + offset); + if (offset == total_len || offset + hdr->nlmsg_len > total_len) + return -1; + return hdr->nlmsg_len; +} + +static void netlink_add_device_impl(struct nlmsg* nlmsg, const char* type, + const char* name) +{ + struct ifinfomsg hdr; + memset(&hdr, 0, sizeof(hdr)); + netlink_init(nlmsg, RTM_NEWLINK, NLM_F_EXCL | NLM_F_CREATE, &hdr, + sizeof(hdr)); + if (name) + netlink_attr(nlmsg, IFLA_IFNAME, name, strlen(name)); + netlink_nest(nlmsg, IFLA_LINKINFO); + netlink_attr(nlmsg, IFLA_INFO_KIND, type, strlen(type)); +} + +static void netlink_add_device(struct nlmsg* nlmsg, int sock, const char* type, + const char* name) +{ + netlink_add_device_impl(nlmsg, type, name); + netlink_done(nlmsg); + int err = netlink_send(nlmsg, sock); + (void)err; +} + +static void netlink_add_veth(struct nlmsg* nlmsg, int sock, const char* name, + const char* peer) +{ + netlink_add_device_impl(nlmsg, "veth", name); + netlink_nest(nlmsg, IFLA_INFO_DATA); + netlink_nest(nlmsg, VETH_INFO_PEER); + nlmsg->pos += sizeof(struct ifinfomsg); + netlink_attr(nlmsg, IFLA_IFNAME, peer, strlen(peer)); + netlink_done(nlmsg); + netlink_done(nlmsg); + netlink_done(nlmsg); + int err = netlink_send(nlmsg, sock); + (void)err; +} + +static void netlink_add_hsr(struct nlmsg* nlmsg, int sock, const char* name, + const char* slave1, const char* slave2) +{ + netlink_add_device_impl(nlmsg, "hsr", name); + netlink_nest(nlmsg, IFLA_INFO_DATA); + int ifindex1 = if_nametoindex(slave1); + netlink_attr(nlmsg, IFLA_HSR_SLAVE1, &ifindex1, sizeof(ifindex1)); + int ifindex2 = if_nametoindex(slave2); + netlink_attr(nlmsg, IFLA_HSR_SLAVE2, &ifindex2, sizeof(ifindex2)); + netlink_done(nlmsg); + netlink_done(nlmsg); + int err = netlink_send(nlmsg, sock); + (void)err; +} + +static void netlink_device_change(struct nlmsg* nlmsg, int sock, + const char* name, bool up, const char* master, + const void* mac, int macsize, + const char* new_name) +{ + struct ifinfomsg hdr; + memset(&hdr, 0, sizeof(hdr)); + if (up) + hdr.ifi_flags = hdr.ifi_change = IFF_UP; + hdr.ifi_index = if_nametoindex(name); + netlink_init(nlmsg, RTM_NEWLINK, 0, &hdr, sizeof(hdr)); + if (new_name) + netlink_attr(nlmsg, IFLA_IFNAME, new_name, strlen(new_name)); + if (master) { + int ifindex = if_nametoindex(master); + netlink_attr(nlmsg, IFLA_MASTER, &ifindex, sizeof(ifindex)); + } + if (macsize) + netlink_attr(nlmsg, IFLA_ADDRESS, mac, macsize); + int err = netlink_send(nlmsg, sock); + (void)err; +} + +static int netlink_add_addr(struct nlmsg* nlmsg, int sock, const char* dev, + const void* addr, int addrsize) +{ + struct ifaddrmsg hdr; + memset(&hdr, 0, sizeof(hdr)); + hdr.ifa_family = addrsize == 4 ? AF_INET : AF_INET6; + hdr.ifa_prefixlen = addrsize == 4 ? 24 : 120; + hdr.ifa_scope = RT_SCOPE_UNIVERSE; + hdr.ifa_index = if_nametoindex(dev); + netlink_init(nlmsg, RTM_NEWADDR, NLM_F_CREATE | NLM_F_REPLACE, &hdr, + sizeof(hdr)); + netlink_attr(nlmsg, IFA_LOCAL, addr, addrsize); + netlink_attr(nlmsg, IFA_ADDRESS, addr, addrsize); + return netlink_send(nlmsg, sock); +} + +static void netlink_add_addr4(struct nlmsg* nlmsg, int sock, const char* dev, + const char* addr) +{ + struct in_addr in_addr; + inet_pton(AF_INET, addr, &in_addr); + int err = netlink_add_addr(nlmsg, sock, dev, &in_addr, sizeof(in_addr)); + (void)err; +} + +static void netlink_add_addr6(struct nlmsg* nlmsg, int sock, const char* dev, + const char* addr) +{ + struct in6_addr in6_addr; + inet_pton(AF_INET6, addr, &in6_addr); + int err = netlink_add_addr(nlmsg, sock, dev, &in6_addr, sizeof(in6_addr)); + (void)err; +} + +static void netlink_add_neigh(struct nlmsg* nlmsg, int sock, const char* name, + const void* addr, int addrsize, const void* mac, + int macsize) +{ + struct ndmsg hdr; + memset(&hdr, 0, sizeof(hdr)); + hdr.ndm_family = addrsize == 4 ? AF_INET : AF_INET6; + hdr.ndm_ifindex = if_nametoindex(name); + hdr.ndm_state = NUD_PERMANENT; + netlink_init(nlmsg, RTM_NEWNEIGH, NLM_F_EXCL | NLM_F_CREATE, &hdr, + sizeof(hdr)); + netlink_attr(nlmsg, NDA_DST, addr, addrsize); + netlink_attr(nlmsg, NDA_LLADDR, mac, macsize); + int err = netlink_send(nlmsg, sock); + (void)err; +} + +static int tunfd = -1; +static int tun_frags_enabled; + +#define TUN_IFACE "syz_tun" + +#define LOCAL_MAC 0xaaaaaaaaaaaa +#define REMOTE_MAC 0xaaaaaaaaaabb + +#define LOCAL_IPV4 "172.20.20.170" +#define REMOTE_IPV4 "172.20.20.187" + +#define LOCAL_IPV6 "fe80::aa" +#define REMOTE_IPV6 "fe80::bb" + +#define IFF_NAPI 0x0010 +#define IFF_NAPI_FRAGS 0x0020 + +static void initialize_tun(void) +{ + tunfd = open("/dev/net/tun", O_RDWR | O_NONBLOCK); + if (tunfd == -1) { + printf("tun: can't open /dev/net/tun: please enable CONFIG_TUN=y\n"); + printf("otherwise fuzzing or reproducing might not work as intended\n"); + return; + } + const int kTunFd = 240; + if (dup2(tunfd, kTunFd) < 0) + exit(1); + close(tunfd); + tunfd = kTunFd; + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, TUN_IFACE, IFNAMSIZ); + ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_NAPI | IFF_NAPI_FRAGS; + if (ioctl(tunfd, TUNSETIFF, (void*)&ifr) < 0) { + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + if (ioctl(tunfd, TUNSETIFF, (void*)&ifr) < 0) + exit(1); + } + if (ioctl(tunfd, TUNGETIFF, (void*)&ifr) < 0) + exit(1); + tun_frags_enabled = (ifr.ifr_flags & IFF_NAPI_FRAGS) != 0; + char sysctl[64]; + sprintf(sysctl, "/proc/sys/net/ipv6/conf/%s/accept_dad", TUN_IFACE); + write_file(sysctl, "0"); + sprintf(sysctl, "/proc/sys/net/ipv6/conf/%s/router_solicitations", TUN_IFACE); + write_file(sysctl, "0"); + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock == -1) + exit(1); + netlink_add_addr4(&nlmsg, sock, TUN_IFACE, LOCAL_IPV4); + netlink_add_addr6(&nlmsg, sock, TUN_IFACE, LOCAL_IPV6); + uint64_t macaddr = REMOTE_MAC; + struct in_addr in_addr; + inet_pton(AF_INET, REMOTE_IPV4, &in_addr); + netlink_add_neigh(&nlmsg, sock, TUN_IFACE, &in_addr, sizeof(in_addr), + &macaddr, ETH_ALEN); + struct in6_addr in6_addr; + inet_pton(AF_INET6, REMOTE_IPV6, &in6_addr); + netlink_add_neigh(&nlmsg, sock, TUN_IFACE, &in6_addr, sizeof(in6_addr), + &macaddr, ETH_ALEN); + macaddr = LOCAL_MAC; + netlink_device_change(&nlmsg, sock, TUN_IFACE, true, 0, &macaddr, ETH_ALEN, + NULL); + close(sock); +} + +const int kInitNetNsFd = 239; + +#define DEVLINK_FAMILY_NAME "devlink" + +#define DEVLINK_CMD_PORT_GET 5 +#define DEVLINK_CMD_RELOAD 37 +#define DEVLINK_ATTR_BUS_NAME 1 +#define DEVLINK_ATTR_DEV_NAME 2 +#define DEVLINK_ATTR_NETDEV_NAME 7 +#define DEVLINK_ATTR_NETNS_FD 138 + +static int netlink_devlink_id_get(struct nlmsg* nlmsg, int sock) +{ + struct genlmsghdr genlhdr; + struct nlattr* attr; + int err, n; + uint16_t id = 0; + memset(&genlhdr, 0, sizeof(genlhdr)); + genlhdr.cmd = CTRL_CMD_GETFAMILY; + netlink_init(nlmsg, GENL_ID_CTRL, 0, &genlhdr, sizeof(genlhdr)); + netlink_attr(nlmsg, CTRL_ATTR_FAMILY_NAME, DEVLINK_FAMILY_NAME, + strlen(DEVLINK_FAMILY_NAME) + 1); + err = netlink_send_ext(nlmsg, sock, GENL_ID_CTRL, &n); + if (err) { + return -1; + } + 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) { + return -1; + } + recv(sock, nlmsg->buf, sizeof(nlmsg->buf), 0); /* recv ack */ + return id; +} + +static void netlink_devlink_netns_move(const char* bus_name, + const char* dev_name, int netns_fd) +{ + struct genlmsghdr genlhdr; + int sock; + int id, err; + sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (sock == -1) + exit(1); + id = netlink_devlink_id_get(&nlmsg, sock); + if (id == -1) + goto error; + memset(&genlhdr, 0, sizeof(genlhdr)); + genlhdr.cmd = DEVLINK_CMD_RELOAD; + netlink_init(&nlmsg, id, 0, &genlhdr, sizeof(genlhdr)); + netlink_attr(&nlmsg, DEVLINK_ATTR_BUS_NAME, bus_name, strlen(bus_name) + 1); + netlink_attr(&nlmsg, DEVLINK_ATTR_DEV_NAME, dev_name, strlen(dev_name) + 1); + netlink_attr(&nlmsg, DEVLINK_ATTR_NETNS_FD, &netns_fd, sizeof(netns_fd)); + err = netlink_send(&nlmsg, sock); + if (err) { + } +error: + close(sock); +} + +static struct nlmsg nlmsg2; + +static void initialize_devlink_ports(const char* bus_name, const char* dev_name, + const char* netdev_prefix) +{ + struct genlmsghdr genlhdr; + int len, total_len, id, err, offset; + uint16_t netdev_index; + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (sock == -1) + exit(1); + int rtsock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (rtsock == -1) + exit(1); + id = netlink_devlink_id_get(&nlmsg, sock); + if (id == -1) + goto error; + memset(&genlhdr, 0, sizeof(genlhdr)); + genlhdr.cmd = DEVLINK_CMD_PORT_GET; + netlink_init(&nlmsg, id, NLM_F_DUMP, &genlhdr, sizeof(genlhdr)); + netlink_attr(&nlmsg, DEVLINK_ATTR_BUS_NAME, bus_name, strlen(bus_name) + 1); + netlink_attr(&nlmsg, DEVLINK_ATTR_DEV_NAME, dev_name, strlen(dev_name) + 1); + err = netlink_send_ext(&nlmsg, sock, id, &total_len); + if (err) { + goto error; + } + offset = 0; + netdev_index = 0; + while ((len = netlink_next_msg(&nlmsg, offset, total_len)) != -1) { + struct nlattr* attr = (struct nlattr*)(nlmsg.buf + offset + NLMSG_HDRLEN + + NLMSG_ALIGN(sizeof(genlhdr))); + for (; (char*)attr < nlmsg.buf + offset + len; + attr = (struct nlattr*)((char*)attr + NLMSG_ALIGN(attr->nla_len))) { + if (attr->nla_type == DEVLINK_ATTR_NETDEV_NAME) { + char* port_name; + char netdev_name[IFNAMSIZ]; + port_name = (char*)(attr + 1); + snprintf(netdev_name, sizeof(netdev_name), "%s%d", netdev_prefix, + netdev_index); + netlink_device_change(&nlmsg2, rtsock, port_name, true, 0, 0, 0, + netdev_name); + break; + } + } + offset += len; + netdev_index++; + } +error: + close(rtsock); + 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); + initialize_devlink_ports("pci", "0000:00:10.0", "netpci"); +} + +#define DEV_IPV4 "172.20.20.%d" +#define DEV_IPV6 "fe80::%02x" +#define DEV_MAC 0x00aaaaaaaaaa + +static void netdevsim_add(unsigned int addr, unsigned int port_count) +{ + char buf[16]; + sprintf(buf, "%u %u", addr, port_count); + if (write_file("/sys/bus/netdevsim/new_device", buf)) { + snprintf(buf, sizeof(buf), "netdevsim%d", addr); + initialize_devlink_ports("netdevsim", buf, "netdevsim"); + } +} +static void initialize_netdevices(void) +{ + char netdevsim[16]; + sprintf(netdevsim, "netdevsim%d", (int)procid); + struct { + const char* type; + const char* dev; + } devtypes[] = { + {"ip6gretap", "ip6gretap0"}, {"bridge", "bridge0"}, + {"vcan", "vcan0"}, {"bond", "bond0"}, + {"team", "team0"}, {"dummy", "dummy0"}, + {"nlmon", "nlmon0"}, {"caif", "caif0"}, + {"batadv", "batadv0"}, {"vxcan", "vxcan1"}, + {"netdevsim", netdevsim}, {"veth", 0}, + }; + const char* devmasters[] = {"bridge", "bond", "team"}; + struct { + const char* name; + int macsize; + bool noipv6; + } devices[] = { + {"lo", ETH_ALEN}, + {"sit0", 0}, + {"bridge0", ETH_ALEN}, + {"vcan0", 0, true}, + {"tunl0", 0}, + {"gre0", 0}, + {"gretap0", ETH_ALEN}, + {"ip_vti0", 0}, + {"ip6_vti0", 0}, + {"ip6tnl0", 0}, + {"ip6gre0", 0}, + {"ip6gretap0", ETH_ALEN}, + {"erspan0", ETH_ALEN}, + {"bond0", ETH_ALEN}, + {"veth0", ETH_ALEN}, + {"veth1", ETH_ALEN}, + {"team0", ETH_ALEN}, + {"veth0_to_bridge", ETH_ALEN}, + {"veth1_to_bridge", ETH_ALEN}, + {"veth0_to_bond", ETH_ALEN}, + {"veth1_to_bond", ETH_ALEN}, + {"veth0_to_team", ETH_ALEN}, + {"veth1_to_team", ETH_ALEN}, + {"veth0_to_hsr", ETH_ALEN}, + {"veth1_to_hsr", ETH_ALEN}, + {"hsr0", 0}, + {"dummy0", ETH_ALEN}, + {"nlmon0", 0}, + {"vxcan0", 0, true}, + {"vxcan1", 0, true}, + {"caif0", ETH_ALEN}, + {"batadv0", ETH_ALEN}, + {netdevsim, ETH_ALEN}, + }; + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock == -1) + exit(1); + unsigned i; + for (i = 0; i < sizeof(devtypes) / sizeof(devtypes[0]); i++) + netlink_add_device(&nlmsg, sock, devtypes[i].type, devtypes[i].dev); + for (i = 0; i < sizeof(devmasters) / (sizeof(devmasters[0])); i++) { + char master[32], slave0[32], veth0[32], slave1[32], veth1[32]; + sprintf(slave0, "%s_slave_0", devmasters[i]); + sprintf(veth0, "veth0_to_%s", devmasters[i]); + netlink_add_veth(&nlmsg, sock, slave0, veth0); + sprintf(slave1, "%s_slave_1", devmasters[i]); + sprintf(veth1, "veth1_to_%s", devmasters[i]); + netlink_add_veth(&nlmsg, sock, slave1, veth1); + sprintf(master, "%s0", devmasters[i]); + netlink_device_change(&nlmsg, sock, slave0, false, master, 0, 0, NULL); + netlink_device_change(&nlmsg, sock, slave1, false, master, 0, 0, NULL); + } + netlink_device_change(&nlmsg, sock, "bridge_slave_0", true, 0, 0, 0, NULL); + netlink_device_change(&nlmsg, sock, "bridge_slave_1", true, 0, 0, 0, NULL); + netlink_add_veth(&nlmsg, sock, "hsr_slave_0", "veth0_to_hsr"); + netlink_add_veth(&nlmsg, sock, "hsr_slave_1", "veth1_to_hsr"); + netlink_add_hsr(&nlmsg, sock, "hsr0", "hsr_slave_0", "hsr_slave_1"); + netlink_device_change(&nlmsg, sock, "hsr_slave_0", true, 0, 0, 0, NULL); + netlink_device_change(&nlmsg, sock, "hsr_slave_1", true, 0, 0, 0, NULL); + netdevsim_add((int)procid, 4); + for (i = 0; i < sizeof(devices) / (sizeof(devices[0])); i++) { + char addr[32]; + sprintf(addr, DEV_IPV4, i + 10); + netlink_add_addr4(&nlmsg, sock, devices[i].name, addr); + if (!devices[i].noipv6) { + sprintf(addr, DEV_IPV6, i + 10); + netlink_add_addr6(&nlmsg, sock, devices[i].name, addr); + } + uint64_t macaddr = DEV_MAC + ((i + 10ull) << 40); + netlink_device_change(&nlmsg, sock, devices[i].name, true, 0, &macaddr, + devices[i].macsize, NULL); + } + close(sock); +} +static void initialize_netdevices_init(void) +{ + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock == -1) + exit(1); + struct { + const char* type; + int macsize; + bool noipv6; + bool noup; + } devtypes[] = { + {"nr", 7, true}, + {"rose", 5, true, true}, + }; + unsigned i; + for (i = 0; i < sizeof(devtypes) / sizeof(devtypes[0]); i++) { + char dev[32], addr[32]; + sprintf(dev, "%s%d", devtypes[i].type, (int)procid); + sprintf(addr, "172.30.%d.%d", i, (int)procid + 1); + netlink_add_addr4(&nlmsg, sock, dev, addr); + if (!devtypes[i].noipv6) { + sprintf(addr, "fe88::%02x:%02x", i, (int)procid + 1); + netlink_add_addr6(&nlmsg, sock, dev, addr); + } + int macsize = devtypes[i].macsize; + uint64_t macaddr = 0xbbbbbb + + ((unsigned long long)i << (8 * (macsize - 2))) + + (procid << (8 * (macsize - 1))); + netlink_device_change(&nlmsg, sock, dev, !devtypes[i].noup, 0, &macaddr, + macsize, NULL); + } + close(sock); +} + +static int read_tun(char* data, int size) +{ + if (tunfd < 0) + return -1; + int rv = read(tunfd, data, size); + if (rv < 0) { + if (errno == EAGAIN) + return -1; + if (errno == EBADFD) + return -1; + exit(1); + } + return rv; +} + +static void flush_tun() +{ + char data[1000]; + while (read_tun(&data[0], sizeof(data)) != -1) { + } +} + +#define MAX_FDS 30 + +static void setup_cgroups() +{ + if (mkdir("/syzcgroup", 0777)) { + } + if (mkdir("/syzcgroup/unified", 0777)) { + } + if (mount("none", "/syzcgroup/unified", "cgroup2", 0, NULL)) { + } + if (chmod("/syzcgroup/unified", 0777)) { + } + write_file("/syzcgroup/unified/cgroup.subtree_control", + "+cpu +memory +io +pids +rdma"); + if (mkdir("/syzcgroup/cpu", 0777)) { + } + if (mount("none", "/syzcgroup/cpu", "cgroup", 0, + "cpuset,cpuacct,perf_event,hugetlb")) { + } + write_file("/syzcgroup/cpu/cgroup.clone_children", "1"); + if (chmod("/syzcgroup/cpu", 0777)) { + } + if (mkdir("/syzcgroup/net", 0777)) { + } + if (mount("none", "/syzcgroup/net", "cgroup", 0, + "net_cls,net_prio,devices,freezer")) { + } + if (chmod("/syzcgroup/net", 0777)) { + } +} + +static void setup_cgroups_loop() +{ + int pid = getpid(); + char file[128]; + char cgroupdir[64]; + snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/unified/syz%llu", procid); + if (mkdir(cgroupdir, 0777)) { + } + snprintf(file, sizeof(file), "%s/pids.max", cgroupdir); + write_file(file, "32"); + snprintf(file, sizeof(file), "%s/memory.low", cgroupdir); + write_file(file, "%d", 298 << 20); + snprintf(file, sizeof(file), "%s/memory.high", cgroupdir); + write_file(file, "%d", 299 << 20); + snprintf(file, sizeof(file), "%s/memory.max", cgroupdir); + write_file(file, "%d", 300 << 20); + snprintf(file, sizeof(file), "%s/cgroup.procs", cgroupdir); + write_file(file, "%d", pid); + snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/cpu/syz%llu", procid); + if (mkdir(cgroupdir, 0777)) { + } + snprintf(file, sizeof(file), "%s/cgroup.procs", cgroupdir); + write_file(file, "%d", pid); + snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/net/syz%llu", procid); + if (mkdir(cgroupdir, 0777)) { + } + snprintf(file, sizeof(file), "%s/cgroup.procs", cgroupdir); + write_file(file, "%d", pid); +} + +static void setup_cgroups_test() +{ + char cgroupdir[64]; + snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/unified/syz%llu", procid); + if (symlink(cgroupdir, "./cgroup")) { + } + snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/cpu/syz%llu", procid); + if (symlink(cgroupdir, "./cgroup.cpu")) { + } + snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/net/syz%llu", procid); + if (symlink(cgroupdir, "./cgroup.net")) { + } +} + +static void setup_common() +{ + if (mount(0, "/sys/fs/fuse/connections", "fusectl", 0, 0)) { + } + setup_cgroups(); +} + +static void loop(); + +static void sandbox_common() +{ + prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); + setpgrp(); + setsid(); + int netns = open("/proc/self/ns/net", O_RDONLY); + if (netns == -1) + exit(1); + if (dup2(netns, kInitNetNsFd) < 0) + exit(1); + close(netns); + struct rlimit rlim; + rlim.rlim_cur = rlim.rlim_max = (200 << 20); + setrlimit(RLIMIT_AS, &rlim); + rlim.rlim_cur = rlim.rlim_max = 32 << 20; + setrlimit(RLIMIT_MEMLOCK, &rlim); + rlim.rlim_cur = rlim.rlim_max = 136 << 20; + setrlimit(RLIMIT_FSIZE, &rlim); + rlim.rlim_cur = rlim.rlim_max = 1 << 20; + setrlimit(RLIMIT_STACK, &rlim); + rlim.rlim_cur = rlim.rlim_max = 0; + setrlimit(RLIMIT_CORE, &rlim); + rlim.rlim_cur = rlim.rlim_max = 256; + setrlimit(RLIMIT_NOFILE, &rlim); + if (unshare(CLONE_NEWNS)) { + } + if (unshare(CLONE_NEWIPC)) { + } + if (unshare(0x02000000)) { + } + if (unshare(CLONE_NEWUTS)) { + } + if (unshare(CLONE_SYSVSEM)) { + } + typedef struct { + const char* name; + const char* value; + } sysctl_t; + static const sysctl_t sysctls[] = { + {"/proc/sys/kernel/shmmax", "16777216"}, + {"/proc/sys/kernel/shmall", "536870912"}, + {"/proc/sys/kernel/shmmni", "1024"}, + {"/proc/sys/kernel/msgmax", "8192"}, + {"/proc/sys/kernel/msgmni", "1024"}, + {"/proc/sys/kernel/msgmnb", "1024"}, + {"/proc/sys/kernel/sem", "1024 1048576 500 1024"}, + }; + unsigned i; + for (i = 0; i < sizeof(sysctls) / sizeof(sysctls[0]); i++) + write_file(sysctls[i].name, sysctls[i].value); +} + +int wait_for_loop(int pid) +{ + if (pid < 0) + exit(1); + int status = 0; + while (waitpid(-1, &status, __WALL) != pid) { + } + return WEXITSTATUS(status); +} + +static void drop_caps(void) +{ + struct __user_cap_header_struct cap_hdr = {}; + struct __user_cap_data_struct cap_data[2] = {}; + cap_hdr.version = _LINUX_CAPABILITY_VERSION_3; + cap_hdr.pid = getpid(); + if (syscall(SYS_capget, &cap_hdr, &cap_data)) + exit(1); + const int drop = (1 << CAP_SYS_PTRACE) | (1 << CAP_SYS_NICE); + cap_data[0].effective &= ~drop; + cap_data[0].permitted &= ~drop; + cap_data[0].inheritable &= ~drop; + if (syscall(SYS_capset, &cap_hdr, &cap_data)) + exit(1); +} + +static int do_sandbox_none(void) +{ + if (unshare(CLONE_NEWPID)) { + } + int pid = fork(); + if (pid != 0) + return wait_for_loop(pid); + setup_common(); + sandbox_common(); + drop_caps(); + initialize_netdevices_init(); + if (unshare(CLONE_NEWNET)) { + } + initialize_devlink_pci(); + initialize_tun(); + initialize_netdevices(); + loop(); + exit(1); +} + +#define FS_IOC_SETFLAGS _IOW('f', 2, long) +static void remove_dir(const char* dir) +{ + DIR* dp; + struct dirent* ep; + int iter = 0; +retry: + while (umount2(dir, MNT_DETACH) == 0) { + } + dp = opendir(dir); + if (dp == NULL) { + if (errno == EMFILE) { + exit(1); + } + exit(1); + } + while ((ep = readdir(dp))) { + if (strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0) + continue; + char filename[FILENAME_MAX]; + snprintf(filename, sizeof(filename), "%s/%s", dir, ep->d_name); + while (umount2(filename, MNT_DETACH) == 0) { + } + struct stat st; + if (lstat(filename, &st)) + exit(1); + if (S_ISDIR(st.st_mode)) { + remove_dir(filename); + continue; + } + int i; + for (i = 0;; i++) { + if (unlink(filename) == 0) + break; + if (errno == EPERM) { + int fd = open(filename, O_RDONLY); + if (fd != -1) { + long flags = 0; + if (ioctl(fd, FS_IOC_SETFLAGS, &flags) == 0) { + } + close(fd); + continue; + } + } + if (errno == EROFS) { + break; + } + if (errno != EBUSY || i > 100) + exit(1); + if (umount2(filename, MNT_DETACH)) + exit(1); + } + } + closedir(dp); + int i; + for (i = 0;; i++) { + if (rmdir(dir) == 0) + break; + if (i < 100) { + if (errno == EPERM) { + int fd = open(dir, O_RDONLY); + if (fd != -1) { + long flags = 0; + if (ioctl(fd, FS_IOC_SETFLAGS, &flags) == 0) { + } + close(fd); + continue; + } + } + if (errno == EROFS) { + break; + } + if (errno == EBUSY) { + if (umount2(dir, MNT_DETACH)) + exit(1); + continue; + } + if (errno == ENOTEMPTY) { + if (iter < 100) { + iter++; + goto retry; + } + } + } + exit(1); + } +} + +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_loop() +{ + setup_cgroups_loop(); +} + +static void setup_test() +{ + prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); + setpgrp(); + setup_cgroups_test(); + write_file("/proc/self/oom_score_adj", "1000"); + flush_tun(); +} + +static void close_fds() +{ + int fd; + for (fd = 3; fd < MAX_FDS; fd++) + close(fd); +} + +#define KMEMLEAK_FILE "/sys/kernel/debug/kmemleak" + +static void setup_leak() +{ + if (!write_file(KMEMLEAK_FILE, "scan")) + exit(1); + sleep(5); + if (!write_file(KMEMLEAK_FILE, "scan")) + exit(1); + if (!write_file(KMEMLEAK_FILE, "clear")) + exit(1); +} + +static void check_leaks(void) +{ + int fd = open(KMEMLEAK_FILE, O_RDWR); + if (fd == -1) + exit(1); + uint64_t start = current_time_ms(); + if (write(fd, "scan", 4) != 4) + exit(1); + sleep(1); + while (current_time_ms() - start < 4 * 1000) + sleep(1); + if (write(fd, "scan", 4) != 4) + exit(1); + static char buf[128 << 10]; + ssize_t n = read(fd, buf, sizeof(buf) - 1); + if (n < 0) + exit(1); + int nleaks = 0; + if (n != 0) { + sleep(1); + if (write(fd, "scan", 4) != 4) + exit(1); + if (lseek(fd, 0, SEEK_SET) < 0) + exit(1); + n = read(fd, buf, sizeof(buf) - 1); + if (n < 0) + exit(1); + buf[n] = 0; + char* pos = buf; + char* end = buf + n; + while (pos < end) { + char* next = strstr(pos + 1, "unreferenced object"); + if (!next) + next = end; + char prev = *next; + *next = 0; + fprintf(stderr, "BUG: memory leak\n%s\n", pos); + *next = prev; + pos = next; + nleaks++; + } + } + if (write(fd, "clear", 5) != 5) + exit(1); + close(fd); + if (nleaks) + exit(1); +} + +struct thread_t { + int created, call; + event_t ready, done; +}; + +static struct thread_t threads[16]; +static void execute_call(int call); +static int running; + +static void* thr(void* arg) +{ + struct thread_t* th = (struct thread_t*)arg; + for (;;) { + event_wait(&th->ready); + event_reset(&th->ready); + execute_call(th->call); + __atomic_fetch_sub(&running, 1, __ATOMIC_RELAXED); + event_set(&th->done); + } + return 0; +} + +static void execute_one(void) +{ + int i, call, thread; + for (call = 0; call < 3; call++) { + for (thread = 0; thread < (int)(sizeof(threads) / sizeof(threads[0])); + thread++) { + struct thread_t* th = &threads[thread]; + if (!th->created) { + th->created = 1; + event_init(&th->ready); + event_init(&th->done); + event_set(&th->done); + thread_start(thr, th); + } + if (!event_isset(&th->done)) + continue; + event_reset(&th->done); + th->call = call; + __atomic_fetch_add(&running, 1, __ATOMIC_RELAXED); + event_set(&th->ready); + event_timedwait(&th->done, 45); + break; + } + } + for (i = 0; i < 100 && __atomic_load_n(&running, __ATOMIC_RELAXED); i++) + sleep_ms(1); + close_fds(); +} + +static void execute_one(void); + +#define WAIT_FLAGS __WALL + +static void loop(void) +{ + setup_loop(); + int iter; + for (iter = 0;; iter++) { + char cwdbuf[32]; + sprintf(cwdbuf, "./%d", iter); + if (mkdir(cwdbuf, 0777)) + exit(1); + int pid = fork(); + if (pid < 0) + exit(1); + if (pid == 0) { + if (chdir(cwdbuf)) + exit(1); + 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; + } + remove_dir(cwdbuf); + check_leaks(); + } +} + +uint64_t r[1] = {0xffffffffffffffff}; + +void execute_call(int call) +{ + intptr_t res; + switch (call) { + case 0: + res = syscall(__NR_socket, 0x10ul, 3ul, 0xcul); + if (res != -1) + r[0] = res; + break; + case 1: + syscall(__NR_socket, 0xaul, 2ul, 0x3aul); + break; + case 2: + memcpy((void*)0x20000000, "bridge0\000\000\000\000\000\000\000\000\000", + 16); + *(uint64_t*)0x20000010 = 0x2000000000009; + *(uint64_t*)0x20000018 = 0; + *(uint16_t*)0x20000020 = 0; + *(uint8_t*)0x20000022 = 0; + *(uint8_t*)0x20000023 = 0; + *(uint8_t*)0x20000024 = 0; + syscall(__NR_ioctl, r[0], 0x89a2ul, 0x20000000ul); + break; + } +} +int main(void) +{ + syscall(__NR_mmap, 0x20000000ul, 0x1000000ul, 3ul, 0x32ul, -1, 0); + setup_leak(); + use_temporary_dir(); + do_sandbox_none(); + return 0; +} diff --git a/syzkaller-repros/linux/ccef92ea0c38c3e4def623d61d57f625c3d6d9a4.c b/syzkaller-repros/linux/ccef92ea0c38c3e4def623d61d57f625c3d6d9a4.c new file mode 100644 index 0000000..ad353a6 --- /dev/null +++ b/syzkaller-repros/linux/ccef92ea0c38c3e4def623d61d57f625c3d6d9a4.c @@ -0,0 +1,488 @@ +// autogenerated by syzkaller (https://github.com/google/syzkaller) + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} + +struct nlmsg { + char* pos; + int nesting; + struct nlattr* nested[8]; + char buf[1024]; +}; + +static struct nlmsg nlmsg; + +static void netlink_init(struct nlmsg* nlmsg, 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(struct nlmsg* nlmsg, 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(struct nlmsg* nlmsg, 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 (hdr->nlmsg_type == NLMSG_DONE) { + *reply_len = 0; + return 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(struct nlmsg* nlmsg, int sock) +{ + return netlink_send_ext(nlmsg, sock, 0, NULL); +} + +static int netlink_next_msg(struct nlmsg* nlmsg, unsigned int offset, + unsigned int total_len) +{ + struct nlmsghdr* hdr = (struct nlmsghdr*)(nlmsg->buf + offset); + if (offset == total_len || offset + hdr->nlmsg_len > total_len) + return -1; + return hdr->nlmsg_len; +} + +static void netlink_device_change(struct nlmsg* nlmsg, int sock, + const char* name, bool up, const char* master, + const void* mac, int macsize, + const char* new_name) +{ + struct ifinfomsg hdr; + memset(&hdr, 0, sizeof(hdr)); + if (up) + hdr.ifi_flags = hdr.ifi_change = IFF_UP; + hdr.ifi_index = if_nametoindex(name); + netlink_init(nlmsg, RTM_NEWLINK, 0, &hdr, sizeof(hdr)); + if (new_name) + netlink_attr(nlmsg, IFLA_IFNAME, new_name, strlen(new_name)); + if (master) { + int ifindex = if_nametoindex(master); + netlink_attr(nlmsg, IFLA_MASTER, &ifindex, sizeof(ifindex)); + } + if (macsize) + netlink_attr(nlmsg, IFLA_ADDRESS, mac, macsize); + int err = netlink_send(nlmsg, sock); + (void)err; +} + +const int kInitNetNsFd = 239; + +#define DEVLINK_FAMILY_NAME "devlink" + +#define DEVLINK_CMD_PORT_GET 5 +#define DEVLINK_CMD_RELOAD 37 +#define DEVLINK_ATTR_BUS_NAME 1 +#define DEVLINK_ATTR_DEV_NAME 2 +#define DEVLINK_ATTR_NETDEV_NAME 7 +#define DEVLINK_ATTR_NETNS_FD 138 + +static int netlink_devlink_id_get(struct nlmsg* nlmsg, int sock) +{ + struct genlmsghdr genlhdr; + struct nlattr* attr; + int err, n; + uint16_t id = 0; + memset(&genlhdr, 0, sizeof(genlhdr)); + genlhdr.cmd = CTRL_CMD_GETFAMILY; + netlink_init(nlmsg, GENL_ID_CTRL, 0, &genlhdr, sizeof(genlhdr)); + netlink_attr(nlmsg, CTRL_ATTR_FAMILY_NAME, DEVLINK_FAMILY_NAME, + strlen(DEVLINK_FAMILY_NAME) + 1); + err = netlink_send_ext(nlmsg, sock, GENL_ID_CTRL, &n); + if (err) { + return -1; + } + 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) { + return -1; + } + recv(sock, nlmsg->buf, sizeof(nlmsg->buf), 0); /* recv ack */ + return id; +} + +static void netlink_devlink_netns_move(const char* bus_name, + const char* dev_name, int netns_fd) +{ + struct genlmsghdr genlhdr; + int sock; + int id, err; + sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (sock == -1) + exit(1); + id = netlink_devlink_id_get(&nlmsg, sock); + if (id == -1) + goto error; + memset(&genlhdr, 0, sizeof(genlhdr)); + genlhdr.cmd = DEVLINK_CMD_RELOAD; + netlink_init(&nlmsg, id, 0, &genlhdr, sizeof(genlhdr)); + netlink_attr(&nlmsg, DEVLINK_ATTR_BUS_NAME, bus_name, strlen(bus_name) + 1); + netlink_attr(&nlmsg, DEVLINK_ATTR_DEV_NAME, dev_name, strlen(dev_name) + 1); + netlink_attr(&nlmsg, DEVLINK_ATTR_NETNS_FD, &netns_fd, sizeof(netns_fd)); + err = netlink_send(&nlmsg, sock); + if (err) { + } +error: + close(sock); +} + +static struct nlmsg nlmsg2; + +static void initialize_devlink_ports(const char* bus_name, const char* dev_name, + const char* netdev_prefix) +{ + struct genlmsghdr genlhdr; + int len, total_len, id, err, offset; + uint16_t netdev_index; + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (sock == -1) + exit(1); + int rtsock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (rtsock == -1) + exit(1); + id = netlink_devlink_id_get(&nlmsg, sock); + if (id == -1) + goto error; + memset(&genlhdr, 0, sizeof(genlhdr)); + genlhdr.cmd = DEVLINK_CMD_PORT_GET; + netlink_init(&nlmsg, id, NLM_F_DUMP, &genlhdr, sizeof(genlhdr)); + netlink_attr(&nlmsg, DEVLINK_ATTR_BUS_NAME, bus_name, strlen(bus_name) + 1); + netlink_attr(&nlmsg, DEVLINK_ATTR_DEV_NAME, dev_name, strlen(dev_name) + 1); + err = netlink_send_ext(&nlmsg, sock, id, &total_len); + if (err) { + goto error; + } + offset = 0; + netdev_index = 0; + while ((len = netlink_next_msg(&nlmsg, offset, total_len)) != -1) { + struct nlattr* attr = (struct nlattr*)(nlmsg.buf + offset + NLMSG_HDRLEN + + NLMSG_ALIGN(sizeof(genlhdr))); + for (; (char*)attr < nlmsg.buf + offset + len; + attr = (struct nlattr*)((char*)attr + NLMSG_ALIGN(attr->nla_len))) { + if (attr->nla_type == DEVLINK_ATTR_NETDEV_NAME) { + char* port_name; + char netdev_name[IFNAMSIZ]; + port_name = (char*)(attr + 1); + snprintf(netdev_name, sizeof(netdev_name), "%s%d", netdev_prefix, + netdev_index); + netlink_device_change(&nlmsg2, rtsock, port_name, true, 0, 0, 0, + netdev_name); + break; + } + } + offset += len; + netdev_index++; + } +error: + close(rtsock); + 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); + initialize_devlink_ports("pci", "0000:00:10.0", "netpci"); +} + +static void setup_common() +{ + if (mount(0, "/sys/fs/fuse/connections", "fusectl", 0, 0)) { + } +} + +static void loop(); + +static void sandbox_common() +{ + prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); + setpgrp(); + setsid(); + int netns = open("/proc/self/ns/net", O_RDONLY); + if (netns == -1) + exit(1); + if (dup2(netns, kInitNetNsFd) < 0) + exit(1); + close(netns); + struct rlimit rlim; + rlim.rlim_cur = rlim.rlim_max = (200 << 20); + setrlimit(RLIMIT_AS, &rlim); + rlim.rlim_cur = rlim.rlim_max = 32 << 20; + setrlimit(RLIMIT_MEMLOCK, &rlim); + rlim.rlim_cur = rlim.rlim_max = 136 << 20; + setrlimit(RLIMIT_FSIZE, &rlim); + rlim.rlim_cur = rlim.rlim_max = 1 << 20; + setrlimit(RLIMIT_STACK, &rlim); + rlim.rlim_cur = rlim.rlim_max = 0; + setrlimit(RLIMIT_CORE, &rlim); + rlim.rlim_cur = rlim.rlim_max = 256; + setrlimit(RLIMIT_NOFILE, &rlim); + if (unshare(CLONE_NEWNS)) { + } + if (unshare(CLONE_NEWIPC)) { + } + if (unshare(0x02000000)) { + } + if (unshare(CLONE_NEWUTS)) { + } + if (unshare(CLONE_SYSVSEM)) { + } + typedef struct { + const char* name; + const char* value; + } sysctl_t; + static const sysctl_t sysctls[] = { + {"/proc/sys/kernel/shmmax", "16777216"}, + {"/proc/sys/kernel/shmall", "536870912"}, + {"/proc/sys/kernel/shmmni", "1024"}, + {"/proc/sys/kernel/msgmax", "8192"}, + {"/proc/sys/kernel/msgmni", "1024"}, + {"/proc/sys/kernel/msgmnb", "1024"}, + {"/proc/sys/kernel/sem", "1024 1048576 500 1024"}, + }; + unsigned i; + for (i = 0; i < sizeof(sysctls) / sizeof(sysctls[0]); i++) + write_file(sysctls[i].name, sysctls[i].value); +} + +int wait_for_loop(int pid) +{ + if (pid < 0) + exit(1); + int status = 0; + while (waitpid(-1, &status, __WALL) != pid) { + } + return WEXITSTATUS(status); +} + +static void drop_caps(void) +{ + struct __user_cap_header_struct cap_hdr = {}; + struct __user_cap_data_struct cap_data[2] = {}; + cap_hdr.version = _LINUX_CAPABILITY_VERSION_3; + cap_hdr.pid = getpid(); + if (syscall(SYS_capget, &cap_hdr, &cap_data)) + exit(1); + const int drop = (1 << CAP_SYS_PTRACE) | (1 << CAP_SYS_NICE); + cap_data[0].effective &= ~drop; + cap_data[0].permitted &= ~drop; + cap_data[0].inheritable &= ~drop; + if (syscall(SYS_capset, &cap_hdr, &cap_data)) + exit(1); +} + +static int do_sandbox_none(void) +{ + if (unshare(CLONE_NEWPID)) { + } + int pid = fork(); + if (pid != 0) + return wait_for_loop(pid); + setup_common(); + sandbox_common(); + drop_caps(); + if (unshare(CLONE_NEWNET)) { + } + initialize_devlink_pci(); + loop(); + exit(1); +} + +#define KMEMLEAK_FILE "/sys/kernel/debug/kmemleak" + +static void setup_leak() +{ + if (!write_file(KMEMLEAK_FILE, "scan")) + exit(1); + sleep(5); + if (!write_file(KMEMLEAK_FILE, "scan")) + exit(1); + if (!write_file(KMEMLEAK_FILE, "clear")) + exit(1); +} + +static void check_leaks(void) +{ + int fd = open(KMEMLEAK_FILE, O_RDWR); + if (fd == -1) + exit(1); + uint64_t start = current_time_ms(); + if (write(fd, "scan", 4) != 4) + exit(1); + sleep(1); + while (current_time_ms() - start < 4 * 1000) + sleep(1); + if (write(fd, "scan", 4) != 4) + exit(1); + static char buf[128 << 10]; + ssize_t n = read(fd, buf, sizeof(buf) - 1); + if (n < 0) + exit(1); + int nleaks = 0; + if (n != 0) { + sleep(1); + if (write(fd, "scan", 4) != 4) + exit(1); + if (lseek(fd, 0, SEEK_SET) < 0) + exit(1); + n = read(fd, buf, sizeof(buf) - 1); + if (n < 0) + exit(1); + buf[n] = 0; + char* pos = buf; + char* end = buf + n; + while (pos < end) { + char* next = strstr(pos + 1, "unreferenced object"); + if (!next) + next = end; + char prev = *next; + *next = 0; + fprintf(stderr, "BUG: memory leak\n%s\n", pos); + *next = prev; + pos = next; + nleaks++; + } + } + if (write(fd, "clear", 5) != 5) + exit(1); + close(fd); + if (nleaks) + exit(1); +} + +#ifndef __NR_mmap +#define __NR_mmap 222 +#endif +#ifndef __NR_openat +#define __NR_openat 56 +#endif + +void loop(void) +{ + syscall(__NR_openat, 0xffffffffffffff9cul, 0ul, 0x4000ul, 0ul); +} +int main(void) +{ + syscall(__NR_mmap, 0x20000000ul, 0x1000000ul, 3ul, 0x32ul, -1, 0); + setup_leak(); + do_sandbox_none(); + check_leaks(); + return 0; +} diff --git a/syzkaller-repros/linux/e9813c22cf50f109dfe6ce3c4901795c6158a791.c b/syzkaller-repros/linux/e9813c22cf50f109dfe6ce3c4901795c6158a791.c new file mode 100644 index 0000000..8d7fbcb --- /dev/null +++ b/syzkaller-repros/linux/e9813c22cf50f109dfe6ce3c4901795c6158a791.c @@ -0,0 +1,536 @@ +// autogenerated by syzkaller (https://github.com/google/syzkaller) + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 void thread_start(void* (*fn)(void*), void* arg) +{ + pthread_t th; + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setstacksize(&attr, 128 << 10); + int i; + for (i = 0; i < 100; i++) { + if (pthread_create(&th, &attr, fn, arg) == 0) { + pthread_attr_destroy(&attr); + return; + } + if (errno == EAGAIN) { + usleep(50); + continue; + } + break; + } + exit(1); +} + +typedef struct { + int state; +} event_t; + +static void event_init(event_t* ev) +{ + ev->state = 0; +} + +static void event_reset(event_t* ev) +{ + ev->state = 0; +} + +static void event_set(event_t* ev) +{ + if (ev->state) + exit(1); + __atomic_store_n(&ev->state, 1, __ATOMIC_RELEASE); + syscall(SYS_futex, &ev->state, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1000000); +} + +static void event_wait(event_t* ev) +{ + while (!__atomic_load_n(&ev->state, __ATOMIC_ACQUIRE)) + syscall(SYS_futex, &ev->state, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, 0, 0); +} + +static int event_isset(event_t* ev) +{ + return __atomic_load_n(&ev->state, __ATOMIC_ACQUIRE); +} + +static int event_timedwait(event_t* ev, uint64_t timeout) +{ + uint64_t start = current_time_ms(); + uint64_t now = start; + for (;;) { + uint64_t remain = timeout - (now - start); + struct timespec ts; + ts.tv_sec = remain / 1000; + ts.tv_nsec = (remain % 1000) * 1000 * 1000; + syscall(SYS_futex, &ev->state, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, 0, &ts); + if (__atomic_load_n(&ev->state, __ATOMIC_RELAXED)) + return 1; + now = current_time_ms(); + if (now - start > timeout) + return 0; + } +} + +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; +} + +struct nlmsg { + char* pos; + int nesting; + struct nlattr* nested[8]; + char buf[1024]; +}; + +static struct nlmsg nlmsg; + +static void netlink_init(struct nlmsg* nlmsg, 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(struct nlmsg* nlmsg, 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(struct nlmsg* nlmsg, 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 (hdr->nlmsg_type == NLMSG_DONE) { + *reply_len = 0; + return 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(struct nlmsg* nlmsg, int sock) +{ + return netlink_send_ext(nlmsg, sock, 0, NULL); +} + +static int netlink_next_msg(struct nlmsg* nlmsg, unsigned int offset, + unsigned int total_len) +{ + struct nlmsghdr* hdr = (struct nlmsghdr*)(nlmsg->buf + offset); + if (offset == total_len || offset + hdr->nlmsg_len > total_len) + return -1; + return hdr->nlmsg_len; +} + +static void netlink_device_change(struct nlmsg* nlmsg, int sock, + const char* name, bool up, const char* master, + const void* mac, int macsize, + const char* new_name) +{ + struct ifinfomsg hdr; + memset(&hdr, 0, sizeof(hdr)); + if (up) + hdr.ifi_flags = hdr.ifi_change = IFF_UP; + hdr.ifi_index = if_nametoindex(name); + netlink_init(nlmsg, RTM_NEWLINK, 0, &hdr, sizeof(hdr)); + if (new_name) + netlink_attr(nlmsg, IFLA_IFNAME, new_name, strlen(new_name)); + if (master) { + int ifindex = if_nametoindex(master); + netlink_attr(nlmsg, IFLA_MASTER, &ifindex, sizeof(ifindex)); + } + if (macsize) + netlink_attr(nlmsg, IFLA_ADDRESS, mac, macsize); + int err = netlink_send(nlmsg, sock); + (void)err; +} + +const int kInitNetNsFd = 239; + +#define DEVLINK_FAMILY_NAME "devlink" + +#define DEVLINK_CMD_PORT_GET 5 +#define DEVLINK_CMD_RELOAD 37 +#define DEVLINK_ATTR_BUS_NAME 1 +#define DEVLINK_ATTR_DEV_NAME 2 +#define DEVLINK_ATTR_NETDEV_NAME 7 +#define DEVLINK_ATTR_NETNS_FD 138 + +static int netlink_devlink_id_get(struct nlmsg* nlmsg, int sock) +{ + struct genlmsghdr genlhdr; + struct nlattr* attr; + int err, n; + uint16_t id = 0; + memset(&genlhdr, 0, sizeof(genlhdr)); + genlhdr.cmd = CTRL_CMD_GETFAMILY; + netlink_init(nlmsg, GENL_ID_CTRL, 0, &genlhdr, sizeof(genlhdr)); + netlink_attr(nlmsg, CTRL_ATTR_FAMILY_NAME, DEVLINK_FAMILY_NAME, + strlen(DEVLINK_FAMILY_NAME) + 1); + err = netlink_send_ext(nlmsg, sock, GENL_ID_CTRL, &n); + if (err) { + return -1; + } + 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) { + return -1; + } + recv(sock, nlmsg->buf, sizeof(nlmsg->buf), 0); /* recv ack */ + return id; +} + +static void netlink_devlink_netns_move(const char* bus_name, + const char* dev_name, int netns_fd) +{ + struct genlmsghdr genlhdr; + int sock; + int id, err; + sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (sock == -1) + exit(1); + id = netlink_devlink_id_get(&nlmsg, sock); + if (id == -1) + goto error; + memset(&genlhdr, 0, sizeof(genlhdr)); + genlhdr.cmd = DEVLINK_CMD_RELOAD; + netlink_init(&nlmsg, id, 0, &genlhdr, sizeof(genlhdr)); + netlink_attr(&nlmsg, DEVLINK_ATTR_BUS_NAME, bus_name, strlen(bus_name) + 1); + netlink_attr(&nlmsg, DEVLINK_ATTR_DEV_NAME, dev_name, strlen(dev_name) + 1); + netlink_attr(&nlmsg, DEVLINK_ATTR_NETNS_FD, &netns_fd, sizeof(netns_fd)); + err = netlink_send(&nlmsg, sock); + if (err) { + } +error: + close(sock); +} + +static struct nlmsg nlmsg2; + +static void initialize_devlink_ports(const char* bus_name, const char* dev_name, + const char* netdev_prefix) +{ + struct genlmsghdr genlhdr; + int len, total_len, id, err, offset; + uint16_t netdev_index; + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (sock == -1) + exit(1); + int rtsock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (rtsock == -1) + exit(1); + id = netlink_devlink_id_get(&nlmsg, sock); + if (id == -1) + goto error; + memset(&genlhdr, 0, sizeof(genlhdr)); + genlhdr.cmd = DEVLINK_CMD_PORT_GET; + netlink_init(&nlmsg, id, NLM_F_DUMP, &genlhdr, sizeof(genlhdr)); + netlink_attr(&nlmsg, DEVLINK_ATTR_BUS_NAME, bus_name, strlen(bus_name) + 1); + netlink_attr(&nlmsg, DEVLINK_ATTR_DEV_NAME, dev_name, strlen(dev_name) + 1); + err = netlink_send_ext(&nlmsg, sock, id, &total_len); + if (err) { + goto error; + } + offset = 0; + netdev_index = 0; + while ((len = netlink_next_msg(&nlmsg, offset, total_len)) != -1) { + struct nlattr* attr = (struct nlattr*)(nlmsg.buf + offset + NLMSG_HDRLEN + + NLMSG_ALIGN(sizeof(genlhdr))); + for (; (char*)attr < nlmsg.buf + offset + len; + attr = (struct nlattr*)((char*)attr + NLMSG_ALIGN(attr->nla_len))) { + if (attr->nla_type == DEVLINK_ATTR_NETDEV_NAME) { + char* port_name; + char netdev_name[IFNAMSIZ]; + port_name = (char*)(attr + 1); + snprintf(netdev_name, sizeof(netdev_name), "%s%d", netdev_prefix, + netdev_index); + netlink_device_change(&nlmsg2, rtsock, port_name, true, 0, 0, 0, + netdev_name); + break; + } + } + offset += len; + netdev_index++; + } +error: + close(rtsock); + 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); + initialize_devlink_ports("pci", "0000:00:10.0", "netpci"); +} + +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"); +} + +struct thread_t { + int created, call; + event_t ready, done; +}; + +static struct thread_t threads[16]; +static void execute_call(int call); +static int running; + +static void* thr(void* arg) +{ + struct thread_t* th = (struct thread_t*)arg; + for (;;) { + event_wait(&th->ready); + event_reset(&th->ready); + execute_call(th->call); + __atomic_fetch_sub(&running, 1, __ATOMIC_RELAXED); + event_set(&th->done); + } + return 0; +} + +static void execute_one(void) +{ + int i, call, thread; + for (call = 0; call < 2; call++) { + for (thread = 0; thread < (int)(sizeof(threads) / sizeof(threads[0])); + thread++) { + struct thread_t* th = &threads[thread]; + if (!th->created) { + th->created = 1; + event_init(&th->ready); + event_init(&th->done); + event_set(&th->done); + thread_start(thr, th); + } + if (!event_isset(&th->done)) + continue; + event_reset(&th->done); + th->call = call; + __atomic_fetch_add(&running, 1, __ATOMIC_RELAXED); + event_set(&th->ready); + event_timedwait(&th->done, 45); + break; + } + } + for (i = 0; i < 100 && __atomic_load_n(&running, __ATOMIC_RELAXED); i++) + sleep_ms(1); +} + +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; + } + } +} + +#ifndef __NR_ioctl +#define __NR_ioctl 29 +#endif +#ifndef __NR_mmap +#define __NR_mmap 222 +#endif +#ifndef __NR_openat +#define __NR_openat 56 +#endif + +uint64_t r[1] = {0xffffffffffffffff}; + +void execute_call(int call) +{ + intptr_t res; + switch (call) { + case 0: + memcpy((void*)0x20000100, "/dev/ttyS3\000", 11); + res = syscall(__NR_openat, 0xffffffffffffff9cul, 0x20000100ul, 0ul, 0ul); + if (res != -1) + r[0] = res; + break; + case 1: + syscall(__NR_ioctl, r[0], 0x5437ul, 0ul); + break; + } +} +int main(void) +{ + syscall(__NR_mmap, 0x20000000ul, 0x1000000ul, 3ul, 0x32ul, -1, 0); + loop(); + return 0; +} -- cgit 1.2.3-korg