| // AUTOGENERATED FROM executor/common.h |
| |
| package csource |
| |
| var commonHeader = ` |
| |
| |
| #ifndef _GNU_SOURCE |
| #define _GNU_SOURCE |
| #endif |
| |
| #include <endian.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #if SYZ_TRACE |
| #include <errno.h> |
| #endif |
| |
| #if SYZ_EXECUTOR && !GOOS_linux |
| #include <unistd.h> |
| NORETURN void doexit(int status) |
| { |
| _exit(status); |
| for (;;) { |
| } |
| } |
| #endif |
| |
| #if SYZ_EXECUTOR || SYZ_PROCS || SYZ_REPEAT && SYZ_ENABLE_CGROUPS || \ |
| __NR_syz_mount_image || __NR_syz_read_part_table |
| unsigned long long procid; |
| #endif |
| |
| #if !GOOS_fuchsia && !GOOS_windows |
| #if SYZ_EXECUTOR || SYZ_HANDLE_SEGV |
| #include <setjmp.h> |
| #include <signal.h> |
| #include <string.h> |
| |
| #if GOOS_linux |
| #include <sys/syscall.h> |
| #endif |
| |
| static __thread int skip_segv; |
| static __thread jmp_buf segv_env; |
| |
| #if GOOS_akaros |
| #include <parlib/parlib.h> |
| 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_linux |
| #if (SYZ_EXECUTOR || SYZ_REPEAT) && SYZ_EXECUTOR_USES_FORK_SERVER |
| #include <signal.h> |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| |
| static void kill_and_wait(int pid, int* status) |
| { |
| kill(pid, SIGKILL); |
| while (waitpid(-1, status, 0) != pid) { |
| } |
| } |
| #endif |
| #endif |
| |
| #if !GOOS_windows |
| #if SYZ_EXECUTOR || SYZ_THREADED || SYZ_REPEAT && SYZ_EXECUTOR_USES_FORK_SERVER |
| static void sleep_ms(uint64 ms) |
| { |
| usleep(ms * 1000); |
| } |
| #endif |
| |
| #if SYZ_EXECUTOR || SYZ_THREADED || SYZ_REPEAT && SYZ_EXECUTOR_USES_FORK_SERVER |
| #include <time.h> |
| |
| static 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 <stdlib.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| |
| 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 <dirent.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| |
| 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 <pthread.h> |
| |
| 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 <pthread.h> |
| #include <time.h> |
| |
| 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) |
| { |
| uint64 start = current_time_ms(); |
| uint64 now = start; |
| pthread_mutex_lock(&ev->mu); |
| for (;;) { |
| if (ev->state) |
| break; |
| uint64 remain = timeout - (now - start); |
| struct timespec ts; |
| ts.tv_sec = remain / 1000; |
| ts.tv_nsec = (remain % 1000) * 1000 * 1000; |
| pthread_cond_timedwait(&ev->cv, &ev->mu, &ts); |
| now = current_time_ms(); |
| if (now - start > timeout) |
| break; |
| } |
| 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 <ros/syscall.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| |
| #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 |
| |
| #if SYZ_EXECUTOR |
| #define do_sandbox_setuid() 0 |
| #define do_sandbox_namespace() 0 |
| #endif |
| |
| #elif GOOS_freebsd || GOOS_netbsd |
| |
| #include <unistd.h> |
| |
| #if SYZ_EXECUTOR || SYZ_SANDBOX_NONE |
| static void loop(); |
| static int do_sandbox_none(void) |
| { |
| loop(); |
| return 0; |
| } |
| #endif |
| |
| #if SYZ_EXECUTOR |
| #define do_sandbox_setuid() 0 |
| #define do_sandbox_namespace() 0 |
| #endif |
| |
| #elif GOOS_fuchsia |
| |
| #include <ddk/driver.h> |
| #include <fcntl.h> |
| #include <lib/fdio/util.h> |
| #include <poll.h> |
| #include <signal.h> |
| #include <stdlib.h> |
| #include <sys/file.h> |
| #include <sys/ioctl.h> |
| #include <sys/socket.h> |
| #include <sys/stat.h> |
| #include <sys/time.h> |
| #include <sys/types.h> |
| #include <sys/uio.h> |
| #include <time.h> |
| #include <unistd.h> |
| #include <utime.h> |
| #include <zircon/process.h> |
| #include <zircon/syscalls.h> |
| |
| #if SYZ_EXECUTOR || SYZ_HANDLE_SEGV |
| #include <pthread.h> |
| #include <setjmp.h> |
| #include <zircon/syscalls/debug.h> |
| #include <zircon/syscalls/exception.h> |
| #include <zircon/syscalls/object.h> |
| #include <zircon/syscalls/port.h> |
| |
| 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 <unistd.h> |
| 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 |
| |
| #if SYZ_EXECUTOR |
| #define do_sandbox_setuid() 0 |
| #define do_sandbox_namespace() 0 |
| #endif |
| |
| #elif GOOS_linux |
| |
| #include <stdlib.h> |
| #include <sys/syscall.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #if SYZ_EXECUTOR |
| struct cover_t; |
| static void cover_reset(cover_t* cov); |
| #endif |
| |
| #if SYZ_EXECUTOR || SYZ_THREADED |
| #include <linux/futex.h> |
| #include <pthread.h> |
| |
| 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) |
| { |
| uint64 start = current_time_ms(); |
| uint64 now = start; |
| for (;;) { |
| uint64 remain = timeout - (now - start); |
| struct timespec ts; |
| ts.tv_sec = remain / 1000; |
| ts.tv_nsec = (remain % 1000) * 1000 * 1000; |
| syscall(SYS_futex, &ev->state, FUTEX_WAIT, 0, &ts); |
| if (__atomic_load_n(&ev->state, __ATOMIC_RELAXED)) |
| return 1; |
| now = current_time_ms(); |
| if (now - start > timeout) |
| return 0; |
| } |
| } |
| #endif |
| |
| #if SYZ_EXECUTOR || SYZ_TUN_ENABLE || SYZ_ENABLE_NETDEV |
| #include <stdarg.h> |
| #include <stdbool.h> |
| #include <string.h> |
| |
| 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 <arpa/inet.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <linux/if.h> |
| #include <linux/if_ether.h> |
| #include <linux/if_tun.h> |
| #include <linux/ip.h> |
| #include <linux/tcp.h> |
| #include <net/if_arp.h> |
| #include <stdarg.h> |
| #include <stdbool.h> |
| #include <sys/ioctl.h> |
| #include <sys/stat.h> |
| |
| 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 <arpa/inet.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <linux/if.h> |
| #include <linux/if_ether.h> |
| #include <linux/if_tun.h> |
| #include <linux/ip.h> |
| #include <linux/tcp.h> |
| #include <net/if_arp.h> |
| #include <stdarg.h> |
| #include <stdbool.h> |
| #include <sys/ioctl.h> |
| #include <sys/stat.h> |
| #include <sys/uio.h> |
| #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 <errno.h> |
| |
| 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 <stdbool.h> |
| #include <sys/uio.h> |
| |
| #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 <fcntl.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| |
| 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 <fcntl.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| |
| 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 <fcntl.h> |
| #include <sys/ioctl.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| |
| 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 <fcntl.h> |
| #include <sched.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| 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 <errno.h> |
| #include <linux/genetlink.h> |
| #include <linux/netlink.h> |
| #include <sys/socket.h> |
| #include <sys/types.h> |
| |
| 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 <errno.h> |
| #include <fcntl.h> |
| #include <linux/loop.h> |
| #include <sys/ioctl.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| |
| 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 |
| #include <string.h> |
| #include <sys/mount.h> |
| 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 <errno.h> |
| #include <fcntl.h> |
| #include <linux/kvm.h> |
| #include <stdarg.h> |
| #include <stddef.h> |
| #include <sys/ioctl.h> |
| #include <sys/stat.h> |
| |
| #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 <errno.h> |
| #include <fcntl.h> |
| #include <stdarg.h> |
| #include <stdbool.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| |
| 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 <errno.h> |
| #include <linux/net.h> |
| #include <netinet/in.h> |
| #include <string.h> |
| #include <sys/socket.h> |
| #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("iptable checkpoint %d: socket failed", 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("iptable checkpoint %s/%d: getsockopt(IPT_SO_GET_INFO)", table->name, family); |
| } |
| debug("iptable checkpoint %s/%d: checkpoint 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("iptable checkpoint %s/%d: table size is too large: %u", |
| table->name, family, table->info.size); |
| if (table->info.num_entries > XT_MAX_ENTRIES) |
| fail("iptable checkpoint %s/%d: too many counters: %u", |
| table->name, family, 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("iptable checkpoint %s/%d: getsockopt(IPT_SO_GET_ENTRIES)", |
| table->name, family); |
| 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("iptable %d: socket failed", 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("iptable %s/%d: getsockopt(IPT_SO_GET_INFO)", table->name, family); |
| 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("iptable %s/%d: getsockopt(IPT_SO_GET_ENTRIES)", table->name, family); |
| if (memcmp(table->replace.entrytable, entries.entrytable, table->info.size) == 0) |
| continue; |
| } |
| debug("iptable %s/%d: resetting\n", table->name, family); |
| 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("iptable %s/%d: setsockopt(IPT_SO_SET_REPLACE)", table->name, family); |
| } |
| 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("arptable checkpoint: 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("arptable checkpoint %s: getsockopt(ARPT_SO_GET_INFO)", table->name); |
| } |
| debug("arptable checkpoint %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("arptable checkpoint %s: table size is too large: %u", |
| table->name, table->info.size); |
| if (table->info.num_entries > XT_MAX_ENTRIES) |
| fail("arptable checkpoint %s: too many counters: %u", |
| table->name, 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("arptable checkpoint %s: getsockopt(ARPT_SO_GET_ENTRIES)", table->name); |
| 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("arptable: 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("arptable %s:getsockopt(ARPT_SO_GET_INFO)", table->name); |
| 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("arptable %s: getsockopt(ARPT_SO_GET_ENTRIES)", table->name); |
| if (memcmp(table->replace.entrytable, entries.entrytable, table->info.size) == 0) |
| continue; |
| debug("arptable %s: data changed\n", table->name); |
| } else { |
| debug("arptable %s: header changed\n", table->name); |
| } |
| debug("arptable %s: resetting\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("arptable %s: setsockopt(ARPT_SO_SET_REPLACE)", table->name); |
| } |
| close(fd); |
| } |
| |
| #include <linux/if.h> |
| #include <linux/netfilter_bridge/ebtables.h> |
| |
| 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("ebtable checkpoint: 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("ebtable checkpoint %s: getsockopt(EBT_SO_GET_INIT_INFO)", table->name); |
| } |
| debug("ebtable checkpoint %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("ebtable checkpoint %s: table size is too large: %u", |
| table->name, 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("ebtable checkpoint %s: getsockopt(EBT_SO_GET_INIT_ENTRIES)", table->name); |
| } |
| 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("ebtable: 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("ebtable %s: getsockopt(EBT_SO_GET_INFO)", table->name); |
| 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("ebtable %s: getsockopt(EBT_SO_GET_ENTRIES)", table->name); |
| if (memcmp(table->entrytable, entrytable, replace.entries_size) == 0) |
| continue; |
| } |
| debug("ebtable %s: resetting\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("ebtable %s: setsockopt(EBT_SO_SET_ENTRIES)", table->name); |
| } |
| close(fd); |
| } |
| |
| static void checkpoint_net_namespace(void) |
| { |
| #if SYZ_EXECUTOR |
| if (flag_sandbox == sandbox_setuid) |
| return; |
| #endif |
| 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) |
| { |
| #if SYZ_EXECUTOR |
| if (flag_sandbox == sandbox_setuid) |
| return; |
| #endif |
| 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 <fcntl.h> |
| #include <sys/mount.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| |
| 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 (mount(0, "/proc/sys/fs/binfmt_misc", "binfmt_misc", 0, 0)) { |
| debug("mount(binfmt_misc) failed: %d\n", errno); |
| } |
| if (!write_file("/proc/sys/fs/binfmt_misc/register", ":syz0:M:0:\x01::./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:\x02::./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 |
| #include <errno.h> |
| #include <sys/mount.h> |
| |
| static void setup_common() |
| { |
| if (mount(0, "/sys/fs/fuse/connections", "fusectl", 0, 0)) { |
| debug("mount(fusectl) failed: %d\n", errno); |
| } |
| #if SYZ_EXECUTOR || SYZ_ENABLE_CGROUPS |
| setup_cgroups(); |
| setup_binfmt_misc(); |
| #endif |
| } |
| #endif |
| |
| #if SYZ_EXECUTOR || SYZ_SANDBOX_NONE || SYZ_SANDBOX_SETUID || SYZ_SANDBOX_NAMESPACE |
| #include <sched.h> |
| #include <sys/prctl.h> |
| #include <sys/resource.h> |
| #include <sys/time.h> |
| #include <sys/wait.h> |
| |
| 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 <sched.h> |
| #include <sys/types.h> |
| |
| 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 <grp.h> |
| #include <sched.h> |
| #include <sys/prctl.h> |
| |
| static int do_sandbox_setuid(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 |
| |
| 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 <linux/capability.h> |
| #include <sched.h> |
| #include <sys/mman.h> |
| #include <sys/mount.h> |
| |
| 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 <dirent.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <sys/ioctl.h> |
| #include <sys/mount.h> |
| |
| #define FS_IOC_SETFLAGS _IOW('f', 2, long) |
| static void remove_dir(const char* dir) |
| { |
| DIR* dp; |
| struct dirent* ep; |
| int iter = 0; |
| retry: |
| while (umount2(dir, MNT_DETACH) == 0) { |
| 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 == EPERM) { |
| int fd = open(filename, O_RDONLY); |
| if (fd != -1) { |
| long flags = 0; |
| if (ioctl(fd, FS_IOC_SETFLAGS, &flags) == 0) |
| debug("reset FS_XFLAG_IMMUTABLE\n"); |
| close(fd); |
| continue; |
| } |
| } |
| 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 == EPERM) { |
| int fd = open(dir, O_RDONLY); |
| if (fd != -1) { |
| long flags = 0; |
| if (ioctl(fd, FS_IOC_SETFLAGS, &flags) == 0) |
| debug("reset FS_XFLAG_IMMUTABLE\n"); |
| close(fd); |
| continue; |
| } |
| } |
| 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 <fcntl.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| |
| 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 <dirent.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <signal.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| |
| static void kill_and_wait(int pid, int* status) |
| { |
| kill(-pid, SIGKILL); |
| kill(pid, SIGKILL); |
| int i; |
| for (i = 0; i < 100; i++) { |
| if (waitpid(-1, status, WNOHANG | __WALL) == pid) |
| return; |
| usleep(1000); |
| } |
| debug("kill is not working\n"); |
| DIR* dir = opendir("/sys/fs/fuse/connections"); |
| if (dir) { |
| for (;;) { |
| struct dirent* ent = readdir(dir); |
| if (!ent) |
| break; |
| if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) |
| continue; |
| char abort[300]; |
| snprintf(abort, sizeof(abort), "/sys/fs/fuse/connections/%s/abort", ent->d_name); |
| int fd = open(abort, O_WRONLY); |
| if (fd == -1) { |
| debug("failed to open %s: %d\n", abort, errno); |
| continue; |
| } |
| debug("aborting fuse conn %s\n", ent->d_name); |
| if (write(fd, abort, 1) < 0) { |
| debug("failed to abort: %d\n", errno); |
| } |
| close(fd); |
| } |
| closedir(dir); |
| } else { |
| debug("failed to open /sys/fs/fuse/connections: %d\n", errno); |
| } |
| while (waitpid(-1, status, __WALL) != pid) { |
| } |
| } |
| #endif |
| |
| #if SYZ_EXECUTOR || SYZ_REPEAT && (SYZ_ENABLE_CGROUPS || SYZ_RESET_NET_NAMESPACE) |
| #include <fcntl.h> |
| #include <sys/ioctl.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #define SYZ_HAVE_SETUP_LOOP 1 |
| static void setup_loop() |
| { |
| #if SYZ_EXECUTOR || 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 |
| #if SYZ_EXECUTOR || SYZ_RESET_NET_NAMESPACE |
| checkpoint_net_namespace(); |
| #endif |
| } |
| #endif |
| |
| #if SYZ_EXECUTOR || SYZ_REPEAT && (SYZ_RESET_NET_NAMESPACE || __NR_syz_mount_image || __NR_syz_read_part_table) |
| #define SYZ_HAVE_RESET_LOOP 1 |
| 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 |
| } |
| #endif |
| |
| #if SYZ_EXECUTOR || SYZ_REPEAT |
| #include <sys/prctl.h> |
| |
| #define SYZ_HAVE_SETUP_TEST 1 |
| 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 |
| } |
| |
| #define SYZ_HAVE_RESET_TEST 1 |
| static void reset_test() |
| { |
| int fd; |
| for (fd = 3; fd < 30; fd++) |
| close(fd); |
| } |
| #endif |
| |
| #elif GOOS_test |
| |
| #include <stdlib.h> |
| #include <unistd.h> |
| |
| #if SYZ_EXECUTOR || __NR_syz_mmap |
| #include <sys/mman.h> |
| static long syz_mmap(long a0, long a1) |
| { |
| return (long)mmap((void*)a0, a1, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); |
| } |
| #endif |
| |
| #if SYZ_EXECUTOR || __NR_syz_errno |
| #include <errno.h> |
| static long syz_errno(long v) |
| { |
| errno = v; |
| return v == 0 ? 0 : -1; |
| } |
| #endif |
| |
| #if SYZ_EXECUTOR || __NR_syz_compare |
| #include <errno.h> |
| #include <string.h> |
| static long syz_compare(long want, long want_len, long got, long got_len) |
| { |
| if (want_len != got_len) { |
| debug("syz_compare: want_len=%lu got_len=%lu\n", want_len, got_len); |
| errno = EBADF; |
| return -1; |
| } |
| if (memcmp((void*)want, (void*)got, want_len)) { |
| debug("syz_compare: data differs\n"); |
| errno = EINVAL; |
| return -1; |
| } |
| return 0; |
| } |
| #endif |
| |
| #if SYZ_EXECUTOR || SYZ_SANDBOX_NONE |
| static void loop(); |
| static int do_sandbox_none(void) |
| { |
| loop(); |
| doexit(0); |
| } |
| #endif |
| |
| #if SYZ_EXECUTOR |
| #define do_sandbox_setuid() 0 |
| #define do_sandbox_namespace() 0 |
| #endif |
| |
| #elif GOOS_windows |
| |
| #include <windows.h> |
| |
| #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_THREADED || SYZ_REPEAT && SYZ_EXECUTOR_USES_FORK_SERVER |
| static uint64 current_time_ms() |
| { |
| return GetTickCount64(); |
| } |
| #endif |
| |
| #if SYZ_EXECUTOR || SYZ_THREADED || SYZ_REPEAT && SYZ_EXECUTOR_USES_FORK_SERVER |
| 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 |
| |
| #if SYZ_EXECUTOR || SYZ_SANDBOX_NONE |
| static void loop(); |
| static int do_sandbox_none(void) |
| { |
| loop(); |
| doexit(0); |
| } |
| #endif |
| |
| #if SYZ_EXECUTOR |
| #define do_sandbox_setuid() 0 |
| #define do_sandbox_namespace() 0 |
| #endif |
| |
| #elif GOOS_test |
| |
| #include <stdlib.h> |
| #include <unistd.h> |
| |
| #if SYZ_EXECUTOR || __NR_syz_mmap |
| #include <sys/mman.h> |
| static long syz_mmap(long a0, long a1) |
| { |
| return (long)mmap((void*)a0, a1, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); |
| } |
| #endif |
| |
| #if SYZ_EXECUTOR || __NR_syz_errno |
| #include <errno.h> |
| static long syz_errno(long v) |
| { |
| errno = v; |
| return v == 0 ? 0 : -1; |
| } |
| #endif |
| |
| #if SYZ_EXECUTOR || __NR_syz_compare |
| #include <errno.h> |
| #include <string.h> |
| static long syz_compare(long want, long want_len, long got, long got_len) |
| { |
| if (want_len != got_len) { |
| debug("syz_compare: want_len=%lu got_len=%lu\n", want_len, got_len); |
| errno = EBADF; |
| return -1; |
| } |
| if (memcmp((void*)want, (void*)got, want_len)) { |
| debug("syz_compare: data differs\n"); |
| errno = EINVAL; |
| return -1; |
| } |
| return 0; |
| } |
| #endif |
| |
| #if SYZ_EXECUTOR || SYZ_SANDBOX_NONE |
| static void loop(); |
| static int do_sandbox_none(void) |
| { |
| loop(); |
| doexit(0); |
| } |
| #endif |
| |
| #if SYZ_EXECUTOR |
| #define do_sandbox_setuid() 0 |
| #define do_sandbox_namespace() 0 |
| #endif |
| |
| #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; |
| |
| 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; |
| } |
| |
| #if SYZ_REPEAT |
| static void execute_one() |
| #else |
| static void loop() |
| #endif |
| { |
| #if SYZ_REPRO |
| if (write(1, "executing program\n", sizeof("executing program\n") - 1)) { |
| } |
| #endif |
| #if SYZ_TRACE |
| printf("### start\n"); |
| #endif |
| int i, call, thread; |
| #if SYZ_COLLIDE |
| int collide = 0; |
| again: |
| #endif |
| 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, 45); |
| break; |
| } |
| } |
| for (i = 0; i < 100 && __atomic_load_n(&running, __ATOMIC_RELAXED); i++) |
| sleep_ms(1); |
| #if SYZ_COLLIDE |
| if (!collide) { |
| collide = 1; |
| goto again; |
| } |
| #endif |
| } |
| #endif |
| |
| #if SYZ_EXECUTOR || SYZ_REPEAT |
| static void execute_one(); |
| #if SYZ_EXECUTOR_USES_FORK_SERVER |
| #include <signal.h> |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| |
| #if GOOS_linux |
| #define WAIT_FLAGS __WALL |
| #else |
| #define WAIT_FLAGS 0 |
| #endif |
| |
| #if SYZ_EXECUTOR |
| static void reply_handshake(); |
| #endif |
| |
| static void loop() |
| { |
| #if SYZ_HAVE_SETUP_LOOP |
| setup_loop(); |
| #endif |
| #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; |
| #if SYZ_REPEAT_TIMES |
| for (iter = 0; iter < [[REPEAT_TIMES]]; iter++) { |
| #else |
| for (iter = 0;; iter++) { |
| #endif |
| #if SYZ_EXECUTOR || SYZ_USE_TMP_DIR |
| char cwdbuf[32]; |
| sprintf(cwdbuf, "./%d", iter); |
| if (mkdir(cwdbuf, 0777)) |
| fail("failed to mkdir"); |
| #endif |
| #if SYZ_HAVE_RESET_LOOP |
| reset_loop(); |
| #endif |
| #if SYZ_EXECUTOR |
| receive_execute(); |
| #endif |
| int pid = fork(); |
| if (pid < 0) |
| fail("clone failed"); |
| if (pid == 0) { |
| #if SYZ_EXECUTOR || SYZ_USE_TMP_DIR |
| if (chdir(cwdbuf)) |
| fail("failed to chdir"); |
| #endif |
| #if SYZ_HAVE_SETUP_TEST |
| setup_test(); |
| #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"); |
| #if SYZ_HAVE_RESET_TEST |
| reset_test(); |
| #endif |
| 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"); |
| kill_and_wait(pid, &status); |
| 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() |
| { |
| execute_one(); |
| } |
| #endif |
| #endif |
| #if !SYZ_EXECUTOR |
| [[SYSCALL_DEFINES]] |
| |
| [[RESULTS]] |
| |
| #if SYZ_THREADED || SYZ_REPEAT || SYZ_SANDBOX_NONE || SYZ_SANDBOX_SETUID || SYZ_SANDBOX_NAMESPACE |
| #if SYZ_THREADED |
| void execute_call(int call) |
| #elif SYZ_REPEAT |
| void execute_one() |
| #else |
| void loop() |
| #endif |
| { |
| [[SYSCALLS]] |
| } |
| #endif |
| #if GOOS_akaros && SYZ_REPEAT |
| #include <string.h> |
| |
| int main(int argc, char** argv) |
| { |
| [[MMAP_DATA]] |
| |
| program_name = argv[0]; |
| if (argc == 2 && strcmp(argv[1], "child") == 0) |
| child(); |
| #else |
| int main() |
| { |
| [[MMAP_DATA]] |
| #endif |
| |
| #if SYZ_HANDLE_SEGV |
| install_segv_handler(); |
| #endif |
| #if SYZ_PROCS |
| for (procid = 0; procid < [[PROCS]]; procid++) { |
| if (fork() == 0) { |
| #endif |
| #if SYZ_USE_TMP_DIR |
| use_temporary_dir(); |
| #endif |
| [[SANDBOX_FUNC]] |
| #if SYZ_PROCS |
| } |
| } |
| sleep(1000000); |
| #endif |
| return 0; |
| } |
| #endif |
| ` |