Path: blob/main/contrib/llvm-project/compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp
35262 views
//===- FuzzerUtilFuchsia.cpp - Misc utils for Fuchsia. --------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7// Misc utils implementation using Fuchsia/Zircon APIs.8//===----------------------------------------------------------------------===//9#include "FuzzerPlatform.h"1011#if LIBFUZZER_FUCHSIA1213#include "FuzzerInternal.h"14#include "FuzzerUtil.h"15#include <cassert>16#include <cerrno>17#include <cinttypes>18#include <cstdint>19#include <fcntl.h>20#include <lib/fdio/fdio.h>21#include <lib/fdio/spawn.h>22#include <string>23#include <sys/select.h>24#include <thread>25#include <unistd.h>26#include <zircon/errors.h>27#include <zircon/process.h>28#include <zircon/sanitizer.h>29#include <zircon/status.h>30#include <zircon/syscalls.h>31#include <zircon/syscalls/debug.h>32#include <zircon/syscalls/exception.h>33#include <zircon/syscalls/object.h>34#include <zircon/types.h>3536#include <vector>3738namespace fuzzer {3940// Given that Fuchsia doesn't have the POSIX signals that libFuzzer was written41// around, the general approach is to spin up dedicated threads to watch for42// each requested condition (alarm, interrupt, crash). Of these, the crash43// handler is the most involved, as it requires resuming the crashed thread in44// order to invoke the sanitizers to get the needed state.4546// Forward declaration of assembly trampoline needed to resume crashed threads.47// This appears to have external linkage to C++, which is why it's not in the48// anonymous namespace. The assembly definition inside MakeTrampoline()49// actually defines the symbol with internal linkage only.50void CrashTrampolineAsm() __asm__("CrashTrampolineAsm");5152namespace {5354// The signal handler thread uses Zircon exceptions to resume crashed threads55// into libFuzzer's POSIX signal handlers. The associated event is used to56// signal when the thread is running, and when it should stop.57std::thread SignalHandler;58zx_handle_t SignalHandlerEvent = ZX_HANDLE_INVALID;5960// Helper function to handle Zircon syscall failures.61void ExitOnErr(zx_status_t Status, const char *Syscall) {62if (Status != ZX_OK) {63Printf("libFuzzer: %s failed: %s\n", Syscall,64_zx_status_get_string(Status));65exit(1);66}67}6869void AlarmHandler(int Seconds) {70while (true) {71SleepSeconds(Seconds);72Fuzzer::StaticAlarmCallback();73}74}7576// For the crash handler, we need to call Fuzzer::StaticCrashSignalCallback77// without POSIX signal handlers. To achieve this, we use an assembly function78// to add the necessary CFI unwinding information and a C function to bridge79// from that back into C++.8081// FIXME: This works as a short-term solution, but this code really shouldn't be82// architecture dependent. A better long term solution is to implement remote83// unwinding and expose the necessary APIs through sanitizer_common and/or ASAN84// to allow the exception handling thread to gather the crash state directly.85//86// Alternatively, Fuchsia may in future actually implement basic signal87// handling for the machine trap signals.88#if defined(__x86_64__)8990#define FOREACH_REGISTER(OP_REG, OP_NUM) \91OP_REG(rax) \92OP_REG(rbx) \93OP_REG(rcx) \94OP_REG(rdx) \95OP_REG(rsi) \96OP_REG(rdi) \97OP_REG(rbp) \98OP_REG(rsp) \99OP_REG(r8) \100OP_REG(r9) \101OP_REG(r10) \102OP_REG(r11) \103OP_REG(r12) \104OP_REG(r13) \105OP_REG(r14) \106OP_REG(r15) \107OP_REG(rip)108109#elif defined(__aarch64__)110111#define FOREACH_REGISTER(OP_REG, OP_NUM) \112OP_NUM(0) \113OP_NUM(1) \114OP_NUM(2) \115OP_NUM(3) \116OP_NUM(4) \117OP_NUM(5) \118OP_NUM(6) \119OP_NUM(7) \120OP_NUM(8) \121OP_NUM(9) \122OP_NUM(10) \123OP_NUM(11) \124OP_NUM(12) \125OP_NUM(13) \126OP_NUM(14) \127OP_NUM(15) \128OP_NUM(16) \129OP_NUM(17) \130OP_NUM(18) \131OP_NUM(19) \132OP_NUM(20) \133OP_NUM(21) \134OP_NUM(22) \135OP_NUM(23) \136OP_NUM(24) \137OP_NUM(25) \138OP_NUM(26) \139OP_NUM(27) \140OP_NUM(28) \141OP_NUM(29) \142OP_REG(sp)143144#elif defined(__riscv)145146#define FOREACH_REGISTER(OP_REG, OP_NUM) \147OP_REG(ra) \148OP_REG(sp) \149OP_REG(gp) \150OP_REG(tp) \151OP_REG(t0) \152OP_REG(t1) \153OP_REG(t2) \154OP_REG(s0) \155OP_REG(s1) \156OP_REG(a0) \157OP_REG(a1) \158OP_REG(a2) \159OP_REG(a3) \160OP_REG(a4) \161OP_REG(a5) \162OP_REG(a6) \163OP_REG(a7) \164OP_REG(s2) \165OP_REG(s3) \166OP_REG(s4) \167OP_REG(s5) \168OP_REG(s6) \169OP_REG(s7) \170OP_REG(s8) \171OP_REG(s9) \172OP_REG(s10) \173OP_REG(s11) \174OP_REG(t3) \175OP_REG(t4) \176OP_REG(t5) \177OP_REG(t6) \178179#else180#error "Unsupported architecture for fuzzing on Fuchsia"181#endif182183// Produces a CFI directive for the named or numbered register.184// The value used refers to an assembler immediate operand with the same name185// as the register (see ASM_OPERAND_REG).186#define CFI_OFFSET_REG(reg) ".cfi_offset " #reg ", %c[" #reg "]\n"187#define CFI_OFFSET_NUM(num) CFI_OFFSET_REG(x##num)188189// Produces an assembler immediate operand for the named or numbered register.190// This operand contains the offset of the register relative to the CFA.191#define ASM_OPERAND_REG(reg) \192[reg] "i"(offsetof(zx_thread_state_general_regs_t, reg)),193#define ASM_OPERAND_NUM(num) \194[x##num] "i"(offsetof(zx_thread_state_general_regs_t, r[num])),195196// Trampoline to bridge from the assembly below to the static C++ crash197// callback.198__attribute__((noreturn))199static void StaticCrashHandler() {200Fuzzer::StaticCrashSignalCallback();201for (;;) {202_Exit(1);203}204}205206// This trampoline function has the necessary CFI information to unwind207// and get a backtrace:208// * The stack contains a copy of all the registers at the point of crash,209// the code has CFI directives specifying how to restore them.210// * A call to StaticCrashHandler, which will print the stacktrace and exit211// the fuzzer, generating a crash artifact.212//213// The __attribute__((used)) is necessary because the function214// is never called; it's just a container around the assembly to allow it to215// use operands for compile-time computed constants.216__attribute__((used))217void MakeTrampoline() {218__asm__(219".cfi_endproc\n"220".pushsection .text.CrashTrampolineAsm\n"221".type CrashTrampolineAsm,STT_FUNC\n"222"CrashTrampolineAsm:\n"223".cfi_startproc simple\n"224".cfi_signal_frame\n"225#if defined(__x86_64__)226".cfi_return_column rip\n"227".cfi_def_cfa rsp, 0\n"228FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM)229"call %c[StaticCrashHandler]\n"230"ud2\n"231#elif defined(__aarch64__)232".cfi_return_column 33\n"233".cfi_def_cfa sp, 0\n"234FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM)235".cfi_offset 33, %c[pc]\n"236".cfi_offset 30, %c[lr]\n"237"bl %c[StaticCrashHandler]\n"238"brk 1\n"239#elif defined(__riscv)240".cfi_return_column 64\n"241".cfi_def_cfa sp, 0\n"242".cfi_offset 64, %[pc]\n"243FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM)244"call %c[StaticCrashHandler]\n"245"unimp\n"246#else247#error "Unsupported architecture for fuzzing on Fuchsia"248#endif249".cfi_endproc\n"250".size CrashTrampolineAsm, . - CrashTrampolineAsm\n"251".popsection\n"252".cfi_startproc\n"253: // No outputs254: FOREACH_REGISTER(ASM_OPERAND_REG, ASM_OPERAND_NUM)255#if defined(__aarch64__) || defined(__riscv)256ASM_OPERAND_REG(pc)257#endif258#if defined(__aarch64__)259ASM_OPERAND_REG(lr)260#endif261[StaticCrashHandler] "i"(StaticCrashHandler));262}263264void CrashHandler() {265assert(SignalHandlerEvent != ZX_HANDLE_INVALID);266267// This structure is used to ensure we close handles to objects we create in268// this handler.269struct ScopedHandle {270~ScopedHandle() { _zx_handle_close(Handle); }271zx_handle_t Handle = ZX_HANDLE_INVALID;272};273274// Create the exception channel. We need to claim to be a "debugger" so the275// kernel will allow us to modify and resume dying threads (see below). Once276// the channel is set, we can signal the main thread to continue and wait277// for the exception to arrive.278ScopedHandle Channel;279zx_handle_t Self = _zx_process_self();280ExitOnErr(_zx_task_create_exception_channel(281Self, ZX_EXCEPTION_CHANNEL_DEBUGGER, &Channel.Handle),282"_zx_task_create_exception_channel");283284ExitOnErr(_zx_object_signal(SignalHandlerEvent, 0, ZX_USER_SIGNAL_0),285"_zx_object_signal");286287// This thread lives as long as the process in order to keep handling288// crashes. In practice, the first crashed thread to reach the end of the289// StaticCrashHandler will end the process.290while (true) {291zx_wait_item_t WaitItems[] = {292{293.handle = SignalHandlerEvent,294.waitfor = ZX_USER_SIGNAL_1,295.pending = 0,296},297{298.handle = Channel.Handle,299.waitfor = ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED,300.pending = 0,301},302};303auto Status = _zx_object_wait_many(304WaitItems, sizeof(WaitItems) / sizeof(WaitItems[0]), ZX_TIME_INFINITE);305if (Status != ZX_OK || (WaitItems[1].pending & ZX_CHANNEL_READABLE) == 0) {306break;307}308309zx_exception_info_t ExceptionInfo;310ScopedHandle Exception;311ExitOnErr(_zx_channel_read(Channel.Handle, 0, &ExceptionInfo,312&Exception.Handle, sizeof(ExceptionInfo), 1,313nullptr, nullptr),314"_zx_channel_read");315316// Ignore informational synthetic exceptions.317if (ZX_EXCP_THREAD_STARTING == ExceptionInfo.type ||318ZX_EXCP_THREAD_EXITING == ExceptionInfo.type ||319ZX_EXCP_PROCESS_STARTING == ExceptionInfo.type) {320continue;321}322323// At this point, we want to get the state of the crashing thread, but324// libFuzzer and the sanitizers assume this will happen from that same325// thread via a POSIX signal handler. "Resurrecting" the thread in the326// middle of the appropriate callback is as simple as forcibly setting the327// instruction pointer/program counter, provided we NEVER EVER return from328// that function (since otherwise our stack will not be valid).329ScopedHandle Thread;330ExitOnErr(_zx_exception_get_thread(Exception.Handle, &Thread.Handle),331"_zx_exception_get_thread");332333zx_thread_state_general_regs_t GeneralRegisters;334ExitOnErr(_zx_thread_read_state(Thread.Handle, ZX_THREAD_STATE_GENERAL_REGS,335&GeneralRegisters,336sizeof(GeneralRegisters)),337"_zx_thread_read_state");338339// To unwind properly, we need to push the crashing thread's register state340// onto the stack and jump into a trampoline with CFI instructions on how341// to restore it.342#if defined(__x86_64__)343344uintptr_t StackPtr =345(GeneralRegisters.rsp - (128 + sizeof(GeneralRegisters))) &346-(uintptr_t)16;347__unsanitized_memcpy(reinterpret_cast<void *>(StackPtr), &GeneralRegisters,348sizeof(GeneralRegisters));349GeneralRegisters.rsp = StackPtr;350GeneralRegisters.rip = reinterpret_cast<zx_vaddr_t>(CrashTrampolineAsm);351352#elif defined(__aarch64__) || defined(__riscv)353354uintptr_t StackPtr =355(GeneralRegisters.sp - sizeof(GeneralRegisters)) & -(uintptr_t)16;356__unsanitized_memcpy(reinterpret_cast<void *>(StackPtr), &GeneralRegisters,357sizeof(GeneralRegisters));358GeneralRegisters.sp = StackPtr;359GeneralRegisters.pc = reinterpret_cast<zx_vaddr_t>(CrashTrampolineAsm);360361#else362#error "Unsupported architecture for fuzzing on Fuchsia"363#endif364365// Now force the crashing thread's state.366ExitOnErr(367_zx_thread_write_state(Thread.Handle, ZX_THREAD_STATE_GENERAL_REGS,368&GeneralRegisters, sizeof(GeneralRegisters)),369"_zx_thread_write_state");370371// Set the exception to HANDLED so it resumes the thread on close.372uint32_t ExceptionState = ZX_EXCEPTION_STATE_HANDLED;373ExitOnErr(_zx_object_set_property(Exception.Handle, ZX_PROP_EXCEPTION_STATE,374&ExceptionState, sizeof(ExceptionState)),375"zx_object_set_property");376}377}378379void StopSignalHandler() {380_zx_object_signal(SignalHandlerEvent, 0, ZX_USER_SIGNAL_1);381if (SignalHandler.joinable()) {382SignalHandler.join();383}384_zx_handle_close(SignalHandlerEvent);385}386387} // namespace388389// Platform specific functions.390void SetSignalHandler(const FuzzingOptions &Options) {391// Make sure information from libFuzzer and the sanitizers are easy to392// reassemble. `__sanitizer_log_write` has the added benefit of ensuring the393// DSO map is always available for the symbolizer.394// A uint64_t fits in 20 chars, so 64 is plenty.395char Buf[64];396memset(Buf, 0, sizeof(Buf));397snprintf(Buf, sizeof(Buf), "==%lu== INFO: libFuzzer starting.\n", GetPid());398if (EF->__sanitizer_log_write)399__sanitizer_log_write(Buf, sizeof(Buf));400Printf("%s", Buf);401402// Set up alarm handler if needed.403if (Options.HandleAlrm && Options.UnitTimeoutSec > 0) {404std::thread T(AlarmHandler, Options.UnitTimeoutSec / 2 + 1);405T.detach();406}407408// Options.HandleInt and Options.HandleTerm are not supported on Fuchsia409410// Early exit if no crash handler needed.411if (!Options.HandleSegv && !Options.HandleBus && !Options.HandleIll &&412!Options.HandleFpe && !Options.HandleAbrt)413return;414415// Set up the crash handler and wait until it is ready before proceeding.416ExitOnErr(_zx_event_create(0, &SignalHandlerEvent), "_zx_event_create");417418SignalHandler = std::thread(CrashHandler);419zx_status_t Status = _zx_object_wait_one(SignalHandlerEvent, ZX_USER_SIGNAL_0,420ZX_TIME_INFINITE, nullptr);421ExitOnErr(Status, "_zx_object_wait_one");422423std::atexit(StopSignalHandler);424}425426void SleepSeconds(int Seconds) {427_zx_nanosleep(_zx_deadline_after(ZX_SEC(Seconds)));428}429430unsigned long GetPid() {431zx_status_t rc;432zx_info_handle_basic_t Info;433if ((rc = _zx_object_get_info(_zx_process_self(), ZX_INFO_HANDLE_BASIC, &Info,434sizeof(Info), NULL, NULL)) != ZX_OK) {435Printf("libFuzzer: unable to get info about self: %s\n",436_zx_status_get_string(rc));437exit(1);438}439return Info.koid;440}441442size_t GetPeakRSSMb() {443zx_status_t rc;444zx_info_task_stats_t Info;445if ((rc = _zx_object_get_info(_zx_process_self(), ZX_INFO_TASK_STATS, &Info,446sizeof(Info), NULL, NULL)) != ZX_OK) {447Printf("libFuzzer: unable to get info about self: %s\n",448_zx_status_get_string(rc));449exit(1);450}451return (Info.mem_private_bytes + Info.mem_shared_bytes) >> 20;452}453454template <typename Fn>455class RunOnDestruction {456public:457explicit RunOnDestruction(Fn fn) : fn_(fn) {}458~RunOnDestruction() { fn_(); }459460private:461Fn fn_;462};463464template <typename Fn>465RunOnDestruction<Fn> at_scope_exit(Fn fn) {466return RunOnDestruction<Fn>(fn);467}468469static fdio_spawn_action_t clone_fd_action(int localFd, int targetFd) {470return {471.action = FDIO_SPAWN_ACTION_CLONE_FD,472.fd =473{474.local_fd = localFd,475.target_fd = targetFd,476},477};478}479480int ExecuteCommand(const Command &Cmd) {481zx_status_t rc;482483// Convert arguments to C array484auto Args = Cmd.getArguments();485size_t Argc = Args.size();486assert(Argc != 0);487std::unique_ptr<const char *[]> Argv(new const char *[Argc + 1]);488for (size_t i = 0; i < Argc; ++i)489Argv[i] = Args[i].c_str();490Argv[Argc] = nullptr;491492// Determine output. On Fuchsia, the fuzzer is typically run as a component493// that lacks a mutable working directory. Fortunately, when this is the case494// a mutable output directory must be specified using "-artifact_prefix=...",495// so write the log file(s) there.496// However, we don't want to apply this logic for absolute paths.497int FdOut = STDOUT_FILENO;498bool discardStdout = false;499bool discardStderr = false;500501if (Cmd.hasOutputFile()) {502std::string Path = Cmd.getOutputFile();503if (Path == getDevNull()) {504// On Fuchsia, there's no "/dev/null" like-file, so we505// just don't copy the FDs into the spawned process.506discardStdout = true;507} else {508bool IsAbsolutePath = Path.length() > 1 && Path[0] == '/';509if (!IsAbsolutePath && Cmd.hasFlag("artifact_prefix"))510Path = Cmd.getFlagValue("artifact_prefix") + "/" + Path;511512FdOut = open(Path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0);513if (FdOut == -1) {514Printf("libFuzzer: failed to open %s: %s\n", Path.c_str(),515strerror(errno));516return ZX_ERR_IO;517}518}519}520auto CloseFdOut = at_scope_exit([FdOut]() {521if (FdOut != STDOUT_FILENO)522close(FdOut);523});524525// Determine stderr526int FdErr = STDERR_FILENO;527if (Cmd.isOutAndErrCombined()) {528FdErr = FdOut;529if (discardStdout)530discardStderr = true;531}532533// Clone the file descriptors into the new process534std::vector<fdio_spawn_action_t> SpawnActions;535SpawnActions.push_back(clone_fd_action(STDIN_FILENO, STDIN_FILENO));536537if (!discardStdout)538SpawnActions.push_back(clone_fd_action(FdOut, STDOUT_FILENO));539if (!discardStderr)540SpawnActions.push_back(clone_fd_action(FdErr, STDERR_FILENO));541542// Start the process.543char ErrorMsg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];544zx_handle_t ProcessHandle = ZX_HANDLE_INVALID;545rc = fdio_spawn_etc(ZX_HANDLE_INVALID,546FDIO_SPAWN_CLONE_ALL & (~FDIO_SPAWN_CLONE_STDIO), Argv[0],547Argv.get(), nullptr, SpawnActions.size(),548SpawnActions.data(), &ProcessHandle, ErrorMsg);549550if (rc != ZX_OK) {551Printf("libFuzzer: failed to launch '%s': %s, %s\n", Argv[0], ErrorMsg,552_zx_status_get_string(rc));553return rc;554}555auto CloseHandle = at_scope_exit([&]() { _zx_handle_close(ProcessHandle); });556557// Now join the process and return the exit status.558if ((rc = _zx_object_wait_one(ProcessHandle, ZX_PROCESS_TERMINATED,559ZX_TIME_INFINITE, nullptr)) != ZX_OK) {560Printf("libFuzzer: failed to join '%s': %s\n", Argv[0],561_zx_status_get_string(rc));562return rc;563}564565zx_info_process_t Info;566if ((rc = _zx_object_get_info(ProcessHandle, ZX_INFO_PROCESS, &Info,567sizeof(Info), nullptr, nullptr)) != ZX_OK) {568Printf("libFuzzer: unable to get return code from '%s': %s\n", Argv[0],569_zx_status_get_string(rc));570return rc;571}572573return static_cast<int>(Info.return_code);574}575576bool ExecuteCommand(const Command &BaseCmd, std::string *CmdOutput) {577auto LogFilePath = TempPath("SimPopenOut", ".txt");578Command Cmd(BaseCmd);579Cmd.setOutputFile(LogFilePath);580int Ret = ExecuteCommand(Cmd);581*CmdOutput = FileToString(LogFilePath);582RemoveFile(LogFilePath);583return Ret == 0;584}585586const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt,587size_t PattLen) {588return memmem(Data, DataLen, Patt, PattLen);589}590591// In fuchsia, accessing /dev/null is not supported. There's nothing592// similar to a file that discards everything that is written to it.593// The way of doing something similar in fuchsia is by using594// fdio_null_create and binding that to a file descriptor.595void DiscardOutput(int Fd) {596fdio_t *fdio_null = fdio_null_create();597if (fdio_null == nullptr) return;598int nullfd = fdio_bind_to_fd(fdio_null, -1, 0);599if (nullfd < 0) return;600dup2(nullfd, Fd);601}602603size_t PageSize() {604static size_t PageSizeCached = _zx_system_get_page_size();605return PageSizeCached;606}607608void SetThreadName(std::thread &thread, const std::string &name) {609// TODO ?610}611612} // namespace fuzzer613614#endif // LIBFUZZER_FUCHSIA615616617