From 9fe4bdc5f1037a409e82299f36117030114c7b94 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Fri, 20 Jul 2018 20:26:05 +0200 Subject: executor: overhaul Make as much code as possible shared between all OSes. In particular main is now common across all OSes. Make more code shared between executor and csource (in particular, loop function and threaded execution logic). Also make loop and threaded logic shared across all OSes. Make more posix/unix code shared across OSes (e.g. signal handling, pthread creation, etc). Plus other changes along similar lines. Also support test OS in executor (based on portable posix) and add 4 arches that cover all execution modes (fork server/no fork server, shmem/no shmem). This change paves way for testing of executor code and allows to preserve consistency across OSes and executor/csource. --- pkg/csource/generated.go | 3756 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3756 insertions(+) create mode 100644 pkg/csource/generated.go (limited to 'pkg/csource/generated.go') diff --git a/pkg/csource/generated.go b/pkg/csource/generated.go new file mode 100644 index 000000000..27ed3e967 --- /dev/null +++ b/pkg/csource/generated.go @@ -0,0 +1,3756 @@ +// AUTOGENERATED FROM executor/common.h + +package csource + +var commonHeader = ` + + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include + +#if SYZ_EXECUTOR && !GOOS_linux +#include +NORETURN void doexit(int status) +{ + _exit(status); + for (;;) { + } +} +#endif + +#if !GOOS_fuchsia && !GOOS_windows +#if SYZ_EXECUTOR || SYZ_HANDLE_SEGV +#include +#include +#include + +#if GOOS_linux +#include +#endif + +static __thread int skip_segv; +static __thread jmp_buf segv_env; + +#if GOOS_akaros +#include +static void recover() +{ + _longjmp(segv_env, 1); +} +#endif + +static void segv_handler(int sig, siginfo_t* info, void* ctx) +{ + uintptr_t addr = (uintptr_t)info->si_addr; + const uintptr_t prog_start = 1 << 20; + const uintptr_t prog_end = 100 << 20; + if (__atomic_load_n(&skip_segv, __ATOMIC_RELAXED) && (addr < prog_start || addr > prog_end)) { + debug("SIGSEGV on %p, skipping\n", (void*)addr); +#if GOOS_akaros + struct user_context* uctx = (struct user_context*)ctx; + uctx->tf.hw_tf.tf_rip = (long)(void*)recover; + return; +#else + _longjmp(segv_env, 1); +#endif + } + debug("SIGSEGV on %p, exiting\n", (void*)addr); + doexit(sig); +} + +static void install_segv_handler() +{ + struct sigaction sa; +#if GOOS_linux + 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); +#endif + memset(&sa, 0, sizeof(sa)); + sa.sa_sigaction = segv_handler; + sa.sa_flags = SA_NODEFER | SA_SIGINFO; + sigaction(SIGSEGV, &sa, NULL); + sigaction(SIGBUS, &sa, NULL); +} + +#define NONFAILING(...) \ + { \ + __atomic_fetch_add(&skip_segv, 1, __ATOMIC_SEQ_CST); \ + if (_setjmp(segv_env) == 0) { \ + __VA_ARGS__; \ + } \ + __atomic_fetch_sub(&skip_segv, 1, __ATOMIC_SEQ_CST); \ + } +#endif +#endif + +#if !GOOS_windows +#if SYZ_EXECUTOR || SYZ_THREADED || SYZ_REPEAT +static void sleep_ms(uint64 ms) +{ + usleep(ms * 1000); +} +#endif + +#if SYZ_EXECUTOR || SYZ_THREADED || SYZ_REPEAT +#include + +uint64 current_time_ms() +{ + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts)) + fail("clock_gettime failed"); + return (uint64)ts.tv_sec * 1000 + (uint64)ts.tv_nsec / 1000000; +} +#endif + +#if SYZ_EXECUTOR || SYZ_USE_TMP_DIR +#include +#include +#include + +static void use_temporary_dir() +{ + char tmpdir_template[] = "./syzkaller.XXXXXX"; + char* tmpdir = mkdtemp(tmpdir_template); + if (!tmpdir) + fail("failed to mkdtemp"); + if (chmod(tmpdir, 0777)) + fail("failed to chmod"); + if (chdir(tmpdir)) + fail("failed to chdir"); +} +#endif +#endif + +#if GOOS_akaros || GOOS_netbsd || GOOS_freebsd || GOOS_test +#if SYZ_EXECUTOR || SYZ_EXECUTOR_USES_FORK_SERVER && SYZ_REPEAT && SYZ_USE_TMP_DIR +#include +#include +#include +#include +#include + +static void remove_dir(const char* dir) +{ + DIR* dp; + struct dirent* ep; + dp = opendir(dir); + if (dp == NULL) + exitf("opendir(%s) failed", dir); + 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); + struct stat st; + if (lstat(filename, &st)) + exitf("lstat(%s) failed", filename); + if (S_ISDIR(st.st_mode)) { + remove_dir(filename); + continue; + } + if (unlink(filename)) + exitf("unlink(%s) failed", filename); + } + closedir(dp); + if (rmdir(dir)) + exitf("rmdir(%s) failed", dir); +} +#endif +#endif + +#if !GOOS_linux +#if SYZ_EXECUTOR || SYZ_FAULT_INJECTION +static int inject_fault(int nth) +{ + return 0; +} +#endif +#if SYZ_EXECUTOR +static int fault_injected(int fail_fd) +{ + return 0; +} +#endif +#endif + +#if !GOOS_windows +#if SYZ_EXECUTOR || SYZ_THREADED +#include + +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); + if (pthread_create(&th, &attr, fn, arg)) + exitf("pthread_create failed"); + pthread_attr_destroy(&attr); +} + +#endif +#endif + +#if GOOS_freebsd || GOOS_netbsd || GOOS_akaros || GOOS_test +#if SYZ_EXECUTOR || SYZ_THREADED + +#include +#include + +typedef struct { + pthread_mutex_t mu; + pthread_cond_t cv; + int state; +} event_t; + +static void event_init(event_t* ev) +{ + if (pthread_mutex_init(&ev->mu, 0)) + fail("pthread_mutex_init failed"); + if (pthread_cond_init(&ev->cv, 0)) + fail("pthread_cond_init failed"); + ev->state = 0; +} + +static void event_reset(event_t* ev) +{ + ev->state = 0; +} + +static void event_set(event_t* ev) +{ + pthread_mutex_lock(&ev->mu); + if (ev->state) + fail("event already set"); + ev->state = 1; + pthread_mutex_unlock(&ev->mu); + pthread_cond_broadcast(&ev->cv); +} + +static void event_wait(event_t* ev) +{ + pthread_mutex_lock(&ev->mu); + while (!ev->state) + pthread_cond_wait(&ev->cv, &ev->mu); + pthread_mutex_unlock(&ev->mu); +} + +static int event_isset(event_t* ev) +{ + pthread_mutex_lock(&ev->mu); + int res = ev->state; + pthread_mutex_unlock(&ev->mu); + return res; +} + +static int event_timedwait(event_t* ev, uint64 timeout_ms) +{ + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts)) + fail("clock_gettime failed"); + const uint64 kNsPerSec = 1000 * 1000 * 1000; + uint64 start_ns = (uint64)ts.tv_sec * kNsPerSec + (uint64)ts.tv_nsec; + uint64 timeout_ns = timeout_ms * 1000 * 1000; + pthread_mutex_lock(&ev->mu); + for (;;) { + if (ev->state) + break; + if (clock_gettime(CLOCK_MONOTONIC, &ts)) + fail("clock_gettime failed"); + uint64 now_ns = (uint64)ts.tv_sec * kNsPerSec + (uint64)ts.tv_nsec; + if (now_ns - start_ns > timeout_ns) + break; + uint64 remain_ns = timeout_ns - (now_ns - start_ns); + ts.tv_sec = remain_ns / kNsPerSec; + ts.tv_nsec = remain_ns % kNsPerSec; + pthread_cond_timedwait(&ev->cv, &ev->mu, &ts); + } + int res = ev->state; + pthread_mutex_unlock(&ev->mu); + return res; +} +#endif +#endif + +#if SYZ_EXECUTOR || SYZ_USE_BITMASKS +#define BITMASK_LEN(type, bf_len) (type)((1ull << (bf_len)) - 1) + +#define BITMASK_LEN_OFF(type, bf_off, bf_len) (type)(BITMASK_LEN(type, (bf_len)) << (bf_off)) + +#define STORE_BY_BITMASK(type, addr, val, bf_off, bf_len) \ + if ((bf_off) == 0 && (bf_len) == 0) { \ + *(type*)(addr) = (type)(val); \ + } else { \ + type new_val = *(type*)(addr); \ + new_val &= ~BITMASK_LEN_OFF(type, (bf_off), (bf_len)); \ + new_val |= ((type)(val)&BITMASK_LEN(type, (bf_len))) << (bf_off); \ + *(type*)(addr) = new_val; \ + } +#endif + +#if SYZ_EXECUTOR || SYZ_USE_CHECKSUMS +struct csum_inet { + uint32 acc; +}; + +static void csum_inet_init(struct csum_inet* csum) +{ + csum->acc = 0; +} + +static void csum_inet_update(struct csum_inet* csum, const uint8* data, size_t length) +{ + if (length == 0) + return; + + size_t i; + for (i = 0; i < length - 1; i += 2) + csum->acc += *(uint16*)&data[i]; + + if (length & 1) + csum->acc += (uint16)data[length - 1]; + + while (csum->acc > 0xffff) + csum->acc = (csum->acc & 0xffff) + (csum->acc >> 16); +} + +static uint16 csum_inet_digest(struct csum_inet* csum) +{ + return ~csum->acc; +} +#endif + +#if GOOS_akaros + + +#include +#include +#include +#include + +#if SYZ_EXECUTOR || SYZ_SANDBOX_NONE +static void loop(); +static int do_sandbox_none(void) +{ + loop(); + doexit(0); +} +#endif + +#if SYZ_EXECUTOR || SYZ_REPEAT +static void execute_one(); +const char* program_name; + +void child() +{ +#if SYZ_EXECUTOR || SYZ_HANDLE_SEGV + install_segv_handler(); +#endif +#if SYZ_EXECUTOR + receive_execute(); + close(kInPipeFd); +#endif + execute_one(); + doexit(0); +} +#endif + +#define do_sandbox_setuid() 0 +#define do_sandbox_namespace() 0 +#define setup_loop() +#define reset_loop() +#define setup_test() +#define reset_test() +#elif GOOS_freebsd || GOOS_netbsd + + +#include + +#if SYZ_EXECUTOR || SYZ_SANDBOX_NONE +static void loop(); +static int do_sandbox_none(void) +{ + loop(); + return 0; +} +#endif + +#define do_sandbox_setuid() 0 +#define do_sandbox_namespace() 0 +#define setup_loop() +#define reset_loop() +#define setup_test() +#define reset_test() +#elif GOOS_fuchsia + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if SYZ_EXECUTOR || SYZ_HANDLE_SEGV +#include +#include +#include +#include +#include +#include + +static __thread int skip_segv; +static __thread jmp_buf segv_env; + +static void segv_handler() +{ + if (__atomic_load_n(&skip_segv, __ATOMIC_RELAXED)) { + debug("recover: skipping\n"); + longjmp(segv_env, 1); + } + debug("recover: exiting\n"); + doexit(SIGSEGV); +} + +static void* ex_handler(void* arg) +{ + zx_handle_t port = (zx_handle_t)(long)arg; + for (int i = 0; i < 10000; i++) { + zx_port_packet_t packet = {}; + zx_status_t status = zx_port_wait(port, ZX_TIME_INFINITE, &packet); + if (status != ZX_OK) { + debug("zx_port_wait failed: %d\n", status); + continue; + } + debug("got exception packet: type=%d status=%d tid=%llu\n", + packet.type, packet.status, (unsigned long long)(packet.exception.tid)); + zx_handle_t thread; + status = zx_object_get_child(zx_process_self(), packet.exception.tid, + ZX_RIGHT_SAME_RIGHTS, &thread); + if (status != ZX_OK) { + debug("zx_object_get_child failed: %d\n", status); + continue; + } + zx_thread_state_general_regs_t regs; + status = zx_thread_read_state(thread, ZX_THREAD_STATE_GENERAL_REGS, + ®s, sizeof(regs)); + if (status != ZX_OK) { + debug("zx_thread_read_state failed: %d (%d)\n", + (int)sizeof(regs), status); + } else { +#if GOARCH_amd64 + regs.rip = (uint64)(void*)&segv_handler; +#elif GOARCH_arm64 + regs.pc = (uint64)(void*)&segv_handler; +#else +#error "unsupported arch" +#endif + status = zx_thread_write_state(thread, ZX_THREAD_STATE_GENERAL_REGS, ®s, sizeof(regs)); + if (status != ZX_OK) { + debug("zx_thread_write_state failed: %d\n", status); + } + } + status = zx_task_resume(thread, ZX_RESUME_EXCEPTION); + if (status != ZX_OK) { + debug("zx_task_resume failed: %d\n", status); + } + zx_handle_close(thread); + } + doexit(1); + return 0; +} + +static void install_segv_handler() +{ + zx_status_t status; + zx_handle_t port; + if ((status = zx_port_create(0, &port)) != ZX_OK) + fail("zx_port_create failed: %d", status); + if ((status = zx_task_bind_exception_port(zx_process_self(), port, 0, 0)) != ZX_OK) + fail("zx_task_bind_exception_port failed: %d", status); + pthread_t th; + if (pthread_create(&th, 0, ex_handler, (void*)(long)port)) + fail("pthread_create failed"); +} + +#define NONFAILING(...) \ + { \ + __atomic_fetch_add(&skip_segv, 1, __ATOMIC_SEQ_CST); \ + if (sigsetjmp(segv_env, 0) == 0) { \ + __VA_ARGS__; \ + } \ + __atomic_fetch_sub(&skip_segv, 1, __ATOMIC_SEQ_CST); \ + } +#endif + +#if SYZ_EXECUTOR || SYZ_THREADED +#include + +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) + fail("event already set"); + __atomic_store_n(&ev->state, 1, __ATOMIC_RELEASE); +} + +static void event_wait(event_t* ev) +{ + while (!__atomic_load_n(&ev->state, __ATOMIC_ACQUIRE)) + usleep(200); +} + +static int event_isset(event_t* ev) +{ + return __atomic_load_n(&ev->state, __ATOMIC_ACQUIRE); +} + +static int event_timedwait(event_t* ev, uint64 timeout_ms) +{ + uint64 start = current_time_ms(); + for (;;) { + if (__atomic_load_n(&ev->state, __ATOMIC_RELAXED)) + return 1; + if (current_time_ms() - start > timeout_ms) + return 0; + usleep(200); + } +} +#endif + +#if SYZ_EXECUTOR || __NR_syz_mmap +long syz_mmap(size_t addr, size_t size) +{ + zx_handle_t root = zx_vmar_root_self(); + zx_info_vmar_t info; + zx_status_t status = zx_object_get_info(root, ZX_INFO_VMAR, &info, sizeof(info), 0, 0); + if (status != ZX_OK) + fail("zx_object_get_info(ZX_INFO_VMAR) failed: %d", status); + zx_handle_t vmo; + status = zx_vmo_create(size, 0, &vmo); + if (status != ZX_OK) + return status; + uintptr_t mapped_addr; + status = zx_vmar_map(root, addr - info.base, vmo, 0, size, + ZX_VM_FLAG_SPECIFIC_OVERWRITE | ZX_VM_FLAG_PERM_READ | + ZX_VM_FLAG_PERM_WRITE | ZX_VM_FLAG_PERM_EXECUTE, + &mapped_addr); + return status; +} +#endif + +#if SYZ_EXECUTOR || __NR_syz_process_self +static long syz_process_self() +{ + return zx_process_self(); +} +#endif + +#if SYZ_EXECUTOR || __NR_syz_thread_self +static long syz_thread_self() +{ + return zx_thread_self(); +} +#endif + +#if SYZ_EXECUTOR || __NR_syz_vmar_root_self +static long syz_vmar_root_self() +{ + return zx_vmar_root_self(); +} +#endif + +#if SYZ_EXECUTOR || __NR_syz_job_default +static long syz_job_default() +{ + return zx_job_default(); +} +#endif + +#if SYZ_EXECUTOR || __NR_syz_future_time +static long syz_future_time(long when) +{ + zx_time_t delta_ms; + switch (when) { + case 0: + delta_ms = 5; + case 1: + delta_ms = 30; + default: + delta_ms = 10000; + } + zx_time_t now = zx_clock_get(ZX_CLOCK_MONOTONIC); + return now + delta_ms * 1000 * 1000; +} +#endif + +#if SYZ_EXECUTOR || SYZ_SANDBOX_NONE +static void loop(); +static int do_sandbox_none(void) +{ + loop(); + return 0; +} +#endif + +#define do_sandbox_setuid() 0 +#define do_sandbox_namespace() 0 +#define setup_loop() +#define reset_loop() +#define setup_test() +#define reset_test() +#elif GOOS_linux + + +#include +#include +#include +#include +#include + +#if SYZ_EXECUTOR +struct cover_t; +static void cover_reset(cover_t* cov); +#endif + +#if SYZ_EXECUTOR || SYZ_THREADED +#include +#include + +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) + fail("event already set"); + __atomic_store_n(&ev->state, 1, __ATOMIC_RELEASE); + syscall(SYS_futex, &ev->state, FUTEX_WAKE); +} + +static void event_wait(event_t* ev) +{ + while (!__atomic_load_n(&ev->state, __ATOMIC_ACQUIRE)) + syscall(SYS_futex, &ev->state, FUTEX_WAIT, 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 timeout_ms) +{ + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts)) + fail("clock_gettime failed"); + const uint64 kNsPerSec = 1000 * 1000 * 1000; + uint64 start_ns = (uint64)ts.tv_sec * kNsPerSec + (uint64)ts.tv_nsec; + uint64 now_ns = start_ns; + uint64 timeout_ns = timeout_ms * 1000 * 1000; + for (;;) { + uint64 remain_ns = timeout_ns - (now_ns - start_ns); + ts.tv_sec = remain_ns / kNsPerSec; + ts.tv_nsec = remain_ns % kNsPerSec; + syscall(SYS_futex, &ev->state, FUTEX_WAIT, 0, &ts); + if (__atomic_load_n(&ev->state, __ATOMIC_RELAXED)) + return 1; + if (clock_gettime(CLOCK_MONOTONIC, &ts)) + fail("clock_gettime failed"); + now_ns = (uint64)ts.tv_sec * kNsPerSec + (uint64)ts.tv_nsec; + if (now_ns - start_ns > timeout_ns) + return 0; + } +} +#endif + +#if SYZ_EXECUTOR || SYZ_TUN_ENABLE || SYZ_ENABLE_NETDEV +#include +#include +#include + +static void vsnprintf_check(char* str, size_t size, const char* format, va_list args) +{ + int rv; + + rv = vsnprintf(str, size, format, args); + if (rv < 0) + fail("tun: snprintf failed"); + if ((size_t)rv >= size) + fail("tun: string '%s...' doesn't fit into buffer", str); +} + +#define COMMAND_MAX_LEN 128 +#define PATH_PREFIX "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin " +#define PATH_PREFIX_LEN (sizeof(PATH_PREFIX) - 1) + +static void execute_command(bool panic, const char* format, ...) +{ + va_list args; + char command[PATH_PREFIX_LEN + COMMAND_MAX_LEN]; + int rv; + + va_start(args, format); + memcpy(command, PATH_PREFIX, PATH_PREFIX_LEN); + vsnprintf_check(command + PATH_PREFIX_LEN, COMMAND_MAX_LEN, format, args); + va_end(args); + rv = system(command); + if (rv) { + if (panic) + fail("command '%s' failed: %d", &command[0], rv); + debug("command '%s': %d\n", &command[0], rv); + } +} +#endif + +#if SYZ_EXECUTOR || SYZ_TUN_ENABLE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int tunfd = -1; +static int tun_frags_enabled; + +#define SYZ_TUN_MAX_PACKET_SIZE 1000 + +#define TUN_IFACE "syz_tun" + +#define LOCAL_MAC "aa:aa:aa:aa:aa:aa" +#define REMOTE_MAC "aa:aa:aa:aa:aa:bb" + +#define LOCAL_IPV4 "172.20.20.170" +#define REMOTE_IPV4 "172.20.20.187" + +#define LOCAL_IPV6 "fe80::aa" +#define REMOTE_IPV6 "fe80::bb" + +#ifndef IFF_NAPI +#define IFF_NAPI 0x0010 +#endif +#ifndef IFF_NAPI_FRAGS +#define IFF_NAPI_FRAGS 0x0020 +#endif + +static void initialize_tun(void) +{ +#if SYZ_EXECUTOR + if (!flag_enable_tun) + return; +#endif + tunfd = open("/dev/net/tun", O_RDWR | O_NONBLOCK); + if (tunfd == -1) { +#if SYZ_EXECUTOR + fail("tun: can't open /dev/net/tun\n"); +#else + 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; +#endif + } + const int kTunFd = 240; + if (dup2(tunfd, kTunFd) < 0) + fail("dup2(tunfd, kTunFd) failed"); + 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) + fail("tun: ioctl(TUNSETIFF) failed"); + } + if (ioctl(tunfd, TUNGETIFF, (void*)&ifr) < 0) + fail("tun: ioctl(TUNGETIFF) failed"); + tun_frags_enabled = (ifr.ifr_flags & IFF_NAPI_FRAGS) != 0; + debug("tun_frags_enabled=%d\n", tun_frags_enabled); + + execute_command(0, "sysctl -w net.ipv6.conf.%s.accept_dad=0", TUN_IFACE); + + execute_command(0, "sysctl -w net.ipv6.conf.%s.router_solicitations=0", TUN_IFACE); + + execute_command(1, "ip link set dev %s address %s", TUN_IFACE, LOCAL_MAC); + execute_command(1, "ip addr add %s/24 dev %s", LOCAL_IPV4, TUN_IFACE); + execute_command(1, "ip neigh add %s lladdr %s dev %s nud permanent", + REMOTE_IPV4, REMOTE_MAC, TUN_IFACE); + execute_command(0, "ip -6 addr add %s/120 dev %s", LOCAL_IPV6, TUN_IFACE); + execute_command(0, "ip -6 neigh add %s lladdr %s dev %s nud permanent", + REMOTE_IPV6, REMOTE_MAC, TUN_IFACE); + execute_command(1, "ip link set dev %s up", TUN_IFACE); +} +#endif + +#if SYZ_EXECUTOR || SYZ_ENABLE_NETDEV +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEV_IPV4 "172.20.20.%d" +#define DEV_IPV6 "fe80::%02hx" +#define DEV_MAC "aa:aa:aa:aa:aa:%02hx" + +static void snprintf_check(char* str, size_t size, const char* format, ...) +{ + va_list args; + + va_start(args, format); + vsnprintf_check(str, size, format, args); + va_end(args); +} + +static void initialize_netdevices(void) +{ +#if SYZ_EXECUTOR + if (!flag_enable_net_dev) + return; +#endif + unsigned i; + const char* devtypes[] = {"ip6gretap", "bridge", "vcan", "bond", "team"}; + const char* devnames[] = {"lo", "sit0", "bridge0", "vcan0", "tunl0", + "gre0", "gretap0", "ip_vti0", "ip6_vti0", + "ip6tnl0", "ip6gre0", "ip6gretap0", + "erspan0", "bond0", "veth0", "veth1", "team0", + "veth0_to_bridge", "veth1_to_bridge", + "veth0_to_bond", "veth1_to_bond", + "veth0_to_team", "veth1_to_team"}; + const char* devmasters[] = {"bridge", "bond", "team"}; + + for (i = 0; i < sizeof(devtypes) / (sizeof(devtypes[0])); i++) + execute_command(0, "ip link add dev %s0 type %s", devtypes[i], devtypes[i]); + execute_command(0, "ip link add type veth"); + + for (i = 0; i < sizeof(devmasters) / (sizeof(devmasters[0])); i++) { + execute_command(0, "ip link add name %s_slave_0 type veth peer name veth0_to_%s", devmasters[i], devmasters[i]); + execute_command(0, "ip link add name %s_slave_1 type veth peer name veth1_to_%s", devmasters[i], devmasters[i]); + execute_command(0, "ip link set %s_slave_0 master %s0", devmasters[i], devmasters[i]); + execute_command(0, "ip link set %s_slave_1 master %s0", devmasters[i], devmasters[i]); + execute_command(0, "ip link set veth0_to_%s up", devmasters[i]); + execute_command(0, "ip link set veth1_to_%s up", devmasters[i]); + } + execute_command(0, "ip link set bridge_slave_0 up"); + execute_command(0, "ip link set bridge_slave_1 up"); + + for (i = 0; i < sizeof(devnames) / (sizeof(devnames[0])); i++) { + char addr[32]; + snprintf_check(addr, sizeof(addr), DEV_IPV4, i + 10); + execute_command(0, "ip -4 addr add %s/24 dev %s", addr, devnames[i]); + snprintf_check(addr, sizeof(addr), DEV_IPV6, i + 10); + execute_command(0, "ip -6 addr add %s/120 dev %s", addr, devnames[i]); + snprintf_check(addr, sizeof(addr), DEV_MAC, i + 10); + execute_command(0, "ip link set dev %s address %s", devnames[i], addr); + execute_command(0, "ip link set dev %s up", devnames[i]); + } +} +#endif + +#if SYZ_EXECUTOR || SYZ_TUN_ENABLE && (__NR_syz_extract_tcp_res || SYZ_REPEAT) +#include + +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; + fail("tun: read failed with %d", rv); + } + return rv; +} +#endif + +#if SYZ_EXECUTOR || __NR_syz_emit_ethernet && SYZ_TUN_ENABLE +#include +#include + +#define MAX_FRAGS 4 +struct vnet_fragmentation { + uint32 full; + uint32 count; + uint32 frags[MAX_FRAGS]; +}; + +static long syz_emit_ethernet(long a0, long a1, long a2) +{ + if (tunfd < 0) + return (uintptr_t)-1; + + uint32 length = a0; + char* data = (char*)a1; + debug_dump_data(data, length); + + struct vnet_fragmentation* frags = (struct vnet_fragmentation*)a2; + struct iovec vecs[MAX_FRAGS + 1]; + uint32 nfrags = 0; + if (!tun_frags_enabled || frags == NULL) { + vecs[nfrags].iov_base = data; + vecs[nfrags].iov_len = length; + nfrags++; + } else { + bool full = true; + uint32 i, count = 0; + NONFAILING(full = frags->full); + NONFAILING(count = frags->count); + if (count > MAX_FRAGS) + count = MAX_FRAGS; + for (i = 0; i < count && length != 0; i++) { + uint32 size = 0; + NONFAILING(size = frags->frags[i]); + if (size > length) + size = length; + vecs[nfrags].iov_base = data; + vecs[nfrags].iov_len = size; + nfrags++; + data += size; + length -= size; + } + if (length != 0 && (full || nfrags == 0)) { + vecs[nfrags].iov_base = data; + vecs[nfrags].iov_len = length; + nfrags++; + } + } + return writev(tunfd, vecs, nfrags); +} +#endif + +#if SYZ_EXECUTOR || SYZ_REPEAT && SYZ_TUN_ENABLE +static void flush_tun() +{ +#if SYZ_EXECUTOR + if (!flag_enable_tun) + return; +#endif + char data[SYZ_TUN_MAX_PACKET_SIZE]; + while (read_tun(&data[0], sizeof(data)) != -1) { + } +} +#endif + +#if SYZ_EXECUTOR || __NR_syz_extract_tcp_res && SYZ_TUN_ENABLE +#ifndef __ANDROID__ +struct ipv6hdr { + __u8 priority : 4, + version : 4; + __u8 flow_lbl[3]; + + __be16 payload_len; + __u8 nexthdr; + __u8 hop_limit; + + struct in6_addr saddr; + struct in6_addr daddr; +}; +#endif + +struct tcp_resources { + uint32 seq; + uint32 ack; +}; + +static long syz_extract_tcp_res(long a0, long a1, long a2) +{ + + if (tunfd < 0) + return (uintptr_t)-1; + + char data[SYZ_TUN_MAX_PACKET_SIZE]; + int rv = read_tun(&data[0], sizeof(data)); + if (rv == -1) + return (uintptr_t)-1; + size_t length = rv; + debug_dump_data(data, length); + + struct tcphdr* tcphdr; + + if (length < sizeof(struct ethhdr)) + return (uintptr_t)-1; + struct ethhdr* ethhdr = (struct ethhdr*)&data[0]; + + if (ethhdr->h_proto == htons(ETH_P_IP)) { + if (length < sizeof(struct ethhdr) + sizeof(struct iphdr)) + return (uintptr_t)-1; + struct iphdr* iphdr = (struct iphdr*)&data[sizeof(struct ethhdr)]; + if (iphdr->protocol != IPPROTO_TCP) + return (uintptr_t)-1; + if (length < sizeof(struct ethhdr) + iphdr->ihl * 4 + sizeof(struct tcphdr)) + return (uintptr_t)-1; + tcphdr = (struct tcphdr*)&data[sizeof(struct ethhdr) + iphdr->ihl * 4]; + } else { + if (length < sizeof(struct ethhdr) + sizeof(struct ipv6hdr)) + return (uintptr_t)-1; + struct ipv6hdr* ipv6hdr = (struct ipv6hdr*)&data[sizeof(struct ethhdr)]; + if (ipv6hdr->nexthdr != IPPROTO_TCP) + return (uintptr_t)-1; + if (length < sizeof(struct ethhdr) + sizeof(struct ipv6hdr) + sizeof(struct tcphdr)) + return (uintptr_t)-1; + tcphdr = (struct tcphdr*)&data[sizeof(struct ethhdr) + sizeof(struct ipv6hdr)]; + } + + struct tcp_resources* res = (struct tcp_resources*)a0; + NONFAILING(res->seq = htonl((ntohl(tcphdr->seq) + (uint32)a1))); + NONFAILING(res->ack = htonl((ntohl(tcphdr->ack_seq) + (uint32)a2))); + + debug("extracted seq: %08x\n", res->seq); + debug("extracted ack: %08x\n", res->ack); + + return 0; +} +#endif + +#if SYZ_EXECUTOR || __NR_syz_open_dev +#include +#include +#include +#include + +static long syz_open_dev(long a0, long a1, long a2) +{ + if (a0 == 0xc || a0 == 0xb) { + char buf[128]; + sprintf(buf, "/dev/%s/%d:%d", a0 == 0xc ? "char" : "block", (uint8)a1, (uint8)a2); + return open(buf, O_RDWR, 0); + } else { + char buf[1024]; + char* hash; + NONFAILING(strncpy(buf, (char*)a0, sizeof(buf) - 1)); + buf[sizeof(buf) - 1] = 0; + while ((hash = strchr(buf, '#'))) { + *hash = '0' + (char)(a1 % 10); + a1 /= 10; + } + return open(buf, a2, 0); + } +} +#endif + +#if SYZ_EXECUTOR || __NR_syz_open_procfs +#include +#include +#include +#include + +static long syz_open_procfs(long a0, long a1) +{ + + char buf[128]; + memset(buf, 0, sizeof(buf)); + if (a0 == 0) { + NONFAILING(snprintf(buf, sizeof(buf), "/proc/self/%s", (char*)a1)); + } else if (a0 == -1) { + NONFAILING(snprintf(buf, sizeof(buf), "/proc/thread-self/%s", (char*)a1)); + } else { + NONFAILING(snprintf(buf, sizeof(buf), "/proc/self/task/%d/%s", (int)a0, (char*)a1)); + } + int fd = open(buf, O_RDWR); + if (fd == -1) + fd = open(buf, O_RDONLY); + return fd; +} +#endif + +#if SYZ_EXECUTOR || __NR_syz_open_pts +#include +#include +#include +#include + +static long syz_open_pts(long a0, long a1) +{ + int ptyno = 0; + if (ioctl(a0, TIOCGPTN, &ptyno)) + return -1; + char buf[128]; + sprintf(buf, "/dev/pts/%d", ptyno); + return open(buf, a1, 0); +} +#endif + +#if SYZ_EXECUTOR || __NR_syz_init_net_socket +#if SYZ_EXECUTOR || SYZ_SANDBOX_NONE || SYZ_SANDBOX_SETUID || SYZ_SANDBOX_NAMESPACE +#include +#include +#include +#include +#include + +const int kInitNetNsFd = 239; +static long syz_init_net_socket(long domain, long type, long proto) +{ + int netns = open("/proc/self/ns/net", O_RDONLY); + if (netns == -1) + return netns; + if (setns(kInitNetNsFd, 0)) + return -1; + int sock = syscall(__NR_socket, domain, type, proto); + int err = errno; + if (setns(netns, 0)) + fail("setns(netns) failed"); + close(netns); + errno = err; + return sock; +} +#else +static long syz_init_net_socket(long domain, long type, long proto) +{ + return syscall(__NR_socket, domain, type, proto); +} +#endif +#endif + +#if SYZ_EXECUTOR || __NR_syz_genetlink_get_family_id +#include +#include +#include +#include +#include + +static long syz_genetlink_get_family_id(long name) +{ + char buf[512] = {0}; + struct nlmsghdr* hdr = (struct nlmsghdr*)buf; + struct genlmsghdr* genlhdr = (struct genlmsghdr*)NLMSG_DATA(hdr); + struct nlattr* attr = (struct nlattr*)(genlhdr + 1); + hdr->nlmsg_len = sizeof(*hdr) + sizeof(*genlhdr) + sizeof(*attr) + GENL_NAMSIZ; + hdr->nlmsg_type = GENL_ID_CTRL; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + genlhdr->cmd = CTRL_CMD_GETFAMILY; + attr->nla_type = CTRL_ATTR_FAMILY_NAME; + attr->nla_len = sizeof(*attr) + GENL_NAMSIZ; + NONFAILING(strncpy((char*)(attr + 1), (char*)name, GENL_NAMSIZ)); + struct iovec iov = {hdr, hdr->nlmsg_len}; + struct sockaddr_nl addr = {0}; + addr.nl_family = AF_NETLINK; + debug("syz_genetlink_get_family_id(%s)\n", (char*)(attr + 1)); + int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (fd == -1) { + debug("syz_genetlink_get_family_id: socket failed: %d\n", errno); + return -1; + } + struct msghdr msg = {&addr, sizeof(addr), &iov, 1, NULL, 0, 0}; + if (sendmsg(fd, &msg, 0) == -1) { + debug("syz_genetlink_get_family_id: sendmsg failed: %d\n", errno); + close(fd); + return -1; + } + ssize_t n = recv(fd, buf, sizeof(buf), 0); + close(fd); + if (n <= 0) { + debug("syz_genetlink_get_family_id: recv failed: %d\n", errno); + return -1; + } + if (hdr->nlmsg_type != GENL_ID_CTRL) { + debug("syz_genetlink_get_family_id: wrong reply type: %d\n", hdr->nlmsg_type); + return -1; + } + for (; (char*)attr < buf + n; attr = (struct nlattr*)((char*)attr + NLMSG_ALIGN(attr->nla_len))) { + if (attr->nla_type == CTRL_ATTR_FAMILY_ID) + return *(uint16*)(attr + 1); + } + debug("syz_genetlink_get_family_id: no CTRL_ATTR_FAMILY_ID attr\n"); + return -1; +} +#endif + +#if SYZ_EXECUTOR || __NR_syz_mount_image || __NR_syz_read_part_table +#include +#include +#include +#include +#include +#include + +extern unsigned long long procid; + +struct fs_image_segment { + void* data; + uintptr_t size; + uintptr_t offset; +}; + +#define IMAGE_MAX_SEGMENTS 4096 +#define IMAGE_MAX_SIZE (129 << 20) + +#if GOARCH_386 +#define SYZ_memfd_create 356 +#elif GOARCH_amd64 +#define SYZ_memfd_create 319 +#elif GOARCH_arm +#define SYZ_memfd_create 385 +#elif GOARCH_arm64 +#define SYZ_memfd_create 279 +#elif GOARCH_ppc64le +#define SYZ_memfd_create 360 +#endif +#endif + +#if SYZ_EXECUTOR || __NR_syz_read_part_table +static long syz_read_part_table(unsigned long size, unsigned long nsegs, long segments) +{ + char loopname[64], linkname[64]; + int loopfd, err = 0, res = -1; + unsigned long i, j; + struct fs_image_segment* segs = (struct fs_image_segment*)segments; + + if (nsegs > IMAGE_MAX_SEGMENTS) + nsegs = IMAGE_MAX_SEGMENTS; + for (i = 0; i < nsegs; i++) { + if (segs[i].size > IMAGE_MAX_SIZE) + segs[i].size = IMAGE_MAX_SIZE; + segs[i].offset %= IMAGE_MAX_SIZE; + if (segs[i].offset > IMAGE_MAX_SIZE - segs[i].size) + segs[i].offset = IMAGE_MAX_SIZE - segs[i].size; + if (size < segs[i].offset + segs[i].offset) + size = segs[i].offset + segs[i].offset; + } + if (size > IMAGE_MAX_SIZE) + size = IMAGE_MAX_SIZE; + int memfd = syscall(SYZ_memfd_create, "syz_read_part_table", 0); + if (memfd == -1) { + err = errno; + goto error; + } + if (ftruncate(memfd, size)) { + err = errno; + goto error_close_memfd; + } + for (i = 0; i < nsegs; i++) { + if (pwrite(memfd, segs[i].data, segs[i].size, segs[i].offset) < 0) { + debug("syz_read_part_table: pwrite[%u] failed: %d\n", (int)i, errno); + } + } + snprintf(loopname, sizeof(loopname), "/dev/loop%llu", procid); + loopfd = open(loopname, O_RDWR); + if (loopfd == -1) { + err = errno; + goto error_close_memfd; + } + if (ioctl(loopfd, LOOP_SET_FD, memfd)) { + if (errno != EBUSY) { + err = errno; + goto error_close_loop; + } + ioctl(loopfd, LOOP_CLR_FD, 0); + usleep(1000); + if (ioctl(loopfd, LOOP_SET_FD, memfd)) { + err = errno; + goto error_close_loop; + } + } + struct loop_info64 info; + if (ioctl(loopfd, LOOP_GET_STATUS64, &info)) { + err = errno; + goto error_clear_loop; + } +#if SYZ_EXECUTOR + cover_reset(0); +#endif + info.lo_flags |= LO_FLAGS_PARTSCAN; + if (ioctl(loopfd, LOOP_SET_STATUS64, &info)) { + err = errno; + goto error_clear_loop; + } + res = 0; + for (i = 1, j = 0; i < 8; i++) { + snprintf(loopname, sizeof(loopname), "/dev/loop%llup%d", procid, (int)i); + struct stat statbuf; + if (stat(loopname, &statbuf) == 0) { + snprintf(linkname, sizeof(linkname), "./file%d", (int)j++); + if (symlink(loopname, linkname)) { + debug("syz_read_part_table: symlink(%s, %s) failed: %d\n", loopname, linkname, errno); + } + } + } +error_clear_loop: + ioctl(loopfd, LOOP_CLR_FD, 0); +error_close_loop: + close(loopfd); +error_close_memfd: + close(memfd); +error: + errno = err; + return res; +} +#endif + +#if SYZ_EXECUTOR || __NR_syz_mount_image +static long syz_mount_image(long fsarg, long dir, unsigned long size, unsigned long nsegs, long segments, long flags, long optsarg) +{ + char loopname[64], fs[32], opts[256]; + int loopfd, err = 0, res = -1; + unsigned long i; + struct fs_image_segment* segs = (struct fs_image_segment*)segments; + + if (nsegs > IMAGE_MAX_SEGMENTS) + nsegs = IMAGE_MAX_SEGMENTS; + for (i = 0; i < nsegs; i++) { + if (segs[i].size > IMAGE_MAX_SIZE) + segs[i].size = IMAGE_MAX_SIZE; + segs[i].offset %= IMAGE_MAX_SIZE; + if (segs[i].offset > IMAGE_MAX_SIZE - segs[i].size) + segs[i].offset = IMAGE_MAX_SIZE - segs[i].size; + if (size < segs[i].offset + segs[i].offset) + size = segs[i].offset + segs[i].offset; + } + if (size > IMAGE_MAX_SIZE) + size = IMAGE_MAX_SIZE; + int memfd = syscall(SYZ_memfd_create, "syz_mount_image", 0); + if (memfd == -1) { + err = errno; + goto error; + } + if (ftruncate(memfd, size)) { + err = errno; + goto error_close_memfd; + } + for (i = 0; i < nsegs; i++) { + if (pwrite(memfd, segs[i].data, segs[i].size, segs[i].offset) < 0) { + debug("syz_mount_image: pwrite[%u] failed: %d\n", (int)i, errno); + } + } + snprintf(loopname, sizeof(loopname), "/dev/loop%llu", procid); + loopfd = open(loopname, O_RDWR); + if (loopfd == -1) { + err = errno; + goto error_close_memfd; + } + if (ioctl(loopfd, LOOP_SET_FD, memfd)) { + if (errno != EBUSY) { + err = errno; + goto error_close_loop; + } + ioctl(loopfd, LOOP_CLR_FD, 0); + usleep(1000); + if (ioctl(loopfd, LOOP_SET_FD, memfd)) { + err = errno; + goto error_close_loop; + } + } + mkdir((char*)dir, 0777); + memset(fs, 0, sizeof(fs)); + NONFAILING(strncpy(fs, (char*)fsarg, sizeof(fs) - 1)); + memset(opts, 0, sizeof(opts)); + NONFAILING(strncpy(opts, (char*)optsarg, sizeof(opts) - 32)); + if (strcmp(fs, "iso9660") == 0) { + flags |= MS_RDONLY; + } else if (strncmp(fs, "ext", 3) == 0) { + if (strstr(opts, "errors=panic") || strstr(opts, "errors=remount-ro") == 0) + strcat(opts, ",errors=continue"); + } else if (strcmp(fs, "xfs") == 0) { + strcat(opts, ",nouuid"); + } + debug("syz_mount_image: size=%llu segs=%llu loop='%s' dir='%s' fs='%s' flags=%llu opts='%s'\n", (uint64)size, (uint64)nsegs, loopname, (char*)dir, fs, (uint64)flags, opts); +#if SYZ_EXECUTOR + cover_reset(0); +#endif + if (mount(loopname, (char*)dir, fs, flags, opts)) { + err = errno; + goto error_clear_loop; + } + res = 0; +error_clear_loop: + ioctl(loopfd, LOOP_CLR_FD, 0); +error_close_loop: + close(loopfd); +error_close_memfd: + close(memfd); +error: + errno = err; + return res; +} +#endif + +#if SYZ_EXECUTOR || __NR_syz_kvm_setup_cpu +#include +#include +#include +#include +#include +#include +#include + +#if defined(__x86_64__) + + + +const char kvm_asm16_cpl3[] = "\x0f\x20\xc0\x66\x83\xc8\x01\x0f\x22\xc0\xb8\xa0\x00\x0f\x00\xd8\xb8\x2b\x00\x8e\xd8\x8e\xc0\x8e\xe0\x8e\xe8\xbc\x00\x01\xc7\x06\x00\x01\x1d\xba\xc7\x06\x02\x01\x23\x00\xc7\x06\x04\x01\x00\x01\xc7\x06\x06\x01\x2b\x00\xcb"; +const char kvm_asm32_paged[] = "\x0f\x20\xc0\x0d\x00\x00\x00\x80\x0f\x22\xc0"; +const char kvm_asm32_vm86[] = "\x66\xb8\xb8\x00\x0f\x00\xd8\xea\x00\x00\x00\x00\xd0\x00"; +const char kvm_asm32_paged_vm86[] = "\x0f\x20\xc0\x0d\x00\x00\x00\x80\x0f\x22\xc0\x66\xb8\xb8\x00\x0f\x00\xd8\xea\x00\x00\x00\x00\xd0\x00"; +const char kvm_asm64_vm86[] = "\x0f\x20\xc0\x0d\x00\x00\x00\x80\x0f\x22\xc0\x66\xb8\xb8\x00\x0f\x00\xd8\xea\x00\x00\x00\x00\xd0\x00"; +const char kvm_asm64_enable_long[] = "\x0f\x20\xc0\x0d\x00\x00\x00\x80\x0f\x22\xc0\xea\xde\xc0\xad\x0b\x50\x00\x48\xc7\xc0\xd8\x00\x00\x00\x0f\x00\xd8"; +const char kvm_asm64_init_vm[] = "\x0f\x20\xc0\x0d\x00\x00\x00\x80\x0f\x22\xc0\xea\xde\xc0\xad\x0b\x50\x00\x48\xc7\xc0\xd8\x00\x00\x00\x0f\x00\xd8\x48\xc7\xc1\x3a\x00\x00\x00\x0f\x32\x48\x83\xc8\x05\x0f\x30\x0f\x20\xe0\x48\x0d\x00\x20\x00\x00\x0f\x22\xe0\x48\xc7\xc1\x80\x04\x00\x00\x0f\x32\x48\xc7\xc2\x00\x60\x00\x00\x89\x02\x48\xc7\xc2\x00\x70\x00\x00\x89\x02\x48\xc7\xc0\x00\x5f\x00\x00\xf3\x0f\xc7\x30\x48\xc7\xc0\x08\x5f\x00\x00\x66\x0f\xc7\x30\x0f\xc7\x30\x48\xc7\xc1\x81\x04\x00\x00\x0f\x32\x48\x83\xc8\x3f\x48\x21\xd0\x48\xc7\xc2\x00\x40\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x02\x40\x00\x00\x48\xb8\x84\x9e\x99\xf3\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x1e\x40\x00\x00\x48\xc7\xc0\x81\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc1\x83\x04\x00\x00\x0f\x32\x48\x0d\xff\x6f\x03\x00\x48\x21\xd0\x48\xc7\xc2\x0c\x40\x00\x00\x0f\x79\xd0\x48\xc7\xc1\x84\x04\x00\x00\x0f\x32\x48\x0d\xff\x17\x00\x00\x48\x21\xd0\x48\xc7\xc2\x12\x40\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x04\x2c\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x00\x28\x00\x00\x48\xc7\xc0\xff\xff\xff\xff\x0f\x79\xd0\x48\xc7\xc2\x02\x0c\x00\x00\x48\xc7\xc0\x50\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc0\x58\x00\x00\x00\x48\xc7\xc2\x00\x0c\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x04\x0c\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x06\x0c\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x08\x0c\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0a\x0c\x00\x00\x0f\x79\xd0\x48\xc7\xc0\xd8\x00\x00\x00\x48\xc7\xc2\x0c\x0c\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x02\x2c\x00\x00\x48\xc7\xc0\x00\x05\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x00\x4c\x00\x00\x48\xc7\xc0\x50\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x10\x6c\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x12\x6c\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x0f\x20\xc0\x48\xc7\xc2\x00\x6c\x00\x00\x48\x89\xc0\x0f\x79\xd0\x0f\x20\xd8\x48\xc7\xc2\x02\x6c\x00\x00\x48\x89\xc0\x0f\x79\xd0\x0f\x20\xe0\x48\xc7\xc2\x04\x6c\x00\x00\x48\x89\xc0\x0f\x79\xd0\x48\xc7\xc2\x06\x6c\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x08\x6c\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0a\x6c\x00\x00\x48\xc7\xc0\x00\x3a\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0c\x6c\x00\x00\x48\xc7\xc0\x00\x10\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0e\x6c\x00\x00\x48\xc7\xc0\x00\x38\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x14\x6c\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x16\x6c\x00\x00\x48\x8b\x04\x25\x10\x5f\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x00\x00\x00\x00\x48\xc7\xc0\x01\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x02\x00\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x00\x20\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x02\x20\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x04\x20\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x06\x20\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc1\x77\x02\x00\x00\x0f\x32\x48\xc1\xe2\x20\x48\x09\xd0\x48\xc7\xc2\x00\x2c\x00\x00\x48\x89\xc0\x0f\x79\xd0\x48\xc7\xc2\x04\x40\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0a\x40\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0e\x40\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x10\x40\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x16\x40\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x14\x40\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x00\x60\x00\x00\x48\xc7\xc0\xff\xff\xff\xff\x0f\x79\xd0\x48\xc7\xc2\x02\x60\x00\x00\x48\xc7\xc0\xff\xff\xff\xff\x0f\x79\xd0\x48\xc7\xc2\x1c\x20\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x1e\x20\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x20\x20\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x22\x20\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x00\x08\x00\x00\x48\xc7\xc0\x58\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x02\x08\x00\x00\x48\xc7\xc0\x50\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x04\x08\x00\x00\x48\xc7\xc0\x58\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x06\x08\x00\x00\x48\xc7\xc0\x58\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x08\x08\x00\x00\x48\xc7\xc0\x58\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0a\x08\x00\x00\x48\xc7\xc0\x58\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0c\x08\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0e\x08\x00\x00\x48\xc7\xc0\xd8\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x12\x68\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x14\x68\x00\x00\x48\xc7\xc0\x00\x3a\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x16\x68\x00\x00\x48\xc7\xc0\x00\x10\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x18\x68\x00\x00\x48\xc7\xc0\x00\x38\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x00\x48\x00\x00\x48\xc7\xc0\xff\xff\x0f\x00\x0f\x79\xd0\x48\xc7\xc2\x02\x48\x00\x00\x48\xc7\xc0\xff\xff\x0f\x00\x0f\x79\xd0\x48\xc7\xc2\x04\x48\x00\x00\x48\xc7\xc0\xff\xff\x0f\x00\x0f\x79\xd0\x48\xc7\xc2\x06\x48\x00\x00\x48\xc7\xc0\xff\xff\x0f\x00\x0f\x79\xd0\x48\xc7\xc2\x08\x48\x00\x00\x48\xc7\xc0\xff\xff\x0f\x00\x0f\x79\xd0\x48\xc7\xc2\x0a\x48\x00\x00\x48\xc7\xc0\xff\xff\x0f\x00\x0f\x79\xd0\x48\xc7\xc2\x0c\x48\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0e\x48\x00\x00\x48\xc7\xc0\xff\x1f\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x10\x48\x00\x00\x48\xc7\xc0\xff\x1f\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x12\x48\x00\x00\x48\xc7\xc0\xff\x1f\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x14\x48\x00\x00\x48\xc7\xc0\x93\x40\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x16\x48\x00\x00\x48\xc7\xc0\x9b\x20\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x18\x48\x00\x00\x48\xc7\xc0\x93\x40\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x1a\x48\x00\x00\x48\xc7\xc0\x93\x40\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x1c\x48\x00\x00\x48\xc7\xc0\x93\x40\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x1e\x48\x00\x00\x48\xc7\xc0\x93\x40\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x20\x48\x00\x00\x48\xc7\xc0\x82\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x22\x48\x00\x00\x48\xc7\xc0\x8b\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x1c\x68\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x1e\x68\x00\x00\x48\xc7\xc0\x00\x91\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x20\x68\x00\x00\x48\xc7\xc0\x02\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x06\x28\x00\x00\x48\xc7\xc0\x00\x05\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0a\x28\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0c\x28\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x0e\x28\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x48\xc7\xc2\x10\x28\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x79\xd0\x0f\x20\xc0\x48\xc7\xc2\x00\x68\x00\x00\x48\x89\xc0\x0f\x79\xd0\x0f\x20\xd8\x48\xc7\xc2\x02\x68\x00\x00\x48\x89\xc0\x0f\x79\xd0\x0f\x20\xe0\x48\xc7\xc2\x04\x68\x00\x00\x48\x89\xc0\x0f\x79\xd0\x48\xc7\xc0\x18\x5f\x00\x00\x48\x8b\x10\x48\xc7\xc0\x20\x5f\x00\x00\x48\x8b\x08\x48\x31\xc0\x0f\x78\xd0\x48\x31\xc8\x0f\x79\xd0\x0f\x01\xc2\x48\xc7\xc2\x00\x44\x00\x00\x0f\x78\xd0\xf4"; +const char kvm_asm64_vm_exit[] = "\x48\xc7\xc3\x00\x44\x00\x00\x0f\x78\xda\x48\xc7\xc3\x02\x44\x00\x00\x0f\x78\xd9\x48\xc7\xc0\x00\x64\x00\x00\x0f\x78\xc0\x48\xc7\xc3\x1e\x68\x00\x00\x0f\x78\xdb\xf4"; +const char kvm_asm64_cpl3[] = "\x0f\x20\xc0\x0d\x00\x00\x00\x80\x0f\x22\xc0\xea\xde\xc0\xad\x0b\x50\x00\x48\xc7\xc0\xd8\x00\x00\x00\x0f\x00\xd8\x48\xc7\xc0\x6b\x00\x00\x00\x8e\xd8\x8e\xc0\x8e\xe0\x8e\xe8\x48\xc7\xc4\x80\x0f\x00\x00\x48\xc7\x04\x24\x1d\xba\x00\x00\x48\xc7\x44\x24\x04\x63\x00\x00\x00\x48\xc7\x44\x24\x08\x80\x0f\x00\x00\x48\xc7\x44\x24\x0c\x6b\x00\x00\x00\xcb"; + +#define ADDR_TEXT 0x0000 +#define ADDR_GDT 0x1000 +#define ADDR_LDT 0x1800 +#define ADDR_PML4 0x2000 +#define ADDR_PDP 0x3000 +#define ADDR_PD 0x4000 +#define ADDR_STACK0 0x0f80 +#define ADDR_VAR_HLT 0x2800 +#define ADDR_VAR_SYSRET 0x2808 +#define ADDR_VAR_SYSEXIT 0x2810 +#define ADDR_VAR_IDT 0x3800 +#define ADDR_VAR_TSS64 0x3a00 +#define ADDR_VAR_TSS64_CPL3 0x3c00 +#define ADDR_VAR_TSS16 0x3d00 +#define ADDR_VAR_TSS16_2 0x3e00 +#define ADDR_VAR_TSS16_CPL3 0x3f00 +#define ADDR_VAR_TSS32 0x4800 +#define ADDR_VAR_TSS32_2 0x4a00 +#define ADDR_VAR_TSS32_CPL3 0x4c00 +#define ADDR_VAR_TSS32_VM86 0x4e00 +#define ADDR_VAR_VMXON_PTR 0x5f00 +#define ADDR_VAR_VMCS_PTR 0x5f08 +#define ADDR_VAR_VMEXIT_PTR 0x5f10 +#define ADDR_VAR_VMWRITE_FLD 0x5f18 +#define ADDR_VAR_VMWRITE_VAL 0x5f20 +#define ADDR_VAR_VMXON 0x6000 +#define ADDR_VAR_VMCS 0x7000 +#define ADDR_VAR_VMEXIT_CODE 0x9000 +#define ADDR_VAR_USER_CODE 0x9100 +#define ADDR_VAR_USER_CODE2 0x9120 + +#define SEL_LDT (1 << 3) +#define SEL_CS16 (2 << 3) +#define SEL_DS16 (3 << 3) +#define SEL_CS16_CPL3 ((4 << 3) + 3) +#define SEL_DS16_CPL3 ((5 << 3) + 3) +#define SEL_CS32 (6 << 3) +#define SEL_DS32 (7 << 3) +#define SEL_CS32_CPL3 ((8 << 3) + 3) +#define SEL_DS32_CPL3 ((9 << 3) + 3) +#define SEL_CS64 (10 << 3) +#define SEL_DS64 (11 << 3) +#define SEL_CS64_CPL3 ((12 << 3) + 3) +#define SEL_DS64_CPL3 ((13 << 3) + 3) +#define SEL_CGATE16 (14 << 3) +#define SEL_TGATE16 (15 << 3) +#define SEL_CGATE32 (16 << 3) +#define SEL_TGATE32 (17 << 3) +#define SEL_CGATE64 (18 << 3) +#define SEL_CGATE64_HI (19 << 3) +#define SEL_TSS16 (20 << 3) +#define SEL_TSS16_2 (21 << 3) +#define SEL_TSS16_CPL3 ((22 << 3) + 3) +#define SEL_TSS32 (23 << 3) +#define SEL_TSS32_2 (24 << 3) +#define SEL_TSS32_CPL3 ((25 << 3) + 3) +#define SEL_TSS32_VM86 (26 << 3) +#define SEL_TSS64 (27 << 3) +#define SEL_TSS64_HI (28 << 3) +#define SEL_TSS64_CPL3 ((29 << 3) + 3) +#define SEL_TSS64_CPL3_HI (30 << 3) + +#define MSR_IA32_FEATURE_CONTROL 0x3a +#define MSR_IA32_VMX_BASIC 0x480 +#define MSR_IA32_SMBASE 0x9e +#define MSR_IA32_SYSENTER_CS 0x174 +#define MSR_IA32_SYSENTER_ESP 0x175 +#define MSR_IA32_SYSENTER_EIP 0x176 +#define MSR_IA32_STAR 0xC0000081 +#define MSR_IA32_LSTAR 0xC0000082 +#define MSR_IA32_VMX_PROCBASED_CTLS2 0x48B + +#define NEXT_INSN $0xbadc0de +#define PREFIX_SIZE 0xba1d + +#ifndef KVM_SMI +#define KVM_SMI _IO(KVMIO, 0xb7) +#endif + +#define CR0_PE 1 +#define CR0_MP (1 << 1) +#define CR0_EM (1 << 2) +#define CR0_TS (1 << 3) +#define CR0_ET (1 << 4) +#define CR0_NE (1 << 5) +#define CR0_WP (1 << 16) +#define CR0_AM (1 << 18) +#define CR0_NW (1 << 29) +#define CR0_CD (1 << 30) +#define CR0_PG (1 << 31) + +#define CR4_VME 1 +#define CR4_PVI (1 << 1) +#define CR4_TSD (1 << 2) +#define CR4_DE (1 << 3) +#define CR4_PSE (1 << 4) +#define CR4_PAE (1 << 5) +#define CR4_MCE (1 << 6) +#define CR4_PGE (1 << 7) +#define CR4_PCE (1 << 8) +#define CR4_OSFXSR (1 << 8) +#define CR4_OSXMMEXCPT (1 << 10) +#define CR4_UMIP (1 << 11) +#define CR4_VMXE (1 << 13) +#define CR4_SMXE (1 << 14) +#define CR4_FSGSBASE (1 << 16) +#define CR4_PCIDE (1 << 17) +#define CR4_OSXSAVE (1 << 18) +#define CR4_SMEP (1 << 20) +#define CR4_SMAP (1 << 21) +#define CR4_PKE (1 << 22) + +#define EFER_SCE 1 +#define EFER_LME (1 << 8) +#define EFER_LMA (1 << 10) +#define EFER_NXE (1 << 11) +#define EFER_SVME (1 << 12) +#define EFER_LMSLE (1 << 13) +#define EFER_FFXSR (1 << 14) +#define EFER_TCE (1 << 15) + +#define PDE32_PRESENT 1 +#define PDE32_RW (1 << 1) +#define PDE32_USER (1 << 2) +#define PDE32_PS (1 << 7) + +#define PDE64_PRESENT 1 +#define PDE64_RW (1 << 1) +#define PDE64_USER (1 << 2) +#define PDE64_ACCESSED (1 << 5) +#define PDE64_DIRTY (1 << 6) +#define PDE64_PS (1 << 7) +#define PDE64_G (1 << 8) + +struct tss16 { + uint16 prev; + uint16 sp0; + uint16 ss0; + uint16 sp1; + uint16 ss1; + uint16 sp2; + uint16 ss2; + uint16 ip; + uint16 flags; + uint16 ax; + uint16 cx; + uint16 dx; + uint16 bx; + uint16 sp; + uint16 bp; + uint16 si; + uint16 di; + uint16 es; + uint16 cs; + uint16 ss; + uint16 ds; + uint16 ldt; +} __attribute__((packed)); + +struct tss32 { + uint16 prev, prevh; + uint32 sp0; + uint16 ss0, ss0h; + uint32 sp1; + uint16 ss1, ss1h; + uint32 sp2; + uint16 ss2, ss2h; + uint32 cr3; + uint32 ip; + uint32 flags; + uint32 ax; + uint32 cx; + uint32 dx; + uint32 bx; + uint32 sp; + uint32 bp; + uint32 si; + uint32 di; + uint16 es, esh; + uint16 cs, csh; + uint16 ss, ssh; + uint16 ds, dsh; + uint16 fs, fsh; + uint16 gs, gsh; + uint16 ldt, ldth; + uint16 trace; + uint16 io_bitmap; +} __attribute__((packed)); + +struct tss64 { + uint32 reserved0; + uint64 rsp[3]; + uint64 reserved1; + uint64 ist[7]; + uint64 reserved2; + uint32 reserved3; + uint32 io_bitmap; +} __attribute__((packed)); + +static void fill_segment_descriptor(uint64* dt, uint64* lt, struct kvm_segment* seg) +{ + uint16 index = seg->selector >> 3; + uint64 limit = seg->g ? seg->limit >> 12 : seg->limit; + uint64 sd = (limit & 0xffff) | (seg->base & 0xffffff) << 16 | (uint64)seg->type << 40 | (uint64)seg->s << 44 | (uint64)seg->dpl << 45 | (uint64)seg->present << 47 | (limit & 0xf0000ULL) << 48 | (uint64)seg->avl << 52 | (uint64)seg->l << 53 | (uint64)seg->db << 54 | (uint64)seg->g << 55 | (seg->base & 0xff000000ULL) << 56; + NONFAILING(dt[index] = sd); + NONFAILING(lt[index] = sd); +} + +static void fill_segment_descriptor_dword(uint64* dt, uint64* lt, struct kvm_segment* seg) +{ + fill_segment_descriptor(dt, lt, seg); + uint16 index = seg->selector >> 3; + NONFAILING(dt[index + 1] = 0); + NONFAILING(lt[index + 1] = 0); +} + +static void setup_syscall_msrs(int cpufd, uint16 sel_cs, uint16 sel_cs_cpl3) +{ + char buf[sizeof(struct kvm_msrs) + 5 * sizeof(struct kvm_msr_entry)]; + memset(buf, 0, sizeof(buf)); + struct kvm_msrs* msrs = (struct kvm_msrs*)buf; + struct kvm_msr_entry* entries = msrs->entries; + msrs->nmsrs = 5; + entries[0].index = MSR_IA32_SYSENTER_CS; + entries[0].data = sel_cs; + entries[1].index = MSR_IA32_SYSENTER_ESP; + entries[1].data = ADDR_STACK0; + entries[2].index = MSR_IA32_SYSENTER_EIP; + entries[2].data = ADDR_VAR_SYSEXIT; + entries[3].index = MSR_IA32_STAR; + entries[3].data = ((uint64)sel_cs << 32) | ((uint64)sel_cs_cpl3 << 48); + entries[4].index = MSR_IA32_LSTAR; + entries[4].data = ADDR_VAR_SYSRET; + ioctl(cpufd, KVM_SET_MSRS, msrs); +} + +static void setup_32bit_idt(struct kvm_sregs* sregs, char* host_mem, uintptr_t guest_mem) +{ + sregs->idt.base = guest_mem + ADDR_VAR_IDT; + sregs->idt.limit = 0x1ff; + uint64* idt = (uint64*)(host_mem + sregs->idt.base); + int i; + for (i = 0; i < 32; i++) { + struct kvm_segment gate; + gate.selector = i << 3; + switch (i % 6) { + case 0: + gate.type = 6; + gate.base = SEL_CS16; + break; + case 1: + gate.type = 7; + gate.base = SEL_CS16; + break; + case 2: + gate.type = 3; + gate.base = SEL_TGATE16; + break; + case 3: + gate.type = 14; + gate.base = SEL_CS32; + break; + case 4: + gate.type = 15; + gate.base = SEL_CS32; + break; + case 6: + gate.type = 11; + gate.base = SEL_TGATE32; + break; + } + gate.limit = guest_mem + ADDR_VAR_USER_CODE2; + gate.present = 1; + gate.dpl = 0; + gate.s = 0; + gate.g = 0; + gate.db = 0; + gate.l = 0; + gate.avl = 0; + fill_segment_descriptor(idt, idt, &gate); + } +} + +static void setup_64bit_idt(struct kvm_sregs* sregs, char* host_mem, uintptr_t guest_mem) +{ + sregs->idt.base = guest_mem + ADDR_VAR_IDT; + sregs->idt.limit = 0x1ff; + uint64* idt = (uint64*)(host_mem + sregs->idt.base); + int i; + for (i = 0; i < 32; i++) { + struct kvm_segment gate; + gate.selector = (i * 2) << 3; + gate.type = (i & 1) ? 14 : 15; + gate.base = SEL_CS64; + gate.limit = guest_mem + ADDR_VAR_USER_CODE2; + gate.present = 1; + gate.dpl = 0; + gate.s = 0; + gate.g = 0; + gate.db = 0; + gate.l = 0; + gate.avl = 0; + fill_segment_descriptor_dword(idt, idt, &gate); + } +} + +struct kvm_text { + uintptr_t typ; + const void* text; + uintptr_t size; +}; + +struct kvm_opt { + uint64 typ; + uint64 val; +}; + +#define KVM_SETUP_PAGING (1 << 0) +#define KVM_SETUP_PAE (1 << 1) +#define KVM_SETUP_PROTECTED (1 << 2) +#define KVM_SETUP_CPL3 (1 << 3) +#define KVM_SETUP_VIRT86 (1 << 4) +#define KVM_SETUP_SMM (1 << 5) +#define KVM_SETUP_VM (1 << 6) + +static uintptr_t syz_kvm_setup_cpu(uintptr_t a0, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7) +{ + const int vmfd = a0; + const int cpufd = a1; + char* const host_mem = (char*)a2; + const struct kvm_text* const text_array_ptr = (struct kvm_text*)a3; + const uintptr_t text_count = a4; + const uintptr_t flags = a5; + const struct kvm_opt* const opt_array_ptr = (struct kvm_opt*)a6; + uintptr_t opt_count = a7; + + const uintptr_t page_size = 4 << 10; + const uintptr_t ioapic_page = 10; + const uintptr_t guest_mem_size = 24 * page_size; + const uintptr_t guest_mem = 0; + + (void)text_count; + int text_type = 0; + const void* text = 0; + uintptr_t text_size = 0; + NONFAILING(text_type = text_array_ptr[0].typ); + NONFAILING(text = text_array_ptr[0].text); + NONFAILING(text_size = text_array_ptr[0].size); + + uintptr_t i; + for (i = 0; i < guest_mem_size / page_size; i++) { + struct kvm_userspace_memory_region memreg; + memreg.slot = i; + memreg.flags = 0; + memreg.guest_phys_addr = guest_mem + i * page_size; + if (i == ioapic_page) + memreg.guest_phys_addr = 0xfec00000; + memreg.memory_size = page_size; + memreg.userspace_addr = (uintptr_t)host_mem + i * page_size; + ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &memreg); + } + struct kvm_userspace_memory_region memreg; + memreg.slot = 1 + (1 << 16); + memreg.flags = 0; + memreg.guest_phys_addr = 0x30000; + memreg.memory_size = 64 << 10; + memreg.userspace_addr = (uintptr_t)host_mem; + ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &memreg); + + struct kvm_sregs sregs; + if (ioctl(cpufd, KVM_GET_SREGS, &sregs)) + return -1; + + struct kvm_regs regs; + memset(®s, 0, sizeof(regs)); + regs.rip = guest_mem + ADDR_TEXT; + regs.rsp = ADDR_STACK0; + + sregs.gdt.base = guest_mem + ADDR_GDT; + sregs.gdt.limit = 256 * sizeof(uint64) - 1; + uint64* gdt = (uint64*)(host_mem + sregs.gdt.base); + + struct kvm_segment seg_ldt; + seg_ldt.selector = SEL_LDT; + seg_ldt.type = 2; + seg_ldt.base = guest_mem + ADDR_LDT; + seg_ldt.limit = 256 * sizeof(uint64) - 1; + seg_ldt.present = 1; + seg_ldt.dpl = 0; + seg_ldt.s = 0; + seg_ldt.g = 0; + seg_ldt.db = 1; + seg_ldt.l = 0; + sregs.ldt = seg_ldt; + uint64* ldt = (uint64*)(host_mem + sregs.ldt.base); + + struct kvm_segment seg_cs16; + seg_cs16.selector = SEL_CS16; + seg_cs16.type = 11; + seg_cs16.base = 0; + seg_cs16.limit = 0xfffff; + seg_cs16.present = 1; + seg_cs16.dpl = 0; + seg_cs16.s = 1; + seg_cs16.g = 0; + seg_cs16.db = 0; + seg_cs16.l = 0; + + struct kvm_segment seg_ds16 = seg_cs16; + seg_ds16.selector = SEL_DS16; + seg_ds16.type = 3; + + struct kvm_segment seg_cs16_cpl3 = seg_cs16; + seg_cs16_cpl3.selector = SEL_CS16_CPL3; + seg_cs16_cpl3.dpl = 3; + + struct kvm_segment seg_ds16_cpl3 = seg_ds16; + seg_ds16_cpl3.selector = SEL_DS16_CPL3; + seg_ds16_cpl3.dpl = 3; + + struct kvm_segment seg_cs32 = seg_cs16; + seg_cs32.selector = SEL_CS32; + seg_cs32.db = 1; + + struct kvm_segment seg_ds32 = seg_ds16; + seg_ds32.selector = SEL_DS32; + seg_ds32.db = 1; + + struct kvm_segment seg_cs32_cpl3 = seg_cs32; + seg_cs32_cpl3.selector = SEL_CS32_CPL3; + seg_cs32_cpl3.dpl = 3; + + struct kvm_segment seg_ds32_cpl3 = seg_ds32; + seg_ds32_cpl3.selector = SEL_DS32_CPL3; + seg_ds32_cpl3.dpl = 3; + + struct kvm_segment seg_cs64 = seg_cs16; + seg_cs64.selector = SEL_CS64; + seg_cs64.l = 1; + + struct kvm_segment seg_ds64 = seg_ds32; + seg_ds64.selector = SEL_DS64; + + struct kvm_segment seg_cs64_cpl3 = seg_cs64; + seg_cs64_cpl3.selector = SEL_CS64_CPL3; + seg_cs64_cpl3.dpl = 3; + + struct kvm_segment seg_ds64_cpl3 = seg_ds64; + seg_ds64_cpl3.selector = SEL_DS64_CPL3; + seg_ds64_cpl3.dpl = 3; + + struct kvm_segment seg_tss32; + seg_tss32.selector = SEL_TSS32; + seg_tss32.type = 9; + seg_tss32.base = ADDR_VAR_TSS32; + seg_tss32.limit = 0x1ff; + seg_tss32.present = 1; + seg_tss32.dpl = 0; + seg_tss32.s = 0; + seg_tss32.g = 0; + seg_tss32.db = 0; + seg_tss32.l = 0; + + struct kvm_segment seg_tss32_2 = seg_tss32; + seg_tss32_2.selector = SEL_TSS32_2; + seg_tss32_2.base = ADDR_VAR_TSS32_2; + + struct kvm_segment seg_tss32_cpl3 = seg_tss32; + seg_tss32_cpl3.selector = SEL_TSS32_CPL3; + seg_tss32_cpl3.base = ADDR_VAR_TSS32_CPL3; + + struct kvm_segment seg_tss32_vm86 = seg_tss32; + seg_tss32_vm86.selector = SEL_TSS32_VM86; + seg_tss32_vm86.base = ADDR_VAR_TSS32_VM86; + + struct kvm_segment seg_tss16 = seg_tss32; + seg_tss16.selector = SEL_TSS16; + seg_tss16.base = ADDR_VAR_TSS16; + seg_tss16.limit = 0xff; + seg_tss16.type = 1; + + struct kvm_segment seg_tss16_2 = seg_tss16; + seg_tss16_2.selector = SEL_TSS16_2; + seg_tss16_2.base = ADDR_VAR_TSS16_2; + seg_tss16_2.dpl = 0; + + struct kvm_segment seg_tss16_cpl3 = seg_tss16; + seg_tss16_cpl3.selector = SEL_TSS16_CPL3; + seg_tss16_cpl3.base = ADDR_VAR_TSS16_CPL3; + seg_tss16_cpl3.dpl = 3; + + struct kvm_segment seg_tss64 = seg_tss32; + seg_tss64.selector = SEL_TSS64; + seg_tss64.base = ADDR_VAR_TSS64; + seg_tss64.limit = 0x1ff; + + struct kvm_segment seg_tss64_cpl3 = seg_tss64; + seg_tss64_cpl3.selector = SEL_TSS64_CPL3; + seg_tss64_cpl3.base = ADDR_VAR_TSS64_CPL3; + seg_tss64_cpl3.dpl = 3; + + struct kvm_segment seg_cgate16; + seg_cgate16.selector = SEL_CGATE16; + seg_cgate16.type = 4; + seg_cgate16.base = SEL_CS16 | (2 << 16); + seg_cgate16.limit = ADDR_VAR_USER_CODE2; + seg_cgate16.present = 1; + seg_cgate16.dpl = 0; + seg_cgate16.s = 0; + seg_cgate16.g = 0; + seg_cgate16.db = 0; + seg_cgate16.l = 0; + seg_cgate16.avl = 0; + + struct kvm_segment seg_tgate16 = seg_cgate16; + seg_tgate16.selector = SEL_TGATE16; + seg_tgate16.type = 3; + seg_cgate16.base = SEL_TSS16_2; + seg_tgate16.limit = 0; + + struct kvm_segment seg_cgate32 = seg_cgate16; + seg_cgate32.selector = SEL_CGATE32; + seg_cgate32.type = 12; + seg_cgate32.base = SEL_CS32 | (2 << 16); + + struct kvm_segment seg_tgate32 = seg_cgate32; + seg_tgate32.selector = SEL_TGATE32; + seg_tgate32.type = 11; + seg_tgate32.base = SEL_TSS32_2; + seg_tgate32.limit = 0; + + struct kvm_segment seg_cgate64 = seg_cgate16; + seg_cgate64.selector = SEL_CGATE64; + seg_cgate64.type = 12; + seg_cgate64.base = SEL_CS64; + + int kvmfd = open("/dev/kvm", O_RDWR); + char buf[sizeof(struct kvm_cpuid2) + 128 * sizeof(struct kvm_cpuid_entry2)]; + memset(buf, 0, sizeof(buf)); + struct kvm_cpuid2* cpuid = (struct kvm_cpuid2*)buf; + cpuid->nent = 128; + ioctl(kvmfd, KVM_GET_SUPPORTED_CPUID, cpuid); + ioctl(cpufd, KVM_SET_CPUID2, cpuid); + close(kvmfd); + + const char* text_prefix = 0; + int text_prefix_size = 0; + char* host_text = host_mem + ADDR_TEXT; + + if (text_type == 8) { + if (flags & KVM_SETUP_SMM) { + if (flags & KVM_SETUP_PROTECTED) { + sregs.cs = seg_cs16; + sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds16; + sregs.cr0 |= CR0_PE; + } else { + sregs.cs.selector = 0; + sregs.cs.base = 0; + } + + NONFAILING(*(host_mem + ADDR_TEXT) = 0xf4); + host_text = host_mem + 0x8000; + + ioctl(cpufd, KVM_SMI, 0); + } else if (flags & KVM_SETUP_VIRT86) { + sregs.cs = seg_cs32; + sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds32; + sregs.cr0 |= CR0_PE; + sregs.efer |= EFER_SCE; + + setup_syscall_msrs(cpufd, SEL_CS32, SEL_CS32_CPL3); + setup_32bit_idt(&sregs, host_mem, guest_mem); + + if (flags & KVM_SETUP_PAGING) { + uint64 pd_addr = guest_mem + ADDR_PD; + uint64* pd = (uint64*)(host_mem + ADDR_PD); + NONFAILING(pd[0] = PDE32_PRESENT | PDE32_RW | PDE32_USER | PDE32_PS); + sregs.cr3 = pd_addr; + sregs.cr4 |= CR4_PSE; + + text_prefix = kvm_asm32_paged_vm86; + text_prefix_size = sizeof(kvm_asm32_paged_vm86) - 1; + } else { + text_prefix = kvm_asm32_vm86; + text_prefix_size = sizeof(kvm_asm32_vm86) - 1; + } + } else { + sregs.cs.selector = 0; + sregs.cs.base = 0; + } + } else if (text_type == 16) { + if (flags & KVM_SETUP_CPL3) { + sregs.cs = seg_cs16; + sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds16; + + text_prefix = kvm_asm16_cpl3; + text_prefix_size = sizeof(kvm_asm16_cpl3) - 1; + } else { + sregs.cr0 |= CR0_PE; + sregs.cs = seg_cs16; + sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds16; + } + } else if (text_type == 32) { + sregs.cr0 |= CR0_PE; + sregs.efer |= EFER_SCE; + + setup_syscall_msrs(cpufd, SEL_CS32, SEL_CS32_CPL3); + setup_32bit_idt(&sregs, host_mem, guest_mem); + + if (flags & KVM_SETUP_SMM) { + sregs.cs = seg_cs32; + sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds32; + + NONFAILING(*(host_mem + ADDR_TEXT) = 0xf4); + host_text = host_mem + 0x8000; + + ioctl(cpufd, KVM_SMI, 0); + } else if (flags & KVM_SETUP_PAGING) { + sregs.cs = seg_cs32; + sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds32; + + uint64 pd_addr = guest_mem + ADDR_PD; + uint64* pd = (uint64*)(host_mem + ADDR_PD); + NONFAILING(pd[0] = PDE32_PRESENT | PDE32_RW | PDE32_USER | PDE32_PS); + sregs.cr3 = pd_addr; + sregs.cr4 |= CR4_PSE; + + text_prefix = kvm_asm32_paged; + text_prefix_size = sizeof(kvm_asm32_paged) - 1; + } else if (flags & KVM_SETUP_CPL3) { + sregs.cs = seg_cs32_cpl3; + sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds32_cpl3; + } else { + sregs.cs = seg_cs32; + sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds32; + } + } else { + sregs.efer |= EFER_LME | EFER_SCE; + sregs.cr0 |= CR0_PE; + + setup_syscall_msrs(cpufd, SEL_CS64, SEL_CS64_CPL3); + setup_64bit_idt(&sregs, host_mem, guest_mem); + + sregs.cs = seg_cs32; + sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds32; + + uint64 pml4_addr = guest_mem + ADDR_PML4; + uint64* pml4 = (uint64*)(host_mem + ADDR_PML4); + uint64 pdpt_addr = guest_mem + ADDR_PDP; + uint64* pdpt = (uint64*)(host_mem + ADDR_PDP); + uint64 pd_addr = guest_mem + ADDR_PD; + uint64* pd = (uint64*)(host_mem + ADDR_PD); + NONFAILING(pml4[0] = PDE64_PRESENT | PDE64_RW | PDE64_USER | pdpt_addr); + NONFAILING(pdpt[0] = PDE64_PRESENT | PDE64_RW | PDE64_USER | pd_addr); + NONFAILING(pd[0] = PDE64_PRESENT | PDE64_RW | PDE64_USER | PDE64_PS); + sregs.cr3 = pml4_addr; + sregs.cr4 |= CR4_PAE; + + if (flags & KVM_SETUP_VM) { + sregs.cr0 |= CR0_NE; + + NONFAILING(*((uint64*)(host_mem + ADDR_VAR_VMXON_PTR)) = ADDR_VAR_VMXON); + NONFAILING(*((uint64*)(host_mem + ADDR_VAR_VMCS_PTR)) = ADDR_VAR_VMCS); + NONFAILING(memcpy(host_mem + ADDR_VAR_VMEXIT_CODE, kvm_asm64_vm_exit, sizeof(kvm_asm64_vm_exit) - 1)); + NONFAILING(*((uint64*)(host_mem + ADDR_VAR_VMEXIT_PTR)) = ADDR_VAR_VMEXIT_CODE); + + text_prefix = kvm_asm64_init_vm; + text_prefix_size = sizeof(kvm_asm64_init_vm) - 1; + } else if (flags & KVM_SETUP_CPL3) { + text_prefix = kvm_asm64_cpl3; + text_prefix_size = sizeof(kvm_asm64_cpl3) - 1; + } else { + text_prefix = kvm_asm64_enable_long; + text_prefix_size = sizeof(kvm_asm64_enable_long) - 1; + } + } + + struct tss16 tss16; + memset(&tss16, 0, sizeof(tss16)); + tss16.ss0 = tss16.ss1 = tss16.ss2 = SEL_DS16; + tss16.sp0 = tss16.sp1 = tss16.sp2 = ADDR_STACK0; + tss16.ip = ADDR_VAR_USER_CODE2; + tss16.flags = (1 << 1); + tss16.cs = SEL_CS16; + tss16.es = tss16.ds = tss16.ss = SEL_DS16; + tss16.ldt = SEL_LDT; + struct tss16* tss16_addr = (struct tss16*)(host_mem + seg_tss16_2.base); + NONFAILING(memcpy(tss16_addr, &tss16, sizeof(tss16))); + + memset(&tss16, 0, sizeof(tss16)); + tss16.ss0 = tss16.ss1 = tss16.ss2 = SEL_DS16; + tss16.sp0 = tss16.sp1 = tss16.sp2 = ADDR_STACK0; + tss16.ip = ADDR_VAR_USER_CODE2; + tss16.flags = (1 << 1); + tss16.cs = SEL_CS16_CPL3; + tss16.es = tss16.ds = tss16.ss = SEL_DS16_CPL3; + tss16.ldt = SEL_LDT; + struct tss16* tss16_cpl3_addr = (struct tss16*)(host_mem + seg_tss16_cpl3.base); + NONFAILING(memcpy(tss16_cpl3_addr, &tss16, sizeof(tss16))); + + struct tss32 tss32; + memset(&tss32, 0, sizeof(tss32)); + tss32.ss0 = tss32.ss1 = tss32.ss2 = SEL_DS32; + tss32.sp0 = tss32.sp1 = tss32.sp2 = ADDR_STACK0; + tss32.ip = ADDR_VAR_USER_CODE; + tss32.flags = (1 << 1) | (1 << 17); + tss32.ldt = SEL_LDT; + tss32.cr3 = sregs.cr3; + tss32.io_bitmap = offsetof(struct tss32, io_bitmap); + struct tss32* tss32_addr = (struct tss32*)(host_mem + seg_tss32_vm86.base); + NONFAILING(memcpy(tss32_addr, &tss32, sizeof(tss32))); + + memset(&tss32, 0, sizeof(tss32)); + tss32.ss0 = tss32.ss1 = tss32.ss2 = SEL_DS32; + tss32.sp0 = tss32.sp1 = tss32.sp2 = ADDR_STACK0; + tss32.ip = ADDR_VAR_USER_CODE; + tss32.flags = (1 << 1); + tss32.cr3 = sregs.cr3; + tss32.es = tss32.ds = tss32.ss = tss32.gs = tss32.fs = SEL_DS32; + tss32.cs = SEL_CS32; + tss32.ldt = SEL_LDT; + tss32.cr3 = sregs.cr3; + tss32.io_bitmap = offsetof(struct tss32, io_bitmap); + struct tss32* tss32_cpl3_addr = (struct tss32*)(host_mem + seg_tss32_2.base); + NONFAILING(memcpy(tss32_cpl3_addr, &tss32, sizeof(tss32))); + + struct tss64 tss64; + memset(&tss64, 0, sizeof(tss64)); + tss64.rsp[0] = ADDR_STACK0; + tss64.rsp[1] = ADDR_STACK0; + tss64.rsp[2] = ADDR_STACK0; + tss64.io_bitmap = offsetof(struct tss64, io_bitmap); + struct tss64* tss64_addr = (struct tss64*)(host_mem + seg_tss64.base); + NONFAILING(memcpy(tss64_addr, &tss64, sizeof(tss64))); + + memset(&tss64, 0, sizeof(tss64)); + tss64.rsp[0] = ADDR_STACK0; + tss64.rsp[1] = ADDR_STACK0; + tss64.rsp[2] = ADDR_STACK0; + tss64.io_bitmap = offsetof(struct tss64, io_bitmap); + struct tss64* tss64_cpl3_addr = (struct tss64*)(host_mem + seg_tss64_cpl3.base); + NONFAILING(memcpy(tss64_cpl3_addr, &tss64, sizeof(tss64))); + + if (text_size > 1000) + text_size = 1000; + if (text_prefix) { + NONFAILING(memcpy(host_text, text_prefix, text_prefix_size)); + void* patch = 0; + NONFAILING(patch = memmem(host_text, text_prefix_size, "\xde\xc0\xad\x0b", 4)); + if (patch) + NONFAILING(*((uint32*)patch) = guest_mem + ADDR_TEXT + ((char*)patch - host_text) + 6); + uint16 magic = PREFIX_SIZE; + patch = 0; + NONFAILING(patch = memmem(host_text, text_prefix_size, &magic, sizeof(magic))); + if (patch) + NONFAILING(*((uint16*)patch) = guest_mem + ADDR_TEXT + text_prefix_size); + } + NONFAILING(memcpy((void*)(host_text + text_prefix_size), text, text_size)); + NONFAILING(*(host_text + text_prefix_size + text_size) = 0xf4); + + NONFAILING(memcpy(host_mem + ADDR_VAR_USER_CODE, text, text_size)); + NONFAILING(*(host_mem + ADDR_VAR_USER_CODE + text_size) = 0xf4); + + NONFAILING(*(host_mem + ADDR_VAR_HLT) = 0xf4); + NONFAILING(memcpy(host_mem + ADDR_VAR_SYSRET, "\x0f\x07\xf4", 3)); + NONFAILING(memcpy(host_mem + ADDR_VAR_SYSEXIT, "\x0f\x35\xf4", 3)); + + NONFAILING(*(uint64*)(host_mem + ADDR_VAR_VMWRITE_FLD) = 0); + NONFAILING(*(uint64*)(host_mem + ADDR_VAR_VMWRITE_VAL) = 0); + + if (opt_count > 2) + opt_count = 2; + for (i = 0; i < opt_count; i++) { + uint64 typ = 0; + uint64 val = 0; + NONFAILING(typ = opt_array_ptr[i].typ); + NONFAILING(val = opt_array_ptr[i].val); + switch (typ % 9) { + case 0: + sregs.cr0 ^= val & (CR0_MP | CR0_EM | CR0_ET | CR0_NE | CR0_WP | CR0_AM | CR0_NW | CR0_CD); + break; + case 1: + sregs.cr4 ^= val & (CR4_VME | CR4_PVI | CR4_TSD | CR4_DE | CR4_MCE | CR4_PGE | CR4_PCE | + CR4_OSFXSR | CR4_OSXMMEXCPT | CR4_UMIP | CR4_VMXE | CR4_SMXE | CR4_FSGSBASE | CR4_PCIDE | + CR4_OSXSAVE | CR4_SMEP | CR4_SMAP | CR4_PKE); + break; + case 2: + sregs.efer ^= val & (EFER_SCE | EFER_NXE | EFER_SVME | EFER_LMSLE | EFER_FFXSR | EFER_TCE); + break; + case 3: + val &= ((1 << 8) | (1 << 9) | (1 << 10) | (1 << 12) | (1 << 13) | (1 << 14) | + (1 << 15) | (1 << 18) | (1 << 19) | (1 << 20) | (1 << 21)); + regs.rflags ^= val; + NONFAILING(tss16_addr->flags ^= val); + NONFAILING(tss16_cpl3_addr->flags ^= val); + NONFAILING(tss32_addr->flags ^= val); + NONFAILING(tss32_cpl3_addr->flags ^= val); + break; + case 4: + seg_cs16.type = val & 0xf; + seg_cs32.type = val & 0xf; + seg_cs64.type = val & 0xf; + break; + case 5: + seg_cs16_cpl3.type = val & 0xf; + seg_cs32_cpl3.type = val & 0xf; + seg_cs64_cpl3.type = val & 0xf; + break; + case 6: + seg_ds16.type = val & 0xf; + seg_ds32.type = val & 0xf; + seg_ds64.type = val & 0xf; + break; + case 7: + seg_ds16_cpl3.type = val & 0xf; + seg_ds32_cpl3.type = val & 0xf; + seg_ds64_cpl3.type = val & 0xf; + break; + case 8: + NONFAILING(*(uint64*)(host_mem + ADDR_VAR_VMWRITE_FLD) = (val & 0xffff)); + NONFAILING(*(uint64*)(host_mem + ADDR_VAR_VMWRITE_VAL) = (val >> 16)); + break; + default: + fail("bad kvm setup opt"); + } + } + regs.rflags |= 2; + + fill_segment_descriptor(gdt, ldt, &seg_ldt); + fill_segment_descriptor(gdt, ldt, &seg_cs16); + fill_segment_descriptor(gdt, ldt, &seg_ds16); + fill_segment_descriptor(gdt, ldt, &seg_cs16_cpl3); + fill_segment_descriptor(gdt, ldt, &seg_ds16_cpl3); + fill_segment_descriptor(gdt, ldt, &seg_cs32); + fill_segment_descriptor(gdt, ldt, &seg_ds32); + fill_segment_descriptor(gdt, ldt, &seg_cs32_cpl3); + fill_segment_descriptor(gdt, ldt, &seg_ds32_cpl3); + fill_segment_descriptor(gdt, ldt, &seg_cs64); + fill_segment_descriptor(gdt, ldt, &seg_ds64); + fill_segment_descriptor(gdt, ldt, &seg_cs64_cpl3); + fill_segment_descriptor(gdt, ldt, &seg_ds64_cpl3); + fill_segment_descriptor(gdt, ldt, &seg_tss32); + fill_segment_descriptor(gdt, ldt, &seg_tss32_2); + fill_segment_descriptor(gdt, ldt, &seg_tss32_cpl3); + fill_segment_descriptor(gdt, ldt, &seg_tss32_vm86); + fill_segment_descriptor(gdt, ldt, &seg_tss16); + fill_segment_descriptor(gdt, ldt, &seg_tss16_2); + fill_segment_descriptor(gdt, ldt, &seg_tss16_cpl3); + fill_segment_descriptor_dword(gdt, ldt, &seg_tss64); + fill_segment_descriptor_dword(gdt, ldt, &seg_tss64_cpl3); + fill_segment_descriptor(gdt, ldt, &seg_cgate16); + fill_segment_descriptor(gdt, ldt, &seg_tgate16); + fill_segment_descriptor(gdt, ldt, &seg_cgate32); + fill_segment_descriptor(gdt, ldt, &seg_tgate32); + fill_segment_descriptor_dword(gdt, ldt, &seg_cgate64); + + if (ioctl(cpufd, KVM_SET_SREGS, &sregs)) + return -1; + if (ioctl(cpufd, KVM_SET_REGS, ®s)) + return -1; + return 0; +} +#elif defined(__aarch64__) + + + +struct kvm_text { + uintptr_t typ; + const void* text; + uintptr_t size; +}; + +struct kvm_opt { + uint64 typ; + uint64 val; +}; + +static uintptr_t syz_kvm_setup_cpu(uintptr_t a0, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7) +{ + const int vmfd = a0; + const int cpufd = a1; + char* const host_mem = (char*)a2; + const struct kvm_text* const text_array_ptr = (struct kvm_text*)a3; + const uintptr_t text_count = a4; + const uintptr_t flags = a5; + const struct kvm_opt* const opt_array_ptr = (struct kvm_opt*)a6; + uintptr_t opt_count = a7; + + (void)flags; + (void)opt_count; + + const uintptr_t page_size = 4 << 10; + const uintptr_t guest_mem = 0; + const uintptr_t guest_mem_size = 24 * page_size; + + (void)text_count; + int text_type = 0; + const void* text = 0; + int text_size = 0; + NONFAILING(text_type = text_array_ptr[0].typ); + NONFAILING(text = text_array_ptr[0].text); + NONFAILING(text_size = text_array_ptr[0].size); + (void)text_type; + (void)opt_array_ptr; + + uint32 features = 0; + if (opt_count > 1) + opt_count = 1; + uintptr_t i; + for (i = 0; i < opt_count; i++) { + uint64 typ = 0; + uint64 val = 0; + NONFAILING(typ = opt_array_ptr[i].typ); + NONFAILING(val = opt_array_ptr[i].val); + switch (typ) { + case 1: + features = val; + break; + } + } + + for (i = 0; i < guest_mem_size / page_size; i++) { + struct kvm_userspace_memory_region memreg; + memreg.slot = i; + memreg.flags = 0; + memreg.guest_phys_addr = guest_mem + i * page_size; + memreg.memory_size = page_size; + memreg.userspace_addr = (uintptr_t)host_mem + i * page_size; + ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &memreg); + } + + struct kvm_vcpu_init init; + ioctl(cpufd, KVM_ARM_PREFERRED_TARGET, &init); + init.features[0] = features; + ioctl(cpufd, KVM_ARM_VCPU_INIT, &init); + + if (text_size > 1000) + text_size = 1000; + NONFAILING(memcpy(host_mem, text, text_size)); + + return 0; +} +#else +static long syz_kvm_setup_cpu(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7) +{ + return 0; +} +#endif +#endif + +#if SYZ_EXECUTOR || SYZ_FAULT_INJECTION || SYZ_SANDBOX_NAMESPACE || SYZ_ENABLE_CGROUPS +#include +#include +#include +#include +#include +#include + +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; +} +#endif + +#if SYZ_EXECUTOR || SYZ_RESET_NET_NAMESPACE +#include +#include +#include +#include + + +#define XT_TABLE_SIZE 1536 +#define XT_MAX_ENTRIES 10 + +struct xt_counters { + uint64 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; + void* entrytable[XT_TABLE_SIZE / sizeof(void*)]; +}; + +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; + char entrytable[XT_TABLE_SIZE]; +}; + +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; + void* entrytable[XT_TABLE_SIZE / sizeof(void*)]; +}; + +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; + char entrytable[XT_TABLE_SIZE]; +}; + +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) +{ + struct ipt_get_entries entries; + socklen_t optlen; + int fd, i; + + fd = socket(family, SOCK_STREAM, IPPROTO_TCP); + if (fd == -1) { + switch (errno) { + case EAFNOSUPPORT: + case ENOPROTOOPT: + return; + } + fail("socket(%d, SOCK_STREAM, IPPROTO_TCP)", family); + } + for (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); + optlen = sizeof(table->info); + if (getsockopt(fd, level, IPT_SO_GET_INFO, &table->info, &optlen)) { + switch (errno) { + case EPERM: + case ENOENT: + case ENOPROTOOPT: + continue; + } + fail("getsockopt(IPT_SO_GET_INFO)"); + } + debug("checkpoint iptable %s/%d: entries=%d hooks=%x size=%d\n", table->name, family, table->info.num_entries, table->info.valid_hooks, table->info.size); + if (table->info.size > sizeof(table->replace.entrytable)) + fail("table size is too large: %u", table->info.size); + if (table->info.num_entries > XT_MAX_ENTRIES) + fail("too many counters: %u", table->info.num_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)) + fail("getsockopt(IPT_SO_GET_ENTRIES)"); + 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) +{ + struct xt_counters counters[XT_MAX_ENTRIES]; + struct ipt_get_entries entries; + struct ipt_getinfo info; + socklen_t optlen; + int fd, i; + + fd = socket(family, SOCK_STREAM, IPPROTO_TCP); + if (fd == -1) { + switch (errno) { + case EAFNOSUPPORT: + case ENOPROTOOPT: + return; + } + fail("socket(%d, SOCK_STREAM, IPPROTO_TCP)", family); + } + for (i = 0; i < num_tables; i++) { + struct ipt_table_desc* table = &tables[i]; + if (table->info.valid_hooks == 0) + continue; + memset(&info, 0, sizeof(info)); + strcpy(info.name, table->name); + optlen = sizeof(info); + if (getsockopt(fd, level, IPT_SO_GET_INFO, &info, &optlen)) + fail("getsockopt(IPT_SO_GET_INFO)"); + if (memcmp(&table->info, &info, sizeof(table->info)) == 0) { + 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)) + fail("getsockopt(IPT_SO_GET_ENTRIES)"); + if (memcmp(table->replace.entrytable, entries.entrytable, table->info.size) == 0) + continue; + } + debug("resetting iptable %s\n", table->name); + 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)) + fail("setsockopt(IPT_SO_SET_REPLACE)"); + } + close(fd); +} + +static void checkpoint_arptables(void) +{ + struct arpt_get_entries entries; + socklen_t optlen; + unsigned i; + int fd; + + fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (fd == -1) { + switch (errno) { + case EAFNOSUPPORT: + case ENOPROTOOPT: + return; + } + fail("socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)"); + } + for (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); + 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; + } + fail("getsockopt(ARPT_SO_GET_INFO)"); + } + debug("checkpoint arptable %s: entries=%d hooks=%x size=%d\n", table->name, table->info.num_entries, table->info.valid_hooks, table->info.size); + if (table->info.size > sizeof(table->replace.entrytable)) + fail("table size is too large: %u", table->info.size); + if (table->info.num_entries > XT_MAX_ENTRIES) + fail("too many counters: %u", table->info.num_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)) + fail("getsockopt(ARPT_SO_GET_ENTRIES)"); + 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() +{ + struct xt_counters counters[XT_MAX_ENTRIES]; + struct arpt_get_entries entries; + struct arpt_getinfo info; + socklen_t optlen; + unsigned i; + int fd; + + fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (fd == -1) { + switch (errno) { + case EAFNOSUPPORT: + case ENOPROTOOPT: + return; + } + fail("socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)"); + } + for (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; + memset(&info, 0, sizeof(info)); + strcpy(info.name, table->name); + optlen = sizeof(info); + if (getsockopt(fd, SOL_IP, ARPT_SO_GET_INFO, &info, &optlen)) + fail("getsockopt(ARPT_SO_GET_INFO)"); + if (memcmp(&table->info, &info, sizeof(table->info)) == 0) { + 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)) + fail("getsockopt(ARPT_SO_GET_ENTRIES)"); + if (memcmp(table->replace.entrytable, entries.entrytable, table->info.size) == 0) + continue; + } + debug("resetting arptable %s\n", table->name); + 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)) + fail("setsockopt(ARPT_SO_SET_REPLACE)"); + } + close(fd); +} + +#include +#include + +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) +{ + socklen_t optlen; + unsigned i; + int fd; + + fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (fd == -1) { + switch (errno) { + case EAFNOSUPPORT: + case ENOPROTOOPT: + return; + } + fail("socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)"); + } + for (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); + 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; + } + fail("getsockopt(EBT_SO_GET_INIT_INFO)"); + } + debug("checkpoint ebtable %s: entries=%d hooks=%x size=%d\n", table->name, table->replace.nentries, table->replace.valid_hooks, table->replace.entries_size); + if (table->replace.entries_size > sizeof(table->entrytable)) + fail("table size is too large: %u", table->replace.entries_size); + 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)) + fail("getsockopt(EBT_SO_GET_INIT_ENTRIES)"); + } + close(fd); +} + +static void reset_ebtables() +{ + struct ebt_replace replace; + char entrytable[XT_TABLE_SIZE]; + socklen_t optlen; + unsigned i, j, h; + int fd; + + fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (fd == -1) { + switch (errno) { + case EAFNOSUPPORT: + case ENOPROTOOPT: + return; + } + fail("socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)"); + } + for (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; + memset(&replace, 0, sizeof(replace)); + strcpy(replace.name, table->name); + optlen = sizeof(replace); + if (getsockopt(fd, SOL_IP, EBT_SO_GET_INFO, &replace, &optlen)) + fail("getsockopt(EBT_SO_GET_INFO)"); + replace.num_counters = 0; + table->replace.entries = 0; + for (h = 0; h < NF_BR_NUMHOOKS; h++) + table->replace.hook_entry[h] = 0; + if (memcmp(&table->replace, &replace, sizeof(table->replace)) == 0) { + 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)) + fail("getsockopt(EBT_SO_GET_ENTRIES)"); + if (memcmp(table->entrytable, entrytable, replace.entries_size) == 0) + continue; + } + debug("resetting ebtable %s\n", table->name); + for (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)) + fail("setsockopt(EBT_SO_SET_ENTRIES)"); + } + 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); +} +#endif + +#if SYZ_EXECUTOR || SYZ_ENABLE_CGROUPS +#include +#include +#include +#include + +static void setup_cgroups() +{ + if (mkdir("/syzcgroup", 0777)) { + debug("mkdir(/syzcgroup) failed: %d\n", errno); + } + if (mkdir("/syzcgroup/unified", 0777)) { + debug("mkdir(/syzcgroup/unified) failed: %d\n", errno); + } + if (mount("none", "/syzcgroup/unified", "cgroup2", 0, NULL)) { + debug("mount(cgroup2) failed: %d\n", errno); + } + if (chmod("/syzcgroup/unified", 0777)) { + debug("chmod(/syzcgroup/unified) failed: %d\n", errno); + } + if (!write_file("/syzcgroup/unified/cgroup.subtree_control", "+cpu +memory +io +pids +rdma")) { + debug("write(cgroup.subtree_control) failed: %d\n", errno); + } + if (mkdir("/syzcgroup/cpu", 0777)) { + debug("mkdir(/syzcgroup/cpu) failed: %d\n", errno); + } + if (mount("none", "/syzcgroup/cpu", "cgroup", 0, "cpuset,cpuacct,perf_event,hugetlb")) { + debug("mount(cgroup cpu) failed: %d\n", errno); + } + if (!write_file("/syzcgroup/cpu/cgroup.clone_children", "1")) { + debug("write(/syzcgroup/cpu/cgroup.clone_children) failed: %d\n", errno); + } + if (chmod("/syzcgroup/cpu", 0777)) { + debug("chmod(/syzcgroup/cpu) failed: %d\n", errno); + } + if (mkdir("/syzcgroup/net", 0777)) { + debug("mkdir(/syzcgroup/net) failed: %d\n", errno); + } + if (mount("none", "/syzcgroup/net", "cgroup", 0, "net_cls,net_prio,devices,freezer")) { + debug("mount(cgroup net) failed: %d\n", errno); + } + if (chmod("/syzcgroup/net", 0777)) { + debug("chmod(/syzcgroup/net) failed: %d\n", errno); + } +} + +static void setup_binfmt_misc() +{ + if (!write_file("/proc/sys/fs/binfmt_misc/register", ":syz0:M:0:syz0::./file0:")) { + debug("write(/proc/sys/fs/binfmt_misc/register, syz0) failed: %d\n", errno); + } + if (!write_file("/proc/sys/fs/binfmt_misc/register", ":syz1:M:1:yz1::./file0:POC")) { + debug("write(/proc/sys/fs/binfmt_misc/register, syz1) failed: %d\n", errno); + } +} +#endif + +#if SYZ_EXECUTOR || SYZ_SANDBOX_NONE || SYZ_SANDBOX_SETUID || SYZ_SANDBOX_NAMESPACE +static void setup_common() +{ +#if SYZ_EXECUTOR || SYZ_ENABLE_CGROUPS + setup_cgroups(); + setup_binfmt_misc(); +#endif +#if SYZ_EXECUTOR || SYZ_RESET_NET_NAMESPACE + checkpoint_net_namespace(); +#endif +} +#endif + +#if SYZ_EXECUTOR || SYZ_SANDBOX_NONE || SYZ_SANDBOX_SETUID || SYZ_SANDBOX_NAMESPACE +#include +#include +#include +#include + +static void loop(); + +static void sandbox_common() +{ + prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); + setpgrp(); + setsid(); + +#if SYZ_EXECUTOR || __NR_syz_init_net_socket + int netns = open("/proc/self/ns/net", O_RDONLY); + if (netns == -1) + fail("open(/proc/self/ns/net) failed"); + if (dup2(netns, kInitNetNsFd) < 0) + fail("dup2(netns, kInitNetNsFd) failed"); + close(netns); +#endif + + struct rlimit rlim; + rlim.rlim_cur = rlim.rlim_max = 160 << 20; + setrlimit(RLIMIT_AS, &rlim); + rlim.rlim_cur = rlim.rlim_max = 8 << 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)) { + debug("unshare(CLONE_NEWNS): %d\n", errno); + } + if (unshare(CLONE_NEWIPC)) { + debug("unshare(CLONE_NEWIPC): %d\n", errno); + } + if (unshare(0x02000000)) { + debug("unshare(CLONE_NEWCGROUP): %d\n", errno); + } + if (unshare(CLONE_NEWUTS)) { + debug("unshare(CLONE_NEWUTS): %d\n", errno); + } + if (unshare(CLONE_SYSVSEM)) { + debug("unshare(CLONE_SYSVSEM): %d\n", errno); + } +} + +int wait_for_loop(int pid) +{ + if (pid < 0) + fail("sandbox fork failed"); + debug("spawned loop pid %d\n", pid); + int status = 0; + while (waitpid(-1, &status, __WALL) != pid) { + } + return WEXITSTATUS(status); +} +#endif + +#if SYZ_EXECUTOR || SYZ_SANDBOX_NONE +#include +#include + +static int do_sandbox_none(void) +{ + if (unshare(CLONE_NEWPID)) { + debug("unshare(CLONE_NEWPID): %d\n", errno); + } + int pid = fork(); + if (pid != 0) + return wait_for_loop(pid); + + setup_common(); + sandbox_common(); + if (unshare(CLONE_NEWNET)) { + debug("unshare(CLONE_NEWNET): %d\n", errno); + } +#if SYZ_EXECUTOR || SYZ_TUN_ENABLE + initialize_tun(); +#endif +#if SYZ_EXECUTOR || SYZ_ENABLE_NETDEV + initialize_netdevices(); +#endif + loop(); + doexit(1); +} +#endif + +#if SYZ_EXECUTOR || SYZ_SANDBOX_SETUID +#include +#include +#include + +static int do_sandbox_setuid(void) +{ + if (unshare(CLONE_NEWPID)) + fail("unshare(CLONE_NEWPID)"); + int pid = fork(); + if (pid != 0) + return wait_for_loop(pid); + + setup_common(); + sandbox_common(); + if (unshare(CLONE_NEWNET)) + fail("unshare(CLONE_NEWNET)"); +#if SYZ_EXECUTOR || SYZ_TUN_ENABLE + initialize_tun(); +#endif +#if SYZ_EXECUTOR || SYZ_ENABLE_NETDEV + initialize_netdevices(); +#endif + + const int nobody = 65534; + if (setgroups(0, NULL)) + fail("failed to setgroups"); + if (syscall(SYS_setresgid, nobody, nobody, nobody)) + fail("failed to setresgid"); + if (syscall(SYS_setresuid, nobody, nobody, nobody)) + fail("failed to setresuid"); + + prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); + + loop(); + doexit(1); +} +#endif + +#if SYZ_EXECUTOR || SYZ_SANDBOX_NAMESPACE +#include +#include + +static int real_uid; +static int real_gid; +__attribute__((aligned(64 << 10))) static char sandbox_stack[1 << 20]; + +static int namespace_sandbox_proc(void* arg) +{ + sandbox_common(); + + write_file("/proc/self/setgroups", "deny"); + if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)) + fail("write of /proc/self/uid_map failed"); + if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) + fail("write of /proc/self/gid_map failed"); + + if (unshare(CLONE_NEWNET)) + fail("unshare(CLONE_NEWNET)"); +#if SYZ_EXECUTOR || SYZ_TUN_ENABLE + initialize_tun(); +#endif +#if SYZ_EXECUTOR || SYZ_ENABLE_NETDEV + initialize_netdevices(); +#endif + + if (mkdir("./syz-tmp", 0777)) + fail("mkdir(syz-tmp) failed"); + if (mount("", "./syz-tmp", "tmpfs", 0, NULL)) + fail("mount(tmpfs) failed"); + if (mkdir("./syz-tmp/newroot", 0777)) + fail("mkdir failed"); + if (mkdir("./syz-tmp/newroot/dev", 0700)) + fail("mkdir failed"); + unsigned bind_mount_flags = MS_BIND | MS_REC | MS_PRIVATE; + if (mount("/dev", "./syz-tmp/newroot/dev", NULL, bind_mount_flags, NULL)) + fail("mount(dev) failed"); + if (mkdir("./syz-tmp/newroot/proc", 0700)) + fail("mkdir failed"); + if (mount(NULL, "./syz-tmp/newroot/proc", "proc", 0, NULL)) + fail("mount(proc) failed"); + if (mkdir("./syz-tmp/newroot/selinux", 0700)) + fail("mkdir failed"); + const char* selinux_path = "./syz-tmp/newroot/selinux"; + if (mount("/selinux", selinux_path, NULL, bind_mount_flags, NULL)) { + if (errno != ENOENT) + fail("mount(/selinux) failed"); + if (mount("/sys/fs/selinux", selinux_path, NULL, bind_mount_flags, NULL) && errno != ENOENT) + fail("mount(/sys/fs/selinux) failed"); + } + if (mkdir("./syz-tmp/newroot/sys", 0700)) + fail("mkdir failed"); + if (mount("/sys", "./syz-tmp/newroot/sys", 0, bind_mount_flags, NULL)) + fail("mount(sysfs) failed"); +#if SYZ_EXECUTOR || SYZ_ENABLE_CGROUPS + if (mkdir("./syz-tmp/newroot/syzcgroup", 0700)) + fail("mkdir failed"); + if (mkdir("./syz-tmp/newroot/syzcgroup/unified", 0700)) + fail("mkdir failed"); + if (mkdir("./syz-tmp/newroot/syzcgroup/cpu", 0700)) + fail("mkdir failed"); + if (mkdir("./syz-tmp/newroot/syzcgroup/net", 0700)) + fail("mkdir failed"); + if (mount("/syzcgroup/unified", "./syz-tmp/newroot/syzcgroup/unified", NULL, bind_mount_flags, NULL)) { + debug("mount(cgroup2, MS_BIND) failed: %d\n", errno); + } + if (mount("/syzcgroup/cpu", "./syz-tmp/newroot/syzcgroup/cpu", NULL, bind_mount_flags, NULL)) { + debug("mount(cgroup/cpu, MS_BIND) failed: %d\n", errno); + } + if (mount("/syzcgroup/net", "./syz-tmp/newroot/syzcgroup/net", NULL, bind_mount_flags, NULL)) { + debug("mount(cgroup/net, MS_BIND) failed: %d\n", errno); + } +#endif + if (mkdir("./syz-tmp/pivot", 0777)) + fail("mkdir failed"); + if (syscall(SYS_pivot_root, "./syz-tmp", "./syz-tmp/pivot")) { + debug("pivot_root failed\n"); + if (chdir("./syz-tmp")) + fail("chdir failed"); + } else { + debug("pivot_root OK\n"); + if (chdir("/")) + fail("chdir failed"); + if (umount2("./pivot", MNT_DETACH)) + fail("umount failed"); + } + if (chroot("./newroot")) + fail("chroot failed"); + if (chdir("/")) + fail("chdir failed"); + + 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)) + fail("capget failed"); + cap_data[0].effective &= ~(1 << CAP_SYS_PTRACE); + cap_data[0].permitted &= ~(1 << CAP_SYS_PTRACE); + cap_data[0].inheritable &= ~(1 << CAP_SYS_PTRACE); + if (syscall(SYS_capset, &cap_hdr, &cap_data)) + fail("capset failed"); + + loop(); + doexit(1); +} + +static int do_sandbox_namespace(void) +{ + int pid; + + setup_common(); + real_uid = getuid(); + real_gid = getgid(); + mprotect(sandbox_stack, 4096, PROT_NONE); + pid = clone(namespace_sandbox_proc, &sandbox_stack[sizeof(sandbox_stack) - 64], + CLONE_NEWUSER | CLONE_NEWPID, 0); + return wait_for_loop(pid); +} +#endif + +#if SYZ_EXECUTOR || SYZ_REPEAT && SYZ_USE_TMP_DIR +#include +#include + +static void remove_dir(const char* dir) +{ + DIR* dp; + struct dirent* ep; + int iter = 0; +retry: + while (umount2(dir, MNT_DETACH) == 0) { + debug("umount(%s)\n", dir); + } + dp = opendir(dir); + if (dp == NULL) { + if (errno == EMFILE) { + exitf("opendir(%s) failed due to NOFILE, exiting", dir); + } + exitf("opendir(%s) failed", dir); + } + 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) { + debug("umount(%s)\n", filename); + } + struct stat st; + if (lstat(filename, &st)) + exitf("lstat(%s) failed", filename); + if (S_ISDIR(st.st_mode)) { + remove_dir(filename); + continue; + } + int i; + for (i = 0;; i++) { + debug("unlink(%s)\n", filename); + if (unlink(filename) == 0) + break; + if (errno == EROFS) { + debug("ignoring EROFS\n"); + break; + } + if (errno != EBUSY || i > 100) + exitf("unlink(%s) failed", filename); + debug("umount(%s)\n", filename); + if (umount2(filename, MNT_DETACH)) + exitf("umount(%s) failed", filename); + } + } + closedir(dp); + int i; + for (i = 0;; i++) { + debug("rmdir(%s)\n", dir); + if (rmdir(dir) == 0) + break; + if (i < 100) { + if (errno == EROFS) { + debug("ignoring EROFS\n"); + break; + } + if (errno == EBUSY) { + debug("umount(%s)\n", dir); + if (umount2(dir, MNT_DETACH)) + exitf("umount(%s) failed", dir); + continue; + } + if (errno == ENOTEMPTY) { + if (iter < 100) { + iter++; + goto retry; + } + } + } + exitf("rmdir(%s) failed", dir); + } +} +#endif + +#if SYZ_EXECUTOR || SYZ_FAULT_INJECTION +#include +#include +#include + +static int inject_fault(int nth) +{ + int fd; + char buf[16]; + + fd = open("/proc/thread-self/fail-nth", O_RDWR); + if (fd == -1) + exitf("failed to open /proc/thread-self/fail-nth"); + sprintf(buf, "%d", nth + 1); + if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) + exitf("failed to write /proc/thread-self/fail-nth"); + return fd; +} +#endif + +#if SYZ_EXECUTOR +static int fault_injected(int fail_fd) +{ + char buf[16]; + int n = read(fail_fd, buf, sizeof(buf) - 1); + if (n <= 0) + exitf("failed to read /proc/thread-self/fail-nth"); + int res = n == 2 && buf[0] == '0' && buf[1] == '\n'; + buf[0] = '0'; + if (write(fail_fd, buf, 1) != 1) + exitf("failed to write /proc/thread-self/fail-nth"); + close(fail_fd); + return res; +} +#endif + +#if SYZ_EXECUTOR || SYZ_REPEAT +#include +#include +#include +#include +#include +#include + +extern unsigned long long procid; + +static void setup_loop() +{ +#if SYZ_ENABLE_CGROUPS + int pid = getpid(); + char cgroupdir[64]; + char procs_file[128]; + snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/unified/syz%llu", procid); + if (mkdir(cgroupdir, 0777)) { + debug("mkdir(%s) failed: %d\n", cgroupdir, errno); + } + snprintf(procs_file, sizeof(procs_file), "%s/cgroup.procs", cgroupdir); + if (!write_file(procs_file, "%d", pid)) { + debug("write(%s) failed: %d\n", procs_file, errno); + } + snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/cpu/syz%llu", procid); + if (mkdir(cgroupdir, 0777)) { + debug("mkdir(%s) failed: %d\n", cgroupdir, errno); + } + snprintf(procs_file, sizeof(procs_file), "%s/cgroup.procs", cgroupdir); + if (!write_file(procs_file, "%d", pid)) { + debug("write(%s) failed: %d\n", procs_file, errno); + } + snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/net/syz%llu", procid); + if (mkdir(cgroupdir, 0777)) { + debug("mkdir(%s) failed: %d\n", cgroupdir, errno); + } + snprintf(procs_file, sizeof(procs_file), "%s/cgroup.procs", cgroupdir); + if (!write_file(procs_file, "%d", pid)) { + debug("write(%s) failed: %d\n", procs_file, errno); + } +#endif +} + +static void reset_loop() +{ +#if SYZ_EXECUTOR || __NR_syz_mount_image || __NR_syz_read_part_table + char buf[64]; + snprintf(buf, sizeof(buf), "/dev/loop%llu", procid); + int loopfd = open(buf, O_RDWR); + if (loopfd != -1) { + ioctl(loopfd, LOOP_CLR_FD, 0); + close(loopfd); + } +#endif +#if SYZ_EXECUTOR || SYZ_RESET_NET_NAMESPACE + reset_net_namespace(); +#endif +} + +static void setup_test() +{ + prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); + setpgrp(); +#if SYZ_EXECUTOR || SYZ_ENABLE_CGROUPS + char cgroupdir[64]; + snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/unified/syz%llu", procid); + if (symlink(cgroupdir, "./cgroup")) { + debug("symlink(%s, ./cgroup) failed: %d\n", cgroupdir, errno); + } + snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/cpu/syz%llu", procid); + if (symlink(cgroupdir, "./cgroup.cpu")) { + debug("symlink(%s, ./cgroup.cpu) failed: %d\n", cgroupdir, errno); + } + snprintf(cgroupdir, sizeof(cgroupdir), "/syzcgroup/net/syz%llu", procid); + if (symlink(cgroupdir, "./cgroup.net")) { + debug("symlink(%s, ./cgroup.net) failed: %d\n", cgroupdir, errno); + } +#endif +#if SYZ_EXECUTOR || SYZ_TUN_ENABLE + flush_tun(); +#endif +} + +static void reset_test() +{ + int fd; + for (fd = 3; fd < 30; fd++) + close(fd); +} +#endif +#elif GOOS_test + + +#include +#include + +#if SYZ_EXECUTOR || __NR_syz_mmap +static long syz_mmap(long a0, long a1) +{ + return 0; +} +#endif + +#if SYZ_EXECUTOR || SYZ_SANDBOX_NONE +static void loop(); +static int do_sandbox_none(void) +{ + loop(); + doexit(0); +} +#endif + +#define do_sandbox_setuid() 0 +#define do_sandbox_namespace() 0 +#define setup_loop() +#define reset_loop() +#define setup_test() +#define reset_test() +#elif GOOS_windows + + +#include + +#include "common.h" + +#if SYZ_EXECUTOR || SYZ_HANDLE_SEGV +static void install_segv_handler() +{ +} + +#define NONFAILING(...) \ + __try { \ + __VA_ARGS__; \ + } __except (EXCEPTION_EXECUTE_HANDLER) { \ + } +#endif + +#if SYZ_EXECUTOR || SYZ_REPEAT +uint64 current_time_ms() +{ + return GetTickCount64(); +} +#endif + +#if SYZ_EXECUTOR || SYZ_THREADED || SYZ_REPEAT +static void sleep_ms(uint64 ms) +{ + Sleep(ms); +} +#endif + +#if SYZ_EXECUTOR || SYZ_THREADED +static void thread_start(void* (*fn)(void*), void* arg) +{ + HANDLE th = CreateThread(NULL, 128 << 10, (LPTHREAD_START_ROUTINE)fn, arg, 0, NULL); + if (th == NULL) + exitf("CreateThread failed"); +} + +struct event_t { + CRITICAL_SECTION cs; + CONDITION_VARIABLE cv; + int state; +}; + +static void event_init(event_t* ev) +{ + InitializeCriticalSection(&ev->cs); + InitializeConditionVariable(&ev->cv); + ev->state = 0; +} + +static void event_reset(event_t* ev) +{ + ev->state = 0; +} + +static void event_set(event_t* ev) +{ + EnterCriticalSection(&ev->cs); + if (ev->state) + fail("event already set"); + ev->state = 1; + LeaveCriticalSection(&ev->cs); + WakeAllConditionVariable(&ev->cv); +} + +static void event_wait(event_t* ev) +{ + EnterCriticalSection(&ev->cs); + while (!ev->state) + SleepConditionVariableCS(&ev->cv, &ev->cs, INFINITE); + LeaveCriticalSection(&ev->cs); +} + +static int event_isset(event_t* ev) +{ + EnterCriticalSection(&ev->cs); + int res = ev->state; + LeaveCriticalSection(&ev->cs); + return res; +} + +static int event_timedwait(event_t* ev, uint64 timeout_ms) +{ + EnterCriticalSection(&ev->cs); + uint64 start = current_time_ms(); + for (;;) { + if (ev->state) + break; + uint64 now = current_time_ms(); + if (now - start > timeout_ms) + break; + SleepConditionVariableCS(&ev->cv, &ev->cs, timeout_ms - (now - start)); + } + int res = ev->state; + LeaveCriticalSection(&ev->cs); + return res; +} +#endif + +#define setup_loop() +#define reset_loop() +#define setup_test() +#define reset_test() +#elif GOOS_test + + +#include +#include + +#if SYZ_EXECUTOR || __NR_syz_mmap +static long syz_mmap(long a0, long a1) +{ + return 0; +} +#endif + +#if SYZ_EXECUTOR || SYZ_SANDBOX_NONE +static void loop(); +static int do_sandbox_none(void) +{ + loop(); + doexit(0); +} +#endif + +#define do_sandbox_setuid() 0 +#define do_sandbox_namespace() 0 +#define setup_loop() +#define reset_loop() +#define setup_test() +#define reset_test() +#else +#error "unknown OS" +#endif + +#if SYZ_THREADED +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; +#if SYZ_COLLIDE +static int collide; +#endif + +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(int num_calls) +{ + int call, thread; + running = 0; + for (call = 0; call < num_calls; call++) { + for (thread = 0; thread < 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); +#if SYZ_COLLIDE + if (collide && (call % 2) == 0) + break; +#endif + event_timedwait(&th->done, 25); + if (__atomic_load_n(&running, __ATOMIC_RELAXED)) + sleep_ms((call == num_calls - 1) ? 10 : 2); + break; + } + } +} +#endif + +#if SYZ_EXECUTOR || SYZ_REPEAT +static void execute_one(); +#if SYZ_EXECUTOR_USES_FORK_SERVER +#include +#include +#include + +#if GOOS_linux +#define WAIT_FLAGS __WALL +#else +#define WAIT_FLAGS 0 +#endif + +#if SYZ_EXECUTOR +static void reply_handshake(); +#endif + +static void loop() +{ + setup_loop(); +#if SYZ_EXECUTOR + reply_handshake(); +#endif +#if SYZ_EXECUTOR && GOOS_akaros + int child_pipe[2]; + if (pipe(child_pipe)) + fail("pipe failed"); +#endif + int iter; + for (iter = 0;; iter++) { +#if SYZ_EXECUTOR || SYZ_USE_TMP_DIR + char cwdbuf[32]; + sprintf(cwdbuf, "./%d", iter); + if (mkdir(cwdbuf, 0777)) + fail("failed to mkdir"); +#endif + reset_loop(); +#if SYZ_EXECUTOR + receive_execute(); +#endif + int pid = fork(); + if (pid < 0) + fail("clone failed"); + if (pid == 0) { + setup_test(); +#if SYZ_EXECUTOR || SYZ_USE_TMP_DIR + if (chdir(cwdbuf)) + fail("failed to chdir"); +#endif +#if GOOS_akaros +#if SYZ_EXECUTOR + dup2(child_pipe[0], kInPipeFd); + close(child_pipe[0]); + close(child_pipe[1]); +#endif + execl(program_name, program_name, "child", NULL); + fail("execl failed"); +#else +#if SYZ_EXECUTOR + close(kInPipeFd); +#endif +#if SYZ_EXECUTOR && SYZ_EXECUTOR_USES_SHMEM + close(kOutPipeFd); +#endif + execute_one(); + debug("worker exiting\n"); + reset_test(); + doexit(0); +#endif + } + debug("spawned worker pid %d\n", pid); + +#if SYZ_EXECUTOR && GOOS_akaros + resend_execute(child_pipe[1]); +#endif + int status = 0; + uint64 start = current_time_ms(); +#if SYZ_EXECUTOR && SYZ_EXECUTOR_USES_SHMEM + uint64 last_executed = start; + uint32 executed_calls = __atomic_load_n(output_data, __ATOMIC_RELAXED); +#endif + for (;;) { + if (waitpid(-1, &status, WNOHANG | WAIT_FLAGS) == pid) + break; + sleep_ms(1); +#if SYZ_EXECUTOR && SYZ_EXECUTOR_USES_SHMEM + uint64 now = current_time_ms(); + uint32 now_executed = __atomic_load_n(output_data, __ATOMIC_RELAXED); + if (executed_calls != now_executed) { + executed_calls = now_executed; + last_executed = now; + } + if ((now - start < 5 * 1000) && (now - start < 3 * 1000 || now - last_executed < 1000)) + continue; +#else + if (current_time_ms() - start < 5 * 1000) + continue; +#endif + debug("killing\n"); +#if GOOS_linux + kill(-pid, SIGKILL); +#endif + kill(pid, SIGKILL); + while (waitpid(-1, &status, WAIT_FLAGS) != pid) { + } + break; + } +#if SYZ_EXECUTOR + status = WEXITSTATUS(status); + if (status == kFailStatus) + fail("child failed"); + if (status == kErrorStatus) + error("child errored"); + reply_execute(0); +#endif +#if SYZ_EXECUTOR || SYZ_USE_TMP_DIR + remove_dir(cwdbuf); +#endif + } +} +#else +static void loop() +{ + (void)sleep_ms; + execute_one(); +} +#endif +#endif +` -- cgit mrf-deployment