// 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if SYZ_EXECUTOR || __NR_get_root_resource #include #endif #if SYZ_EXECUTOR || SYZ_HANDLE_SEGV #include #include #include #include #include #include static __thread int skip_segv; static __thread jmp_buf segv_env; static void segv_handler(void) { 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_from_exception(thread, port, 0); if (status != ZX_OK) { debug("zx_task_resume_from_exception failed: %d\n", status); } zx_handle_close(thread); } doexit(1); return 0; } static void install_segv_handler(void) { 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 // 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) 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, ZX_VM_FLAG_SPECIFIC_OVERWRITE | ZX_VM_FLAG_PERM_READ | ZX_VM_FLAG_PERM_WRITE | ZX_VM_FLAG_PERM_EXECUTE, addr - info.base, vmo, 0, size, &mapped_addr); 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(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 // 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; })