-
-
Save freexxxyyy/33a53d3af6d0808b84224debb1e8ec1a to your computer and use it in GitHub Desktop.
WARNING in rcu_core
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// autogenerated by syzkaller (https://github.com/google/syzkaller) | |
#define _GNU_SOURCE | |
#include <arpa/inet.h> | |
#include <dirent.h> | |
#include <endian.h> | |
#include <errno.h> | |
#include <fcntl.h> | |
#include <net/if.h> | |
#include <net/if_arp.h> | |
#include <netinet/in.h> | |
#include <pthread.h> | |
#include <sched.h> | |
#include <setjmp.h> | |
#include <signal.h> | |
#include <stdarg.h> | |
#include <stdbool.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/ioctl.h> | |
#include <sys/mount.h> | |
#include <sys/prctl.h> | |
#include <sys/resource.h> | |
#include <sys/socket.h> | |
#include <sys/stat.h> | |
#include <sys/syscall.h> | |
#include <sys/time.h> | |
#include <sys/types.h> | |
#include <sys/uio.h> | |
#include <sys/wait.h> | |
#include <time.h> | |
#include <unistd.h> | |
#include <linux/capability.h> | |
#include <linux/futex.h> | |
#include <linux/genetlink.h> | |
#include <linux/if_addr.h> | |
#include <linux/if_ether.h> | |
#include <linux/if_link.h> | |
#include <linux/if_tun.h> | |
#include <linux/in6.h> | |
#include <linux/ip.h> | |
#include <linux/neighbour.h> | |
#include <linux/net.h> | |
#include <linux/netlink.h> | |
#include <linux/rtnetlink.h> | |
#include <linux/tcp.h> | |
#include <linux/veth.h> | |
static unsigned long long procid; | |
static __thread int clone_ongoing; | |
static __thread int skip_segv; | |
static __thread jmp_buf segv_env; | |
static void segv_handler(int sig, siginfo_t* info, void* ctx) | |
{ | |
if (__atomic_load_n(&clone_ongoing, __ATOMIC_RELAXED) != 0) { | |
exit(sig); | |
} | |
uintptr_t addr = (uintptr_t)info->si_addr; | |
const uintptr_t prog_start = 1 << 20; | |
const uintptr_t prog_end = 100 << 20; | |
int skip = __atomic_load_n(&skip_segv, __ATOMIC_RELAXED) != 0; | |
int valid = addr < prog_start || addr > prog_end; | |
if (skip && valid) { | |
_longjmp(segv_env, 1); | |
} | |
exit(sig); | |
} | |
static void install_segv_handler(void) | |
{ | |
struct sigaction sa; | |
memset(&sa, 0, sizeof(sa)); | |
sa.sa_handler = SIG_IGN; | |
syscall(SYS_rt_sigaction, 0x20, &sa, NULL, 8); | |
syscall(SYS_rt_sigaction, 0x21, &sa, NULL, 8); | |
memset(&sa, 0, sizeof(sa)); | |
sa.sa_sigaction = segv_handler; | |
sa.sa_flags = SA_NODEFER | SA_SIGINFO; | |
sigaction(SIGSEGV, &sa, NULL); | |
sigaction(SIGBUS, &sa, NULL); | |
} | |
#define NONFAILING(...) \ | |
({ \ | |
int ok = 1; \ | |
__atomic_fetch_add(&skip_segv, 1, __ATOMIC_SEQ_CST); \ | |
if (_setjmp(segv_env) == 0) { \ | |
__VA_ARGS__; \ | |
} else \ | |
ok = 0; \ | |
__atomic_fetch_sub(&skip_segv, 1, __ATOMIC_SEQ_CST); \ | |
ok; \ | |
}) | |
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 = 0; | |
for (; 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_ACQUIRE)) | |
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[4096]; | |
}; | |
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; | |
if (size > 0) | |
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, bool dofail) | |
{ | |
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; | |
ssize_t n = sendto(sock, nlmsg->buf, hdr->nlmsg_len, 0, | |
(struct sockaddr*)&addr, sizeof(addr)); | |
if (n != (ssize_t)hdr->nlmsg_len) { | |
if (dofail) | |
exit(1); | |
return -1; | |
} | |
n = recv(sock, nlmsg->buf, sizeof(nlmsg->buf), 0); | |
if (reply_len) | |
*reply_len = 0; | |
if (n < 0) { | |
if (dofail) | |
exit(1); | |
return -1; | |
} | |
if (n < (ssize_t)sizeof(struct nlmsghdr)) { | |
errno = EINVAL; | |
if (dofail) | |
exit(1); | |
return -1; | |
} | |
if (hdr->nlmsg_type == NLMSG_DONE) | |
return 0; | |
if (reply_len && hdr->nlmsg_type == reply_type) { | |
*reply_len = n; | |
return 0; | |
} | |
if (n < (ssize_t)(sizeof(struct nlmsghdr) + sizeof(struct nlmsgerr))) { | |
errno = EINVAL; | |
if (dofail) | |
exit(1); | |
return -1; | |
} | |
if (hdr->nlmsg_type != NLMSG_ERROR) { | |
errno = EINVAL; | |
if (dofail) | |
exit(1); | |
return -1; | |
} | |
errno = -((struct nlmsgerr*)(hdr + 1))->error; | |
return -errno; | |
} | |
static int netlink_send(struct nlmsg* nlmsg, int sock) | |
{ | |
return netlink_send_ext(nlmsg, sock, 0, NULL, true); | |
} | |
static int netlink_query_family_id(struct nlmsg* nlmsg, int sock, | |
const char* family_name, bool dofail) | |
{ | |
struct genlmsghdr genlhdr; | |
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, family_name, | |
strnlen(family_name, GENL_NAMSIZ - 1) + 1); | |
int n = 0; | |
int err = netlink_send_ext(nlmsg, sock, GENL_ID_CTRL, &n, dofail); | |
if (err < 0) { | |
return -1; | |
} | |
uint16_t id = 0; | |
struct nlattr* 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) { | |
errno = EINVAL; | |
return -1; | |
} | |
recv(sock, nlmsg->buf, sizeof(nlmsg->buf), 0); | |
return id; | |
} | |
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, bool up) | |
{ | |
struct ifinfomsg hdr; | |
memset(&hdr, 0, sizeof(hdr)); | |
if (up) | |
hdr.ifi_flags = hdr.ifi_change = IFF_UP; | |
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, false); | |
netlink_done(nlmsg); | |
int err = netlink_send(nlmsg, sock); | |
if (err < 0) { | |
} | |
} | |
static void netlink_add_veth(struct nlmsg* nlmsg, int sock, const char* name, | |
const char* peer) | |
{ | |
netlink_add_device_impl(nlmsg, "veth", name, false); | |
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); | |
if (err < 0) { | |
} | |
} | |
static void netlink_add_xfrm(struct nlmsg* nlmsg, int sock, const char* name) | |
{ | |
netlink_add_device_impl(nlmsg, "xfrm", name, true); | |
netlink_nest(nlmsg, IFLA_INFO_DATA); | |
int if_id = 1; | |
netlink_attr(nlmsg, 2, &if_id, sizeof(if_id)); | |
netlink_done(nlmsg); | |
netlink_done(nlmsg); | |
int err = netlink_send(nlmsg, sock); | |
if (err < 0) { | |
} | |
} | |
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, false); | |
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); | |
if (err < 0) { | |
} | |
} | |
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, false); | |
netlink_done(nlmsg); | |
int ifindex = if_nametoindex(link); | |
netlink_attr(nlmsg, IFLA_LINK, &ifindex, sizeof(ifindex)); | |
int err = netlink_send(nlmsg, sock); | |
if (err < 0) { | |
} | |
} | |
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, false); | |
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); | |
if (err < 0) { | |
} | |
} | |
static void netlink_add_macvlan(struct nlmsg* nlmsg, int sock, const char* name, | |
const char* link) | |
{ | |
netlink_add_device_impl(nlmsg, "macvlan", name, false); | |
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); | |
if (err < 0) { | |
} | |
} | |
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, false); | |
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); | |
if (err < 0) { | |
} | |
} | |
#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, false); | |
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); | |
if (err < 0) { | |
} | |
} | |
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); | |
if (err < 0) { | |
} | |
} | |
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)); | |
if (err < 0) { | |
} | |
} | |
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)); | |
if (err < 0) { | |
} | |
} | |
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); | |
if (err < 0) { | |
} | |
} | |
static struct nlmsg nlmsg; | |
static int tunfd = -1; | |
#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 | |
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 = 200; | |
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; | |
if (ioctl(tunfd, TUNSETIFF, (void*)&ifr) < 0) { | |
exit(1); | |
} | |
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); | |
} | |
#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 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_query_family_id(&nlmsg, sock, DEVLINK_FAMILY_NAME, true); | |
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, true); | |
if (err < 0) { | |
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) | |
{ | |
write_file("/sys/bus/netdevsim/del_device", "%u", addr); | |
if (write_file("/sys/bus/netdevsim/new_device", "%u %u", addr, port_count)) { | |
char buf[32]; | |
snprintf(buf, sizeof(buf), "netdevsim%d", addr); | |
initialize_devlink_ports("netdevsim", buf, "netdevsim"); | |
} | |
} | |
#define WG_GENL_NAME "wireguard" | |
enum wg_cmd { | |
WG_CMD_GET_DEVICE, | |
WG_CMD_SET_DEVICE, | |
}; | |
enum wgdevice_attribute { | |
WGDEVICE_A_UNSPEC, | |
WGDEVICE_A_IFINDEX, | |
WGDEVICE_A_IFNAME, | |
WGDEVICE_A_PRIVATE_KEY, | |
WGDEVICE_A_PUBLIC_KEY, | |
WGDEVICE_A_FLAGS, | |
WGDEVICE_A_LISTEN_PORT, | |
WGDEVICE_A_FWMARK, | |
WGDEVICE_A_PEERS, | |
}; | |
enum wgpeer_attribute { | |
WGPEER_A_UNSPEC, | |
WGPEER_A_PUBLIC_KEY, | |
WGPEER_A_PRESHARED_KEY, | |
WGPEER_A_FLAGS, | |
WGPEER_A_ENDPOINT, | |
WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, | |
WGPEER_A_LAST_HANDSHAKE_TIME, | |
WGPEER_A_RX_BYTES, | |
WGPEER_A_TX_BYTES, | |
WGPEER_A_ALLOWEDIPS, | |
WGPEER_A_PROTOCOL_VERSION, | |
}; | |
enum wgallowedip_attribute { | |
WGALLOWEDIP_A_UNSPEC, | |
WGALLOWEDIP_A_FAMILY, | |
WGALLOWEDIP_A_IPADDR, | |
WGALLOWEDIP_A_CIDR_MASK, | |
}; | |
static void netlink_wireguard_setup(void) | |
{ | |
const char ifname_a[] = "wg0"; | |
const char ifname_b[] = "wg1"; | |
const char ifname_c[] = "wg2"; | |
const char private_a[] = | |
"\xa0\x5c\xa8\x4f\x6c\x9c\x8e\x38\x53\xe2\xfd\x7a\x70\xae\x0f\xb2\x0f\xa1" | |
"\x52\x60\x0c\xb0\x08\x45\x17\x4f\x08\x07\x6f\x8d\x78\x43"; | |
const char private_b[] = | |
"\xb0\x80\x73\xe8\xd4\x4e\x91\xe3\xda\x92\x2c\x22\x43\x82\x44\xbb\x88\x5c" | |
"\x69\xe2\x69\xc8\xe9\xd8\x35\xb1\x14\x29\x3a\x4d\xdc\x6e"; | |
const char private_c[] = | |
"\xa0\xcb\x87\x9a\x47\xf5\xbc\x64\x4c\x0e\x69\x3f\xa6\xd0\x31\xc7\x4a\x15" | |
"\x53\xb6\xe9\x01\xb9\xff\x2f\x51\x8c\x78\x04\x2f\xb5\x42"; | |
const char public_a[] = | |
"\x97\x5c\x9d\x81\xc9\x83\xc8\x20\x9e\xe7\x81\x25\x4b\x89\x9f\x8e\xd9\x25" | |
"\xae\x9f\x09\x23\xc2\x3c\x62\xf5\x3c\x57\xcd\xbf\x69\x1c"; | |
const char public_b[] = | |
"\xd1\x73\x28\x99\xf6\x11\xcd\x89\x94\x03\x4d\x7f\x41\x3d\xc9\x57\x63\x0e" | |
"\x54\x93\xc2\x85\xac\xa4\x00\x65\xcb\x63\x11\xbe\x69\x6b"; | |
const char public_c[] = | |
"\xf4\x4d\xa3\x67\xa8\x8e\xe6\x56\x4f\x02\x02\x11\x45\x67\x27\x08\x2f\x5c" | |
"\xeb\xee\x8b\x1b\xf5\xeb\x73\x37\x34\x1b\x45\x9b\x39\x22"; | |
const uint16_t listen_a = 20001; | |
const uint16_t listen_b = 20002; | |
const uint16_t listen_c = 20003; | |
const uint16_t af_inet = AF_INET; | |
const uint16_t af_inet6 = AF_INET6; | |
const struct sockaddr_in endpoint_b_v4 = { | |
.sin_family = AF_INET, | |
.sin_port = htons(listen_b), | |
.sin_addr = {htonl(INADDR_LOOPBACK)}}; | |
const struct sockaddr_in endpoint_c_v4 = { | |
.sin_family = AF_INET, | |
.sin_port = htons(listen_c), | |
.sin_addr = {htonl(INADDR_LOOPBACK)}}; | |
struct sockaddr_in6 endpoint_a_v6 = {.sin6_family = AF_INET6, | |
.sin6_port = htons(listen_a)}; | |
endpoint_a_v6.sin6_addr = in6addr_loopback; | |
struct sockaddr_in6 endpoint_c_v6 = {.sin6_family = AF_INET6, | |
.sin6_port = htons(listen_c)}; | |
endpoint_c_v6.sin6_addr = in6addr_loopback; | |
const struct in_addr first_half_v4 = {0}; | |
const struct in_addr second_half_v4 = {(uint32_t)htonl(128 << 24)}; | |
const struct in6_addr first_half_v6 = {{{0}}}; | |
const struct in6_addr second_half_v6 = {{{0x80}}}; | |
const uint8_t half_cidr = 1; | |
const uint16_t persistent_keepalives[] = {1, 3, 7, 9, 14, 19}; | |
struct genlmsghdr genlhdr = {.cmd = WG_CMD_SET_DEVICE, .version = 1}; | |
int sock; | |
int id, err; | |
sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); | |
if (sock == -1) { | |
return; | |
} | |
id = netlink_query_family_id(&nlmsg, sock, WG_GENL_NAME, true); | |
if (id == -1) | |
goto error; | |
netlink_init(&nlmsg, id, 0, &genlhdr, sizeof(genlhdr)); | |
netlink_attr(&nlmsg, WGDEVICE_A_IFNAME, ifname_a, strlen(ifname_a) + 1); | |
netlink_attr(&nlmsg, WGDEVICE_A_PRIVATE_KEY, private_a, 32); | |
netlink_attr(&nlmsg, WGDEVICE_A_LISTEN_PORT, &listen_a, 2); | |
netlink_nest(&nlmsg, NLA_F_NESTED | WGDEVICE_A_PEERS); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGPEER_A_PUBLIC_KEY, public_b, 32); | |
netlink_attr(&nlmsg, WGPEER_A_ENDPOINT, &endpoint_b_v4, | |
sizeof(endpoint_b_v4)); | |
netlink_attr(&nlmsg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, | |
&persistent_keepalives[0], 2); | |
netlink_nest(&nlmsg, NLA_F_NESTED | WGPEER_A_ALLOWEDIPS); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet, 2); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &first_half_v4, | |
sizeof(first_half_v4)); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1); | |
netlink_done(&nlmsg); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet6, 2); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &first_half_v6, | |
sizeof(first_half_v6)); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGPEER_A_PUBLIC_KEY, public_c, 32); | |
netlink_attr(&nlmsg, WGPEER_A_ENDPOINT, &endpoint_c_v6, | |
sizeof(endpoint_c_v6)); | |
netlink_attr(&nlmsg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, | |
&persistent_keepalives[1], 2); | |
netlink_nest(&nlmsg, NLA_F_NESTED | WGPEER_A_ALLOWEDIPS); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet, 2); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &second_half_v4, | |
sizeof(second_half_v4)); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1); | |
netlink_done(&nlmsg); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet6, 2); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &second_half_v6, | |
sizeof(second_half_v6)); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
err = netlink_send(&nlmsg, sock); | |
if (err < 0) { | |
} | |
netlink_init(&nlmsg, id, 0, &genlhdr, sizeof(genlhdr)); | |
netlink_attr(&nlmsg, WGDEVICE_A_IFNAME, ifname_b, strlen(ifname_b) + 1); | |
netlink_attr(&nlmsg, WGDEVICE_A_PRIVATE_KEY, private_b, 32); | |
netlink_attr(&nlmsg, WGDEVICE_A_LISTEN_PORT, &listen_b, 2); | |
netlink_nest(&nlmsg, NLA_F_NESTED | WGDEVICE_A_PEERS); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGPEER_A_PUBLIC_KEY, public_a, 32); | |
netlink_attr(&nlmsg, WGPEER_A_ENDPOINT, &endpoint_a_v6, | |
sizeof(endpoint_a_v6)); | |
netlink_attr(&nlmsg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, | |
&persistent_keepalives[2], 2); | |
netlink_nest(&nlmsg, NLA_F_NESTED | WGPEER_A_ALLOWEDIPS); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet, 2); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &first_half_v4, | |
sizeof(first_half_v4)); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1); | |
netlink_done(&nlmsg); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet6, 2); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &first_half_v6, | |
sizeof(first_half_v6)); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGPEER_A_PUBLIC_KEY, public_c, 32); | |
netlink_attr(&nlmsg, WGPEER_A_ENDPOINT, &endpoint_c_v4, | |
sizeof(endpoint_c_v4)); | |
netlink_attr(&nlmsg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, | |
&persistent_keepalives[3], 2); | |
netlink_nest(&nlmsg, NLA_F_NESTED | WGPEER_A_ALLOWEDIPS); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet, 2); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &second_half_v4, | |
sizeof(second_half_v4)); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1); | |
netlink_done(&nlmsg); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet6, 2); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &second_half_v6, | |
sizeof(second_half_v6)); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
err = netlink_send(&nlmsg, sock); | |
if (err < 0) { | |
} | |
netlink_init(&nlmsg, id, 0, &genlhdr, sizeof(genlhdr)); | |
netlink_attr(&nlmsg, WGDEVICE_A_IFNAME, ifname_c, strlen(ifname_c) + 1); | |
netlink_attr(&nlmsg, WGDEVICE_A_PRIVATE_KEY, private_c, 32); | |
netlink_attr(&nlmsg, WGDEVICE_A_LISTEN_PORT, &listen_c, 2); | |
netlink_nest(&nlmsg, NLA_F_NESTED | WGDEVICE_A_PEERS); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGPEER_A_PUBLIC_KEY, public_a, 32); | |
netlink_attr(&nlmsg, WGPEER_A_ENDPOINT, &endpoint_a_v6, | |
sizeof(endpoint_a_v6)); | |
netlink_attr(&nlmsg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, | |
&persistent_keepalives[4], 2); | |
netlink_nest(&nlmsg, NLA_F_NESTED | WGPEER_A_ALLOWEDIPS); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet, 2); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &first_half_v4, | |
sizeof(first_half_v4)); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1); | |
netlink_done(&nlmsg); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet6, 2); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &first_half_v6, | |
sizeof(first_half_v6)); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGPEER_A_PUBLIC_KEY, public_b, 32); | |
netlink_attr(&nlmsg, WGPEER_A_ENDPOINT, &endpoint_b_v4, | |
sizeof(endpoint_b_v4)); | |
netlink_attr(&nlmsg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, | |
&persistent_keepalives[5], 2); | |
netlink_nest(&nlmsg, NLA_F_NESTED | WGPEER_A_ALLOWEDIPS); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet, 2); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &second_half_v4, | |
sizeof(second_half_v4)); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1); | |
netlink_done(&nlmsg); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet6, 2); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &second_half_v6, | |
sizeof(second_half_v6)); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
err = netlink_send(&nlmsg, sock); | |
if (err < 0) { | |
} | |
error: | |
close(sock); | |
} | |
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"}, {"veth", 0}, {"wireguard", "wg0"}, | |
{"wireguard", "wg1"}, {"wireguard", "wg2"}, | |
}; | |
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}, | |
{"wg0", 0}, | |
{"wg1", 0}, | |
{"wg2", 0}, | |
}; | |
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_add_xfrm(&nlmsg, sock, "xfrm0"); | |
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); | |
netlink_wireguard_setup(); | |
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 || errno == EBADF || 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 long syz_open_dev(volatile long a0, volatile long a1, volatile long a2) | |
{ | |
if (a0 == 0xc || a0 == 0xb) { | |
char buf[128]; | |
sprintf(buf, "/dev/%s/%d:%d", a0 == 0xc ? "char" : "block", (uint8_t)a1, | |
(uint8_t)a2); | |
return open(buf, O_RDWR, 0); | |
} else { | |
char buf[1024]; | |
char* hash; | |
strncpy(buf, (char*)a0, sizeof(buf) - 1); | |
buf[sizeof(buf) - 1] = 0; | |
while ((hash = strchr(buf, '#'))) { | |
*hash = '0' + (char)(a1 % 10); | |
a1 /= 10; | |
} | |
return open(buf, a2, 0); | |
} | |
} | |
#define XT_TABLE_SIZE 1536 | |
#define XT_MAX_ENTRIES 10 | |
struct xt_counters { | |
uint64_t pcnt, bcnt; | |
}; | |
struct ipt_getinfo { | |
char name[32]; | |
unsigned int valid_hooks; | |
unsigned int hook_entry[5]; | |
unsigned int underflow[5]; | |
unsigned int num_entries; | |
unsigned int size; | |
}; | |
struct ipt_get_entries { | |
char name[32]; | |
unsigned int size; | |
uint64_t entrytable[XT_TABLE_SIZE / sizeof(uint64_t)]; | |
}; | |
struct ipt_replace { | |
char name[32]; | |
unsigned int valid_hooks; | |
unsigned int num_entries; | |
unsigned int size; | |
unsigned int hook_entry[5]; | |
unsigned int underflow[5]; | |
unsigned int num_counters; | |
struct xt_counters* counters; | |
uint64_t entrytable[XT_TABLE_SIZE / sizeof(uint64_t)]; | |
}; | |
struct ipt_table_desc { | |
const char* name; | |
struct ipt_getinfo info; | |
struct ipt_replace replace; | |
}; | |
static struct ipt_table_desc ipv4_tables[] = { | |
{.name = "filter"}, {.name = "nat"}, {.name = "mangle"}, | |
{.name = "raw"}, {.name = "security"}, | |
}; | |
static struct ipt_table_desc ipv6_tables[] = { | |
{.name = "filter"}, {.name = "nat"}, {.name = "mangle"}, | |
{.name = "raw"}, {.name = "security"}, | |
}; | |
#define IPT_BASE_CTL 64 | |
#define IPT_SO_SET_REPLACE (IPT_BASE_CTL) | |
#define IPT_SO_GET_INFO (IPT_BASE_CTL) | |
#define IPT_SO_GET_ENTRIES (IPT_BASE_CTL + 1) | |
struct arpt_getinfo { | |
char name[32]; | |
unsigned int valid_hooks; | |
unsigned int hook_entry[3]; | |
unsigned int underflow[3]; | |
unsigned int num_entries; | |
unsigned int size; | |
}; | |
struct arpt_get_entries { | |
char name[32]; | |
unsigned int size; | |
uint64_t entrytable[XT_TABLE_SIZE / sizeof(uint64_t)]; | |
}; | |
struct arpt_replace { | |
char name[32]; | |
unsigned int valid_hooks; | |
unsigned int num_entries; | |
unsigned int size; | |
unsigned int hook_entry[3]; | |
unsigned int underflow[3]; | |
unsigned int num_counters; | |
struct xt_counters* counters; | |
uint64_t entrytable[XT_TABLE_SIZE / sizeof(uint64_t)]; | |
}; | |
struct arpt_table_desc { | |
const char* name; | |
struct arpt_getinfo info; | |
struct arpt_replace replace; | |
}; | |
static struct arpt_table_desc arpt_tables[] = { | |
{.name = "filter"}, | |
}; | |
#define ARPT_BASE_CTL 96 | |
#define ARPT_SO_SET_REPLACE (ARPT_BASE_CTL) | |
#define ARPT_SO_GET_INFO (ARPT_BASE_CTL) | |
#define ARPT_SO_GET_ENTRIES (ARPT_BASE_CTL + 1) | |
static void checkpoint_iptables(struct ipt_table_desc* tables, int num_tables, | |
int family, int level) | |
{ | |
int fd = socket(family, SOCK_STREAM, IPPROTO_TCP); | |
if (fd == -1) { | |
switch (errno) { | |
case EAFNOSUPPORT: | |
case ENOPROTOOPT: | |
case ENOENT: | |
return; | |
} | |
exit(1); | |
} | |
for (int i = 0; i < num_tables; i++) { | |
struct ipt_table_desc* table = &tables[i]; | |
strcpy(table->info.name, table->name); | |
strcpy(table->replace.name, table->name); | |
socklen_t optlen = sizeof(table->info); | |
if (getsockopt(fd, level, IPT_SO_GET_INFO, &table->info, &optlen)) { | |
switch (errno) { | |
case EPERM: | |
case ENOENT: | |
case ENOPROTOOPT: | |
continue; | |
} | |
exit(1); | |
} | |
if (table->info.size > sizeof(table->replace.entrytable)) | |
exit(1); | |
if (table->info.num_entries > XT_MAX_ENTRIES) | |
exit(1); | |
struct ipt_get_entries entries; | |
memset(&entries, 0, sizeof(entries)); | |
strcpy(entries.name, table->name); | |
entries.size = table->info.size; | |
optlen = sizeof(entries) - sizeof(entries.entrytable) + table->info.size; | |
if (getsockopt(fd, level, IPT_SO_GET_ENTRIES, &entries, &optlen)) | |
exit(1); | |
table->replace.valid_hooks = table->info.valid_hooks; | |
table->replace.num_entries = table->info.num_entries; | |
table->replace.size = table->info.size; | |
memcpy(table->replace.hook_entry, table->info.hook_entry, | |
sizeof(table->replace.hook_entry)); | |
memcpy(table->replace.underflow, table->info.underflow, | |
sizeof(table->replace.underflow)); | |
memcpy(table->replace.entrytable, entries.entrytable, table->info.size); | |
} | |
close(fd); | |
} | |
static void reset_iptables(struct ipt_table_desc* tables, int num_tables, | |
int family, int level) | |
{ | |
int fd = socket(family, SOCK_STREAM, IPPROTO_TCP); | |
if (fd == -1) { | |
switch (errno) { | |
case EAFNOSUPPORT: | |
case ENOPROTOOPT: | |
case ENOENT: | |
return; | |
} | |
exit(1); | |
} | |
for (int i = 0; i < num_tables; i++) { | |
struct ipt_table_desc* table = &tables[i]; | |
if (table->info.valid_hooks == 0) | |
continue; | |
struct ipt_getinfo info; | |
memset(&info, 0, sizeof(info)); | |
strcpy(info.name, table->name); | |
socklen_t optlen = sizeof(info); | |
if (getsockopt(fd, level, IPT_SO_GET_INFO, &info, &optlen)) | |
exit(1); | |
if (memcmp(&table->info, &info, sizeof(table->info)) == 0) { | |
struct ipt_get_entries entries; | |
memset(&entries, 0, sizeof(entries)); | |
strcpy(entries.name, table->name); | |
entries.size = table->info.size; | |
optlen = sizeof(entries) - sizeof(entries.entrytable) + entries.size; | |
if (getsockopt(fd, level, IPT_SO_GET_ENTRIES, &entries, &optlen)) | |
exit(1); | |
if (memcmp(table->replace.entrytable, entries.entrytable, | |
table->info.size) == 0) | |
continue; | |
} | |
struct xt_counters counters[XT_MAX_ENTRIES]; | |
table->replace.num_counters = info.num_entries; | |
table->replace.counters = counters; | |
optlen = sizeof(table->replace) - sizeof(table->replace.entrytable) + | |
table->replace.size; | |
if (setsockopt(fd, level, IPT_SO_SET_REPLACE, &table->replace, optlen)) | |
exit(1); | |
} | |
close(fd); | |
} | |
static void checkpoint_arptables(void) | |
{ | |
int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); | |
if (fd == -1) { | |
switch (errno) { | |
case EAFNOSUPPORT: | |
case ENOPROTOOPT: | |
case ENOENT: | |
return; | |
} | |
exit(1); | |
} | |
for (unsigned i = 0; i < sizeof(arpt_tables) / sizeof(arpt_tables[0]); i++) { | |
struct arpt_table_desc* table = &arpt_tables[i]; | |
strcpy(table->info.name, table->name); | |
strcpy(table->replace.name, table->name); | |
socklen_t optlen = sizeof(table->info); | |
if (getsockopt(fd, SOL_IP, ARPT_SO_GET_INFO, &table->info, &optlen)) { | |
switch (errno) { | |
case EPERM: | |
case ENOENT: | |
case ENOPROTOOPT: | |
continue; | |
} | |
exit(1); | |
} | |
if (table->info.size > sizeof(table->replace.entrytable)) | |
exit(1); | |
if (table->info.num_entries > XT_MAX_ENTRIES) | |
exit(1); | |
struct arpt_get_entries entries; | |
memset(&entries, 0, sizeof(entries)); | |
strcpy(entries.name, table->name); | |
entries.size = table->info.size; | |
optlen = sizeof(entries) - sizeof(entries.entrytable) + table->info.size; | |
if (getsockopt(fd, SOL_IP, ARPT_SO_GET_ENTRIES, &entries, &optlen)) | |
exit(1); | |
table->replace.valid_hooks = table->info.valid_hooks; | |
table->replace.num_entries = table->info.num_entries; | |
table->replace.size = table->info.size; | |
memcpy(table->replace.hook_entry, table->info.hook_entry, | |
sizeof(table->replace.hook_entry)); | |
memcpy(table->replace.underflow, table->info.underflow, | |
sizeof(table->replace.underflow)); | |
memcpy(table->replace.entrytable, entries.entrytable, table->info.size); | |
} | |
close(fd); | |
} | |
static void reset_arptables() | |
{ | |
int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); | |
if (fd == -1) { | |
switch (errno) { | |
case EAFNOSUPPORT: | |
case ENOPROTOOPT: | |
case ENOENT: | |
return; | |
} | |
exit(1); | |
} | |
for (unsigned i = 0; i < sizeof(arpt_tables) / sizeof(arpt_tables[0]); i++) { | |
struct arpt_table_desc* table = &arpt_tables[i]; | |
if (table->info.valid_hooks == 0) | |
continue; | |
struct arpt_getinfo info; | |
memset(&info, 0, sizeof(info)); | |
strcpy(info.name, table->name); | |
socklen_t optlen = sizeof(info); | |
if (getsockopt(fd, SOL_IP, ARPT_SO_GET_INFO, &info, &optlen)) | |
exit(1); | |
if (memcmp(&table->info, &info, sizeof(table->info)) == 0) { | |
struct arpt_get_entries entries; | |
memset(&entries, 0, sizeof(entries)); | |
strcpy(entries.name, table->name); | |
entries.size = table->info.size; | |
optlen = sizeof(entries) - sizeof(entries.entrytable) + entries.size; | |
if (getsockopt(fd, SOL_IP, ARPT_SO_GET_ENTRIES, &entries, &optlen)) | |
exit(1); | |
if (memcmp(table->replace.entrytable, entries.entrytable, | |
table->info.size) == 0) | |
continue; | |
} else { | |
} | |
struct xt_counters counters[XT_MAX_ENTRIES]; | |
table->replace.num_counters = info.num_entries; | |
table->replace.counters = counters; | |
optlen = sizeof(table->replace) - sizeof(table->replace.entrytable) + | |
table->replace.size; | |
if (setsockopt(fd, SOL_IP, ARPT_SO_SET_REPLACE, &table->replace, optlen)) | |
exit(1); | |
} | |
close(fd); | |
} | |
#define NF_BR_NUMHOOKS 6 | |
#define EBT_TABLE_MAXNAMELEN 32 | |
#define EBT_CHAIN_MAXNAMELEN 32 | |
#define EBT_BASE_CTL 128 | |
#define EBT_SO_SET_ENTRIES (EBT_BASE_CTL) | |
#define EBT_SO_GET_INFO (EBT_BASE_CTL) | |
#define EBT_SO_GET_ENTRIES (EBT_SO_GET_INFO + 1) | |
#define EBT_SO_GET_INIT_INFO (EBT_SO_GET_ENTRIES + 1) | |
#define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO + 1) | |
struct ebt_replace { | |
char name[EBT_TABLE_MAXNAMELEN]; | |
unsigned int valid_hooks; | |
unsigned int nentries; | |
unsigned int entries_size; | |
struct ebt_entries* hook_entry[NF_BR_NUMHOOKS]; | |
unsigned int num_counters; | |
struct ebt_counter* counters; | |
char* entries; | |
}; | |
struct ebt_entries { | |
unsigned int distinguisher; | |
char name[EBT_CHAIN_MAXNAMELEN]; | |
unsigned int counter_offset; | |
int policy; | |
unsigned int nentries; | |
char data[0] __attribute__((aligned(__alignof__(struct ebt_replace)))); | |
}; | |
struct ebt_table_desc { | |
const char* name; | |
struct ebt_replace replace; | |
char entrytable[XT_TABLE_SIZE]; | |
}; | |
static struct ebt_table_desc ebt_tables[] = { | |
{.name = "filter"}, | |
{.name = "nat"}, | |
{.name = "broute"}, | |
}; | |
static void checkpoint_ebtables(void) | |
{ | |
int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); | |
if (fd == -1) { | |
switch (errno) { | |
case EAFNOSUPPORT: | |
case ENOPROTOOPT: | |
case ENOENT: | |
return; | |
} | |
exit(1); | |
} | |
for (size_t i = 0; i < sizeof(ebt_tables) / sizeof(ebt_tables[0]); i++) { | |
struct ebt_table_desc* table = &ebt_tables[i]; | |
strcpy(table->replace.name, table->name); | |
socklen_t optlen = sizeof(table->replace); | |
if (getsockopt(fd, SOL_IP, EBT_SO_GET_INIT_INFO, &table->replace, | |
&optlen)) { | |
switch (errno) { | |
case EPERM: | |
case ENOENT: | |
case ENOPROTOOPT: | |
continue; | |
} | |
exit(1); | |
} | |
if (table->replace.entries_size > sizeof(table->entrytable)) | |
exit(1); | |
table->replace.num_counters = 0; | |
table->replace.entries = table->entrytable; | |
optlen = sizeof(table->replace) + table->replace.entries_size; | |
if (getsockopt(fd, SOL_IP, EBT_SO_GET_INIT_ENTRIES, &table->replace, | |
&optlen)) | |
exit(1); | |
} | |
close(fd); | |
} | |
static void reset_ebtables() | |
{ | |
int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); | |
if (fd == -1) { | |
switch (errno) { | |
case EAFNOSUPPORT: | |
case ENOPROTOOPT: | |
case ENOENT: | |
return; | |
} | |
exit(1); | |
} | |
for (unsigned i = 0; i < sizeof(ebt_tables) / sizeof(ebt_tables[0]); i++) { | |
struct ebt_table_desc* table = &ebt_tables[i]; | |
if (table->replace.valid_hooks == 0) | |
continue; | |
struct ebt_replace replace; | |
memset(&replace, 0, sizeof(replace)); | |
strcpy(replace.name, table->name); | |
socklen_t optlen = sizeof(replace); | |
if (getsockopt(fd, SOL_IP, EBT_SO_GET_INFO, &replace, &optlen)) | |
exit(1); | |
replace.num_counters = 0; | |
table->replace.entries = 0; | |
for (unsigned h = 0; h < NF_BR_NUMHOOKS; h++) | |
table->replace.hook_entry[h] = 0; | |
if (memcmp(&table->replace, &replace, sizeof(table->replace)) == 0) { | |
char entrytable[XT_TABLE_SIZE]; | |
memset(&entrytable, 0, sizeof(entrytable)); | |
replace.entries = entrytable; | |
optlen = sizeof(replace) + replace.entries_size; | |
if (getsockopt(fd, SOL_IP, EBT_SO_GET_ENTRIES, &replace, &optlen)) | |
exit(1); | |
if (memcmp(table->entrytable, entrytable, replace.entries_size) == 0) | |
continue; | |
} | |
for (unsigned j = 0, h = 0; h < NF_BR_NUMHOOKS; h++) { | |
if (table->replace.valid_hooks & (1 << h)) { | |
table->replace.hook_entry[h] = | |
(struct ebt_entries*)table->entrytable + j; | |
j++; | |
} | |
} | |
table->replace.entries = table->entrytable; | |
optlen = sizeof(table->replace) + table->replace.entries_size; | |
if (setsockopt(fd, SOL_IP, EBT_SO_SET_ENTRIES, &table->replace, optlen)) | |
exit(1); | |
} | |
close(fd); | |
} | |
static void checkpoint_net_namespace(void) | |
{ | |
checkpoint_ebtables(); | |
checkpoint_arptables(); | |
checkpoint_iptables(ipv4_tables, sizeof(ipv4_tables) / sizeof(ipv4_tables[0]), | |
AF_INET, SOL_IP); | |
checkpoint_iptables(ipv6_tables, sizeof(ipv6_tables) / sizeof(ipv6_tables[0]), | |
AF_INET6, SOL_IPV6); | |
} | |
static void reset_net_namespace(void) | |
{ | |
reset_ebtables(); | |
reset_arptables(); | |
reset_iptables(ipv4_tables, sizeof(ipv4_tables) / sizeof(ipv4_tables[0]), | |
AF_INET, SOL_IP); | |
reset_iptables(ipv6_tables, sizeof(ipv6_tables) / sizeof(ipv6_tables[0]), | |
AF_INET6, SOL_IPV6); | |
} | |
static void mount_cgroups(const char* dir, const char** controllers, int count) | |
{ | |
if (mkdir(dir, 0777)) { | |
return; | |
} | |
char enabled[128] = {0}; | |
int i = 0; | |
for (; i < count; i++) { | |
if (mount("none", dir, "cgroup", 0, controllers[i])) { | |
continue; | |
} | |
umount(dir); | |
strcat(enabled, ","); | |
strcat(enabled, controllers[i]); | |
} | |
if (enabled[0] == 0) { | |
if (rmdir(dir) && errno != EBUSY) | |
exit(1); | |
return; | |
} | |
if (mount("none", dir, "cgroup", 0, enabled + 1)) { | |
if (rmdir(dir) && errno != EBUSY) | |
exit(1); | |
} | |
if (chmod(dir, 0777)) { | |
} | |
} | |
static void mount_cgroups2(const char** controllers, int count) | |
{ | |
if (mkdir("/syzcgroup/unified", 0777)) { | |
return; | |
} | |
if (mount("none", "/syzcgroup/unified", "cgroup2", 0, NULL)) { | |
if (rmdir("/syzcgroup/unified") && errno != EBUSY) | |
exit(1); | |
return; | |
} | |
if (chmod("/syzcgroup/unified", 0777)) { | |
} | |
int control = open("/syzcgroup/unified/cgroup.subtree_control", O_WRONLY); | |
if (control == -1) | |
return; | |
int i; | |
for (i = 0; i < count; i++) | |
if (write(control, controllers[i], strlen(controllers[i])) < 0) { | |
} | |
close(control); | |
} | |
static void setup_cgroups() | |
{ | |
const char* unified_controllers[] = {"+cpu", "+io", "+pids"}; | |
const char* net_controllers[] = {"net", "net_prio", "devices", "blkio", | |
"freezer"}; | |
const char* cpu_controllers[] = {"cpuset", "cpuacct", "hugetlb", "rlimit", | |
"memory"}; | |
if (mkdir("/syzcgroup", 0777)) { | |
return; | |
} | |
mount_cgroups2(unified_controllers, | |
sizeof(unified_controllers) / sizeof(unified_controllers[0])); | |
mount_cgroups("/syzcgroup/net", net_controllers, | |
sizeof(net_controllers) / sizeof(net_controllers[0])); | |
mount_cgroups("/syzcgroup/cpu", cpu_controllers, | |
sizeof(cpu_controllers) / sizeof(cpu_controllers[0])); | |
write_file("/syzcgroup/cpu/cgroup.clone_children", "1"); | |
write_file("/syzcgroup/cpu/cpuset.memory_pressure_enabled", "1"); | |
} | |
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/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(file, sizeof(file), "%s/memory.soft_limit_in_bytes", cgroupdir); | |
write_file(file, "%d", 299 << 20); | |
snprintf(file, sizeof(file), "%s/memory.limit_in_bytes", cgroupdir); | |
write_file(file, "%d", 300 << 20); | |
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 initialize_cgroups() | |
{ | |
if (mkdir("./syz-tmp/newroot/syzcgroup", 0700)) | |
exit(1); | |
if (mkdir("./syz-tmp/newroot/syzcgroup/unified", 0700)) | |
exit(1); | |
if (mkdir("./syz-tmp/newroot/syzcgroup/cpu", 0700)) | |
exit(1); | |
if (mkdir("./syz-tmp/newroot/syzcgroup/net", 0700)) | |
exit(1); | |
unsigned bind_mount_flags = MS_BIND | MS_REC | MS_PRIVATE; | |
if (mount("/syzcgroup/unified", "./syz-tmp/newroot/syzcgroup/unified", NULL, | |
bind_mount_flags, NULL)) { | |
} | |
if (mount("/syzcgroup/cpu", "./syz-tmp/newroot/syzcgroup/cpu", NULL, | |
bind_mount_flags, NULL)) { | |
} | |
if (mount("/syzcgroup/net", "./syz-tmp/newroot/syzcgroup/net", NULL, | |
bind_mount_flags, NULL)) { | |
} | |
} | |
static void sandbox_common_mount_tmpfs(void) | |
{ | |
write_file("/proc/sys/fs/mount-max", "100000"); | |
if (mkdir("./syz-tmp", 0777)) | |
exit(1); | |
if (mount("", "./syz-tmp", "tmpfs", 0, NULL)) | |
exit(1); | |
if (mkdir("./syz-tmp/newroot", 0777)) | |
exit(1); | |
if (mkdir("./syz-tmp/newroot/dev", 0700)) | |
exit(1); | |
unsigned bind_mount_flags = MS_BIND | MS_REC | MS_PRIVATE; | |
if (mount("/dev", "./syz-tmp/newroot/dev", NULL, bind_mount_flags, NULL)) | |
exit(1); | |
if (mkdir("./syz-tmp/newroot/proc", 0700)) | |
exit(1); | |
if (mount("syz-proc", "./syz-tmp/newroot/proc", "proc", 0, NULL)) | |
exit(1); | |
if (mkdir("./syz-tmp/newroot/selinux", 0700)) | |
exit(1); | |
const char* selinux_path = "./syz-tmp/newroot/selinux"; | |
if (mount("/selinux", selinux_path, NULL, bind_mount_flags, NULL)) { | |
if (errno != ENOENT) | |
exit(1); | |
if (mount("/sys/fs/selinux", selinux_path, NULL, bind_mount_flags, NULL) && | |
errno != ENOENT) | |
exit(1); | |
} | |
if (mkdir("./syz-tmp/newroot/sys", 0700)) | |
exit(1); | |
if (mount("/sys", "./syz-tmp/newroot/sys", 0, bind_mount_flags, NULL)) | |
exit(1); | |
initialize_cgroups(); | |
if (mkdir("./syz-tmp/pivot", 0777)) | |
exit(1); | |
if (syscall(SYS_pivot_root, "./syz-tmp", "./syz-tmp/pivot")) { | |
if (chdir("./syz-tmp")) | |
exit(1); | |
} else { | |
if (chdir("/")) | |
exit(1); | |
if (umount2("./pivot", MNT_DETACH)) | |
exit(1); | |
} | |
if (chroot("./newroot")) | |
exit(1); | |
if (chdir("/")) | |
exit(1); | |
} | |
static void setup_common() | |
{ | |
if (mount(0, "/sys/fs/fuse/connections", "fusectl", 0, 0)) { | |
} | |
} | |
static void setup_binderfs() | |
{ | |
if (mkdir("/dev/binderfs", 0777)) { | |
} | |
if (mount("binder", "/dev/binderfs", "binder", 0, NULL)) { | |
} | |
} | |
static void loop(); | |
static void sandbox_common() | |
{ | |
prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); | |
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 = 128 << 20; | |
setrlimit(RLIMIT_CORE, &rlim); | |
rlim.rlim_cur = rlim.rlim_max = 256; | |
setrlimit(RLIMIT_NOFILE, &rlim); | |
if (unshare(CLONE_NEWNS)) { | |
} | |
if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL)) { | |
} | |
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); | |
} | |
static 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)) { | |
} | |
write_file("/proc/sys/net/ipv4/ping_group_range", "0 65535"); | |
initialize_tun(); | |
initialize_netdevices(); | |
sandbox_common_mount_tmpfs(); | |
setup_binderfs(); | |
loop(); | |
exit(1); | |
} | |
#define FS_IOC_SETFLAGS _IOW('f', 2, long) | |
static void remove_dir(const char* dir) | |
{ | |
int iter = 0; | |
DIR* dp = 0; | |
const int umount_flags = MNT_FORCE | UMOUNT_NOFOLLOW; | |
retry: | |
while (umount2(dir, umount_flags) == 0) { | |
} | |
dp = opendir(dir); | |
if (dp == NULL) { | |
if (errno == EMFILE) { | |
exit(1); | |
} | |
exit(1); | |
} | |
struct dirent* ep = 0; | |
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, umount_flags) == 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, umount_flags)) | |
exit(1); | |
} | |
} | |
closedir(dp); | |
for (int 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, umount_flags)) | |
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); | |
for (int 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(); | |
checkpoint_net_namespace(); | |
} | |
static void reset_loop() | |
{ | |
reset_net_namespace(); | |
} | |
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(); | |
if (symlink("/dev/binderfs", "./binderfs")) { | |
} | |
} | |
static void close_fds() | |
{ | |
for (int fd = 3; fd < MAX_FDS; fd++) | |
close(fd); | |
} | |
static const char* setup_binfmt_misc() | |
{ | |
if (mount(0, "/proc/sys/fs/binfmt_misc", "binfmt_misc", 0, 0)) { | |
return NULL; | |
} | |
if (!write_file("/proc/sys/fs/binfmt_misc/register", | |
":syz0:M:0:\x01::./file0:") || | |
!write_file("/proc/sys/fs/binfmt_misc/register", | |
":syz1:M:1:\x02::./file0:POC")) | |
return "write(/proc/sys/fs/binfmt_misc/register) failed"; | |
return NULL; | |
} | |
static const char* setup_usb() | |
{ | |
if (chmod("/dev/raw-gadget", 0666)) | |
return "failed to chmod /dev/raw-gadget"; | |
return NULL; | |
} | |
#define NL802154_CMD_SET_SHORT_ADDR 11 | |
#define NL802154_ATTR_IFINDEX 3 | |
#define NL802154_ATTR_SHORT_ADDR 10 | |
static const char* setup_802154() | |
{ | |
const char* error = NULL; | |
int sock_generic = -1; | |
int sock_route = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | |
if (sock_route == -1) { | |
error = "socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) failed"; | |
goto fail; | |
} | |
sock_generic = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); | |
if (sock_generic == -1) { | |
error = "socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC) failed"; | |
goto fail; | |
} | |
{ | |
int nl802154_family_id = | |
netlink_query_family_id(&nlmsg, sock_generic, "nl802154", true); | |
if (nl802154_family_id < 0) { | |
error = "netlink_query_family_id failed"; | |
goto fail; | |
} | |
for (int i = 0; i < 2; i++) { | |
char devname[] = "wpan0"; | |
devname[strlen(devname) - 1] += i; | |
uint64_t hwaddr = 0xaaaaaaaaaaaa0002 + (i << 8); | |
uint16_t shortaddr = 0xaaa0 + i; | |
int ifindex = if_nametoindex(devname); | |
struct genlmsghdr genlhdr; | |
memset(&genlhdr, 0, sizeof(genlhdr)); | |
genlhdr.cmd = NL802154_CMD_SET_SHORT_ADDR; | |
netlink_init(&nlmsg, nl802154_family_id, 0, &genlhdr, sizeof(genlhdr)); | |
netlink_attr(&nlmsg, NL802154_ATTR_IFINDEX, &ifindex, sizeof(ifindex)); | |
netlink_attr(&nlmsg, NL802154_ATTR_SHORT_ADDR, &shortaddr, | |
sizeof(shortaddr)); | |
if (netlink_send(&nlmsg, sock_generic) < 0) { | |
error = "NL802154_CMD_SET_SHORT_ADDR failed"; | |
goto fail; | |
} | |
netlink_device_change(&nlmsg, sock_route, devname, true, 0, &hwaddr, | |
sizeof(hwaddr), 0); | |
if (i == 0) { | |
netlink_add_device_impl(&nlmsg, "lowpan", "lowpan0", false); | |
netlink_done(&nlmsg); | |
netlink_attr(&nlmsg, IFLA_LINK, &ifindex, sizeof(ifindex)); | |
if (netlink_send(&nlmsg, sock_route) < 0) { | |
error = "netlink: adding device lowpan0 type lowpan link wpan0"; | |
goto fail; | |
} | |
} | |
} | |
} | |
fail: | |
close(sock_route); | |
close(sock_generic); | |
return error; | |
} | |
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) | |
{ | |
if (write(1, "executing program\n", sizeof("executing program\n") - 1)) { | |
} | |
int i, call, thread; | |
for (call = 0; call < 8; 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, 50); | |
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 = 0; | |
for (;; iter++) { | |
char cwdbuf[32]; | |
sprintf(cwdbuf, "./%d", iter); | |
if (mkdir(cwdbuf, 0777)) | |
exit(1); | |
reset_loop(); | |
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 (;;) { | |
sleep_ms(10); | |
if (waitpid(-1, &status, WNOHANG | WAIT_FLAGS) == pid) | |
break; | |
if (current_time_ms() - start < 5000) | |
continue; | |
kill_and_wait(pid, &status); | |
break; | |
} | |
remove_dir(cwdbuf); | |
} | |
} | |
uint64_t r[1] = {0xffffffffffffffff}; | |
void execute_call(int call) | |
{ | |
intptr_t res = 0; | |
switch (call) { | |
case 0: | |
NONFAILING(memcpy((void*)0x20000340, "/dev/ptmx\000", 10)); | |
syscall(__NR_openat, /*fd=*/0xffffffffffffff9cul, /*file=*/0x20000340ul, | |
/*flags=O_SYNC|O_CLOEXEC|FASYNC|O_APPEND|O_WRONLY*/ 0x183401ul, | |
/*mode=*/0ul); | |
break; | |
case 1: | |
syscall(__NR_ioctl, /*fd=*/-1, /*cmd=*/0x5404, /*arg=*/0ul); | |
break; | |
case 2: | |
NONFAILING(memcpy((void*)0x20000000, "/dev/input/event#\000", 18)); | |
NONFAILING( | |
syz_open_dev(/*dev=*/0x20000000, /*id=*/0x8000207, | |
/*flags=O_NONBLOCK|O_NOFOLLOW|O_CREAT|O_APPEND*/ 0x20c40)); | |
break; | |
case 3: | |
NONFAILING(*(uint32_t*)0x20000080 = -1); | |
NONFAILING(*(uint32_t*)0x20000084 = 6); | |
NONFAILING(*(uint32_t*)0x20000088 = 0x200009); | |
NONFAILING(*(uint32_t*)0x2000008c = 5); | |
NONFAILING(*(uint8_t*)0x20000090 = 1); | |
NONFAILING(memcpy((void*)0x20000091, | |
"\x4e\x00\x40\x02\x9e\x35\xbc\xa1\x40\xd5\x84\x27\x39\x2b" | |
"\x33\x68\x89\xf0\xcc", | |
19)); | |
NONFAILING(*(uint32_t*)0x200000a4 = 2); | |
NONFAILING(*(uint32_t*)0x200000a8 = 0x101); | |
syscall(__NR_ioctl, /*fd=*/-1, /*cmd=*/0x402c542d, /*arg=*/0x20000080ul); | |
break; | |
case 4: | |
NONFAILING(memcpy((void*)0x20000200, "/dev/sg#\000", 9)); | |
res = -1; | |
NONFAILING(res = syz_open_dev(/*dev=*/0x20000200, /*id=*/0x140000000000, | |
/*flags=O_LARGEFILE*/ 0x8000)); | |
if (res != -1) | |
r[0] = res; | |
break; | |
case 5: | |
NONFAILING(*(uint32_t*)0x20000080 = 0); | |
NONFAILING(*(uint32_t*)0x20000084 = 2); | |
NONFAILING(*(uint8_t*)0x20000088 = 0x85); | |
NONFAILING(*(uint8_t*)0x20000089 = 8); | |
NONFAILING(*(uint8_t*)0x2000008a = 0x15); | |
NONFAILING(*(uint8_t*)0x2000008b = 0xd); | |
syscall(__NR_ioctl, /*fd=*/r[0], /*arg0=*/1, /*arg1=*/0x20000080ul); | |
break; | |
case 6: | |
syscall(__NR_ioctl, /*fd=*/-1, /*arg0=*/0xc0104320, /*arg1=*/0ul); | |
break; | |
case 7: | |
syscall(__NR_ioctl, /*fd=*/-1, /*arg0=*/0x402c542d, /*arg1=*/0ul); | |
break; | |
} | |
} | |
int main(void) | |
{ | |
syscall(__NR_mmap, /*addr=*/0x1ffff000ul, /*len=*/0x1000ul, /*prot=*/0ul, | |
/*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/ 0x32ul, /*fd=*/-1, | |
/*offset=*/0ul); | |
syscall(__NR_mmap, /*addr=*/0x20000000ul, /*len=*/0x1000000ul, | |
/*prot=PROT_WRITE|PROT_READ|PROT_EXEC*/ 7ul, | |
/*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/ 0x32ul, /*fd=*/-1, | |
/*offset=*/0ul); | |
syscall(__NR_mmap, /*addr=*/0x21000000ul, /*len=*/0x1000ul, /*prot=*/0ul, | |
/*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/ 0x32ul, /*fd=*/-1, | |
/*offset=*/0ul); | |
setup_cgroups(); | |
const char* reason; | |
(void)reason; | |
if ((reason = setup_binfmt_misc())) | |
printf("the reproducer may not work as expected: binfmt_misc setup failed: " | |
"%s\n", | |
reason); | |
if ((reason = setup_usb())) | |
printf("the reproducer may not work as expected: USB injection setup " | |
"failed: %s\n", | |
reason); | |
if ((reason = setup_802154())) | |
printf("the reproducer may not work as expected: 802154 injection setup " | |
"failed: %s\n", | |
reason); | |
install_segv_handler(); | |
use_temporary_dir(); | |
do_sandbox_none(); | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment