| // Copyright 2017 syzkaller project authors. All rights reserved. |
| // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. |
| |
| // This file is shared between executor and csource package. |
| |
| #include <fcntl.h> |
| #include <lib/fdio/directory.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/status.h> |
| #include <zircon/syscalls.h> |
| |
| #if SYZ_EXECUTOR || __NR_get_root_resource |
| #include <lib/ddk/driver.h> |
| #endif |
| |
| #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> |
| |
| static __thread int skip_segv; |
| static __thread jmp_buf segv_env; |
| |
| static void segv_handler(void) |
| { |
| if (__atomic_load_n(&skip_segv, __ATOMIC_RELAXED)) { |
| debug("recover: skipping\n"); |
| longjmp(segv_env, 1); |
| } |
| debug("recover: exiting\n"); |
| doexit(SIGSEGV); |
| } |
| |
| static zx_status_t update_exception_thread_regs(zx_handle_t exception) |
| { |
| zx_handle_t thread; |
| zx_status_t status = zx_exception_get_thread(exception, &thread); |
| if (status != ZX_OK) { |
| debug("zx_exception_get_thread failed: %s (%d)\n", zx_status_get_string(status), status); |
| return status; |
| } |
| |
| 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 %s (%d)\n", |
| (int)sizeof(regs), zx_status_get_string(status), 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: %s (%d)\n", zx_status_get_string(status), status); |
| } |
| } |
| |
| zx_handle_close(thread); |
| return status; |
| } |
| |
| static void* ex_handler(void* arg) |
| { |
| zx_handle_t exception_channel = (zx_handle_t)(long)arg; |
| for (int i = 0; i < 10000; i++) { |
| zx_status_t status = zx_object_wait_one(exception_channel, ZX_CHANNEL_READABLE, ZX_TIME_INFINITE, NULL); |
| if (status != ZX_OK) { |
| debug("zx_object_wait_one failed: %s (%d)\n", zx_status_get_string(status), status); |
| continue; |
| } |
| |
| zx_exception_info_t info; |
| zx_handle_t exception; |
| status = zx_channel_read(exception_channel, 0, &info, &exception, sizeof(info), 1, NULL, NULL); |
| if (status != ZX_OK) { |
| debug("zx_channel_read failed: %s (%d)\n", zx_status_get_string(status), status); |
| continue; |
| } |
| |
| debug("got exception: type=%d tid=%llu\n", info.type, (unsigned long long)(info.tid)); |
| status = update_exception_thread_regs(exception); |
| if (status != ZX_OK) { |
| debug("failed to update exception thread registers: %s (%d)\n", zx_status_get_string(status), status); |
| } |
| |
| uint32 state = ZX_EXCEPTION_STATE_HANDLED; |
| status = zx_object_set_property(exception, ZX_PROP_EXCEPTION_STATE, &state, sizeof(state)); |
| if (status != ZX_OK) { |
| debug("zx_object_set_property(ZX_PROP_EXCEPTION_STATE) failed: %s (%d)\n", zx_status_get_string(status), status); |
| } |
| zx_handle_close(exception); |
| } |
| doexit(1); |
| return 0; |
| } |
| |
| static void install_segv_handler(void) |
| { |
| zx_status_t status; |
| zx_handle_t exception_channel; |
| if ((status = zx_task_create_exception_channel(zx_process_self(), 0, &exception_channel)) != ZX_OK) |
| failmsg("zx_task_create_exception_channel failed", |
| "status=%s (%d)", zx_status_get_string(status), status); |
| pthread_t th; |
| if (pthread_create(&th, 0, ex_handler, (void*)(long)exception_channel)) |
| fail("pthread_create failed"); |
| } |
| |
| #define NONFAILING(...) \ |
| ({ \ |
| int ok = 1; \ |
| __atomic_fetch_add(&skip_segv, 1, __ATOMIC_SEQ_CST); \ |
| if (sigsetjmp(segv_env, 0) == 0) { \ |
| __VA_ARGS__; \ |
| } else \ |
| ok = 0; \ |
| __atomic_fetch_sub(&skip_segv, 1, __ATOMIC_SEQ_CST); \ |
| ok; \ |
| }) |
| #endif |
| |
| #if SYZ_EXECUTOR || SYZ_THREADED |
| #include <unistd.h> |
| |
| // Fuchsia's pthread_cond_timedwait just returns immidiately, so we use simple spin wait. |
| 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) |
| exitf("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) { |
| debug("zx_object_get_info(ZX_INFO_VMAR) failed: %s (%d)", zx_status_get_string(status), status); |
| return status; |
| } |
| zx_handle_t vmo; |
| status = zx_vmo_create(size, 0, &vmo); |
| if (status != ZX_OK) { |
| debug("zx_vmo_create failed: %s (%d)\n", zx_status_get_string(status), status); |
| return status; |
| } |
| |
| uintptr_t mapped_addr; |
| status = zx_vmar_map(root, ZX_VM_FLAG_SPECIFIC_OVERWRITE | ZX_VM_FLAG_PERM_READ | ZX_VM_FLAG_PERM_WRITE, |
| addr - info.base, vmo, 0, size, |
| &mapped_addr); |
| |
| zx_status_t close_vmo_status = zx_handle_close(vmo); |
| if (close_vmo_status != ZX_OK) { |
| debug("zx_handle_close(vmo) failed with: %s (%d)\n", zx_status_get_string(close_vmo_status), close_vmo_status); |
| } |
| return status; |
| } |
| #endif |
| |
| #if SYZ_EXECUTOR || __NR_syz_process_self |
| static long syz_process_self(void) |
| { |
| return zx_process_self(); |
| } |
| #endif |
| |
| #if SYZ_EXECUTOR || __NR_syz_thread_self |
| static long syz_thread_self(void) |
| { |
| return zx_thread_self(); |
| } |
| #endif |
| |
| #if SYZ_EXECUTOR || __NR_syz_vmar_root_self |
| static long syz_vmar_root_self(void) |
| { |
| return zx_vmar_root_self(); |
| } |
| #endif |
| |
| #if SYZ_EXECUTOR || __NR_syz_job_default |
| static long syz_job_default(void) |
| { |
| return zx_job_default(); |
| } |
| #endif |
| |
| #if SYZ_EXECUTOR || __NR_syz_future_time |
| static long syz_future_time(volatile long when) |
| { |
| zx_time_t delta_ms = 10000; |
| switch (when) { |
| case 0: |
| delta_ms = 5; |
| break; |
| case 1: |
| delta_ms = 30; |
| break; |
| } |
| zx_time_t now = 0; |
| zx_clock_read(ZX_CLOCK_MONOTONIC, &now); |
| 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 |
| |
| // Ugly way to work around gcc's "error: function called through a non-compatible type". |
| // The macro is used in generated C code. |
| #define CAST(f) ({void* p = (void*)f; p; }) |