brintos

brintos / llvm-project-archived public Read only

0
0
Text · 25.2 KiB · 1ae8e66 Raw
674 lines · cpp
1//===- FuzzerUtilFuchsia.cpp - Misc utils for Fuchsia. --------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8// Misc utils implementation using Fuchsia/Zircon APIs.9//===----------------------------------------------------------------------===//10#include "FuzzerPlatform.h"11 12#if LIBFUZZER_FUCHSIA13 14#include "FuzzerInternal.h"15#include "FuzzerUtil.h"16#include <cassert>17#include <cerrno>18#include <cinttypes>19#include <cstdint>20#include <fcntl.h>21#include <lib/fdio/fdio.h>22#include <lib/fdio/spawn.h>23#include <string>24#include <sys/select.h>25#include <thread>26#include <unistd.h>27#include <zircon/errors.h>28#include <zircon/process.h>29#include <zircon/sanitizer.h>30#include <zircon/status.h>31#include <zircon/syscalls.h>32#include <zircon/syscalls/debug.h>33#include <zircon/syscalls/exception.h>34#include <zircon/syscalls/object.h>35#include <zircon/types.h>36 37#include <vector>38 39namespace fuzzer {40 41// Given that Fuchsia doesn't have the POSIX signals that libFuzzer was written42// around, the general approach is to spin up dedicated threads to watch for43// each requested condition (alarm, interrupt, crash).  Of these, the crash44// handler is the most involved, as it requires resuming the crashed thread in45// order to invoke the sanitizers to get the needed state.46 47// Forward declaration of assembly trampoline needed to resume crashed threads.48// This appears to have external linkage to  C++, which is why it's not in the49// anonymous namespace.  The assembly definition inside MakeTrampoline()50// actually defines the symbol with internal linkage only.51void CrashTrampolineAsm() __asm__("CrashTrampolineAsm");52 53namespace {54 55// The signal handler thread uses Zircon exceptions to resume crashed threads56// into libFuzzer's POSIX signal handlers. The associated event is used to57// signal when the thread is running, and when it should stop.58std::thread SignalHandler;59zx_handle_t SignalHandlerEvent = ZX_HANDLE_INVALID;60 61// Helper function to handle Zircon syscall failures.62void ExitOnErr(zx_status_t Status, const char *Syscall) {63  if (Status != ZX_OK) {64    Printf("libFuzzer: %s failed: %s\n", Syscall,65           _zx_status_get_string(Status));66    exit(1);67  }68}69 70void AlarmHandler(int Seconds) {71  // Signal the alarm thread started.72  ExitOnErr(_zx_object_signal(SignalHandlerEvent, 0, ZX_USER_SIGNAL_0),73            "_zx_object_signal alarm");74  while (true) {75    SleepSeconds(Seconds);76    Fuzzer::StaticAlarmCallback();77  }78}79 80// For the crash handler, we need to call Fuzzer::StaticCrashSignalCallback81// without POSIX signal handlers.  To achieve this, we use an assembly function82// to add the necessary CFI unwinding information and a C function to bridge83// from that back into C++.84 85// FIXME: This works as a short-term solution, but this code really shouldn't be86// architecture dependent. A better long term solution is to implement remote87// unwinding and expose the necessary APIs through sanitizer_common and/or ASAN88// to allow the exception handling thread to gather the crash state directly.89//90// Alternatively, Fuchsia may in future actually implement basic signal91// handling for the machine trap signals.92#if defined(__x86_64__)93 94#define FOREACH_REGISTER(OP_REG, OP_NUM) \95  OP_REG(rax)                            \96  OP_REG(rbx)                            \97  OP_REG(rcx)                            \98  OP_REG(rdx)                            \99  OP_REG(rsi)                            \100  OP_REG(rdi)                            \101  OP_REG(rbp)                            \102  OP_REG(rsp)                            \103  OP_REG(r8)                             \104  OP_REG(r9)                             \105  OP_REG(r10)                            \106  OP_REG(r11)                            \107  OP_REG(r12)                            \108  OP_REG(r13)                            \109  OP_REG(r14)                            \110  OP_REG(r15)                            \111  OP_REG(rip)112 113#elif defined(__aarch64__)114 115#define FOREACH_REGISTER(OP_REG, OP_NUM) \116  OP_NUM(0)                              \117  OP_NUM(1)                              \118  OP_NUM(2)                              \119  OP_NUM(3)                              \120  OP_NUM(4)                              \121  OP_NUM(5)                              \122  OP_NUM(6)                              \123  OP_NUM(7)                              \124  OP_NUM(8)                              \125  OP_NUM(9)                              \126  OP_NUM(10)                             \127  OP_NUM(11)                             \128  OP_NUM(12)                             \129  OP_NUM(13)                             \130  OP_NUM(14)                             \131  OP_NUM(15)                             \132  OP_NUM(16)                             \133  OP_NUM(17)                             \134  OP_NUM(18)                             \135  OP_NUM(19)                             \136  OP_NUM(20)                             \137  OP_NUM(21)                             \138  OP_NUM(22)                             \139  OP_NUM(23)                             \140  OP_NUM(24)                             \141  OP_NUM(25)                             \142  OP_NUM(26)                             \143  OP_NUM(27)                             \144  OP_NUM(28)                             \145  OP_NUM(29)                             \146  OP_REG(sp)147 148#elif defined(__riscv)149 150#define FOREACH_REGISTER(OP_REG, OP_NUM)                                      \151  OP_REG(ra)                                                                  \152  OP_REG(sp)                                                                  \153  OP_REG(gp)                                                                  \154  OP_REG(tp)                                                                  \155  OP_REG(t0)                                                                  \156  OP_REG(t1)                                                                  \157  OP_REG(t2)                                                                  \158  OP_REG(s0)                                                                  \159  OP_REG(s1)                                                                  \160  OP_REG(a0)                                                                  \161  OP_REG(a1)                                                                  \162  OP_REG(a2)                                                                  \163  OP_REG(a3)                                                                  \164  OP_REG(a4)                                                                  \165  OP_REG(a5)                                                                  \166  OP_REG(a6)                                                                  \167  OP_REG(a7)                                                                  \168  OP_REG(s2)                                                                  \169  OP_REG(s3)                                                                  \170  OP_REG(s4)                                                                  \171  OP_REG(s5)                                                                  \172  OP_REG(s6)                                                                  \173  OP_REG(s7)                                                                  \174  OP_REG(s8)                                                                  \175  OP_REG(s9)                                                                  \176  OP_REG(s10)                                                                 \177  OP_REG(s11)                                                                 \178  OP_REG(t3)                                                                  \179  OP_REG(t4)                                                                  \180  OP_REG(t5)                                                                  \181  OP_REG(t6)                                                                  \182 183#else184#error "Unsupported architecture for fuzzing on Fuchsia"185#endif186 187// Produces a CFI directive for the named or numbered register.188// The value used refers to an assembler immediate operand with the same name189// as the register (see ASM_OPERAND_REG).190#define CFI_OFFSET_REG(reg) ".cfi_offset " #reg ", %c[" #reg "]\n"191#define CFI_OFFSET_NUM(num) CFI_OFFSET_REG(x##num)192 193// Produces an assembler immediate operand for the named or numbered register.194// This operand contains the offset of the register relative to the CFA.195#define ASM_OPERAND_REG(reg)                                                   \196  [reg] "i"(offsetof(zx_thread_state_general_regs_t, reg)),197#define ASM_OPERAND_NUM(num)                                                   \198  [x##num] "i"(offsetof(zx_thread_state_general_regs_t, r[num])),199 200// Trampoline to bridge from the assembly below to the static C++ crash201// callback.202__attribute__((noreturn))203static void StaticCrashHandler() {204  Fuzzer::StaticCrashSignalCallback();205  for (;;) {206    _Exit(1);207  }208}209 210// This trampoline function has the necessary CFI information to unwind211// and get a backtrace:212//  * The stack contains a copy of all the registers at the point of crash,213//    the code has CFI directives specifying how to restore them.214//  * A call to StaticCrashHandler, which will print the stacktrace and exit215//    the fuzzer, generating a crash artifact.216//217// The __attribute__((used)) is necessary because the function218// is never called; it's just a container around the assembly to allow it to219// use operands for compile-time computed constants.220__attribute__((used))221void MakeTrampoline() {222  __asm__(223      ".cfi_endproc\n"224      ".pushsection .text.CrashTrampolineAsm\n"225      ".type CrashTrampolineAsm,STT_FUNC\n"226      "CrashTrampolineAsm:\n"227      ".cfi_startproc simple\n"228      ".cfi_signal_frame\n"229#if defined(__x86_64__)230      ".cfi_return_column rip\n"231      ".cfi_def_cfa rsp, 0\n"232      FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM)233      "call %c[StaticCrashHandler]\n"234      "ud2\n"235#elif defined(__aarch64__)236      ".cfi_return_column 33\n"237      ".cfi_def_cfa sp, 0\n"238      FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM)239      ".cfi_offset 33, %c[pc]\n"240      ".cfi_offset 30, %c[lr]\n"241      "bl %c[StaticCrashHandler]\n"242      "brk 1\n"243#elif defined(__riscv)244      ".cfi_return_column 64\n"245      ".cfi_def_cfa sp, 0\n"246      ".cfi_offset 64, %[pc]\n"247      FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM)248      "call %c[StaticCrashHandler]\n"249      "unimp\n"250#else251#error "Unsupported architecture for fuzzing on Fuchsia"252#endif253     ".cfi_endproc\n"254     ".size CrashTrampolineAsm, . - CrashTrampolineAsm\n"255     ".popsection\n"256     ".cfi_startproc\n"257      : // No outputs258      : FOREACH_REGISTER(ASM_OPERAND_REG, ASM_OPERAND_NUM)259#if defined(__aarch64__) || defined(__riscv)260        ASM_OPERAND_REG(pc)261#endif262#if defined(__aarch64__)263        ASM_OPERAND_REG(lr)264#endif265        [StaticCrashHandler] "i"(StaticCrashHandler));266}267 268void CrashHandler() {269  assert(SignalHandlerEvent != ZX_HANDLE_INVALID);270 271  // This structure is used to ensure we close handles to objects we create in272  // this handler.273  struct ScopedHandle {274    ~ScopedHandle() { _zx_handle_close(Handle); }275    zx_handle_t Handle = ZX_HANDLE_INVALID;276  };277 278  // Create the exception channel.  We need to claim to be a "debugger" so the279  // kernel will allow us to modify and resume dying threads (see below). Once280  // the channel is set, we can signal the main thread to continue and wait281  // for the exception to arrive.282  ScopedHandle Channel;283  zx_handle_t Self = _zx_process_self();284  ExitOnErr(_zx_task_create_exception_channel(285                Self, ZX_EXCEPTION_CHANNEL_DEBUGGER, &Channel.Handle),286            "_zx_task_create_exception_channel");287 288  // Signal the crash thread started.289  ExitOnErr(_zx_object_signal(SignalHandlerEvent, 0, ZX_USER_SIGNAL_0),290            "_zx_object_signal");291 292  // This thread lives as long as the process in order to keep handling293  // crashes.  In practice, the first crashed thread to reach the end of the294  // StaticCrashHandler will end the process.295  while (true) {296    zx_wait_item_t WaitItems[] = {297        {298            .handle = SignalHandlerEvent,299            .waitfor = ZX_USER_SIGNAL_1,300            .pending = 0,301        },302        {303            .handle = Channel.Handle,304            .waitfor = ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED,305            .pending = 0,306        },307    };308    auto Status = _zx_object_wait_many(309        WaitItems, sizeof(WaitItems) / sizeof(WaitItems[0]), ZX_TIME_INFINITE);310    if (Status != ZX_OK || (WaitItems[1].pending & ZX_CHANNEL_READABLE) == 0) {311      break;312    }313 314    zx_exception_info_t ExceptionInfo;315    ScopedHandle Exception;316    ExitOnErr(_zx_channel_read(Channel.Handle, 0, &ExceptionInfo,317                               &Exception.Handle, sizeof(ExceptionInfo), 1,318                               nullptr, nullptr),319              "_zx_channel_read");320 321    // Ignore informational synthetic exceptions.322    if (ZX_EXCP_THREAD_STARTING == ExceptionInfo.type ||323        ZX_EXCP_THREAD_EXITING == ExceptionInfo.type ||324        ZX_EXCP_PROCESS_STARTING == ExceptionInfo.type) {325      continue;326    }327 328    // At this point, we want to get the state of the crashing thread, but329    // libFuzzer and the sanitizers assume this will happen from that same330    // thread via a POSIX signal handler. "Resurrecting" the thread in the331    // middle of the appropriate callback is as simple as forcibly setting the332    // instruction pointer/program counter, provided we NEVER EVER return from333    // that function (since otherwise our stack will not be valid).334    ScopedHandle Thread;335    ExitOnErr(_zx_exception_get_thread(Exception.Handle, &Thread.Handle),336              "_zx_exception_get_thread");337 338    zx_thread_state_general_regs_t GeneralRegisters;339    ExitOnErr(_zx_thread_read_state(Thread.Handle, ZX_THREAD_STATE_GENERAL_REGS,340                                    &GeneralRegisters,341                                    sizeof(GeneralRegisters)),342              "_zx_thread_read_state");343 344    // To unwind properly, we need to push the crashing thread's register state345    // onto the stack and jump into a trampoline with CFI instructions on how346    // to restore it.347#if defined(__x86_64__)348 349    uintptr_t StackPtr =350        (GeneralRegisters.rsp - (128 + sizeof(GeneralRegisters))) &351        -(uintptr_t)16;352    __unsanitized_memcpy(reinterpret_cast<void *>(StackPtr), &GeneralRegisters,353                         sizeof(GeneralRegisters));354    GeneralRegisters.rsp = StackPtr;355    GeneralRegisters.rip = reinterpret_cast<zx_vaddr_t>(CrashTrampolineAsm);356 357#elif defined(__aarch64__) || defined(__riscv)358 359    uintptr_t StackPtr =360        (GeneralRegisters.sp - sizeof(GeneralRegisters)) & -(uintptr_t)16;361    __unsanitized_memcpy(reinterpret_cast<void *>(StackPtr), &GeneralRegisters,362                         sizeof(GeneralRegisters));363    GeneralRegisters.sp = StackPtr;364    GeneralRegisters.pc = reinterpret_cast<zx_vaddr_t>(CrashTrampolineAsm);365 366#else367#error "Unsupported architecture for fuzzing on Fuchsia"368#endif369 370    // Now force the crashing thread's state.371    ExitOnErr(372        _zx_thread_write_state(Thread.Handle, ZX_THREAD_STATE_GENERAL_REGS,373                               &GeneralRegisters, sizeof(GeneralRegisters)),374        "_zx_thread_write_state");375 376    // Set the exception to HANDLED so it resumes the thread on close.377    uint32_t ExceptionState = ZX_EXCEPTION_STATE_HANDLED;378    ExitOnErr(_zx_object_set_property(Exception.Handle, ZX_PROP_EXCEPTION_STATE,379                                      &ExceptionState, sizeof(ExceptionState)),380              "zx_object_set_property");381  }382}383 384void StopSignalHandler() {385  _zx_object_signal(SignalHandlerEvent, 0, ZX_USER_SIGNAL_1);386  if (SignalHandler.joinable()) {387    SignalHandler.join();388  }389  _zx_handle_close(SignalHandlerEvent);390}391 392void RssThread(Fuzzer *F, size_t RssLimitMb) {393  // Signal the rss thread started.394  //395  // We must wait for this thread to start because we could accidentally suspend396  // it while the crash handler is attempting to handle the397  // ZX_EXCP_THREAD_STARTING exception. If the crash handler is suspended by the398  // lsan machinery, then there's no way for this thread to indicate it's399  // suspended because it's blocked on waiting for the exception to be handled.400  ExitOnErr(_zx_object_signal(SignalHandlerEvent, 0, ZX_USER_SIGNAL_0),401            "_zx_object_signal rss");402  while (true) {403    SleepSeconds(1);404    size_t Peak = GetPeakRSSMb();405    if (Peak > RssLimitMb)406      F->RssLimitCallback();407  }408}409 410} // namespace411 412void StartRssThread(Fuzzer *F, size_t RssLimitMb) {413  // Set up the crash handler and wait until it is ready before proceeding.414  assert(SignalHandlerEvent == ZX_HANDLE_INVALID);415  ExitOnErr(_zx_event_create(0, &SignalHandlerEvent), "_zx_event_create");416 417  if (!RssLimitMb)418    return;419  std::thread T(RssThread, F, RssLimitMb);420  T.detach();421 422  // Wait for the rss thread to start.423  ExitOnErr(_zx_object_wait_one(SignalHandlerEvent, ZX_USER_SIGNAL_0,424                                ZX_TIME_INFINITE, nullptr),425            "_zx_object_wait_one rss");426  ExitOnErr(_zx_object_signal(SignalHandlerEvent, ZX_USER_SIGNAL_0, 0),427            "_zx_object_signal rss clear");428}429 430// Platform specific functions.431void SetSignalHandler(const FuzzingOptions &Options) {432  assert(SignalHandlerEvent != ZX_HANDLE_INVALID &&433         "This should've been setup by StartRssThread.");434 435  // Make sure information from libFuzzer and the sanitizers are easy to436  // reassemble. `__sanitizer_log_write` has the added benefit of ensuring the437  // DSO map is always available for the symbolizer.438  // A uint64_t fits in 20 chars, so 64 is plenty.439  char Buf[64];440  memset(Buf, 0, sizeof(Buf));441  snprintf(Buf, sizeof(Buf), "==%lu== INFO: libFuzzer starting.\n", GetPid());442  if (EF->__sanitizer_log_write)443    __sanitizer_log_write(Buf, sizeof(Buf));444  Printf("%s", Buf);445 446  // Set up alarm handler if needed.447  if (Options.HandleAlrm && Options.UnitTimeoutSec > 0) {448    std::thread T(AlarmHandler, Options.UnitTimeoutSec / 2 + 1);449    T.detach();450 451    // Wait for the alarm thread to start.452    //453    // We must wait for this thread to start because we could accidentally454    // suspend it while the crash handler is attempting to handle the455    // ZX_EXCP_THREAD_STARTING exception. If the crash handler is suspended by456    // the lsan machinery, then there's no way for this thread to indicate it's457    // suspended because it's blocked on waiting for the exception to be458    // handled.459    ExitOnErr(_zx_object_wait_one(SignalHandlerEvent, ZX_USER_SIGNAL_0,460                                  ZX_TIME_INFINITE, nullptr),461              "_zx_object_wait_one alarm");462    ExitOnErr(_zx_object_signal(SignalHandlerEvent, ZX_USER_SIGNAL_0, 0),463              "_zx_object_signal alarm clear");464  }465 466  // Options.HandleInt and Options.HandleTerm are not supported on Fuchsia467 468  // Early exit if no crash handler needed.469  if (!Options.HandleSegv && !Options.HandleBus && !Options.HandleIll &&470      !Options.HandleFpe && !Options.HandleAbrt && !Options.HandleTrap)471    return;472 473  SignalHandler = std::thread(CrashHandler);474  zx_status_t Status = _zx_object_wait_one(SignalHandlerEvent, ZX_USER_SIGNAL_0,475                                           ZX_TIME_INFINITE, nullptr);476  ExitOnErr(Status, "_zx_object_wait_one");477 478  std::atexit(StopSignalHandler);479}480 481void SleepSeconds(int Seconds) {482  _zx_nanosleep(_zx_deadline_after(ZX_SEC(Seconds)));483}484 485unsigned long GetPid() {486  zx_status_t rc;487  zx_info_handle_basic_t Info;488  if ((rc = _zx_object_get_info(_zx_process_self(), ZX_INFO_HANDLE_BASIC, &Info,489                                sizeof(Info), NULL, NULL)) != ZX_OK) {490    Printf("libFuzzer: unable to get info about self: %s\n",491           _zx_status_get_string(rc));492    exit(1);493  }494  return Info.koid;495}496 497size_t GetPeakRSSMb() {498  zx_status_t rc;499  zx_info_task_stats_t Info;500  if ((rc = _zx_object_get_info(_zx_process_self(), ZX_INFO_TASK_STATS, &Info,501                                sizeof(Info), NULL, NULL)) != ZX_OK) {502    Printf("libFuzzer: unable to get info about self: %s\n",503           _zx_status_get_string(rc));504    exit(1);505  }506  return (Info.mem_private_bytes + Info.mem_shared_bytes) >> 20;507}508 509template <typename Fn>510class RunOnDestruction {511 public:512  explicit RunOnDestruction(Fn fn) : fn_(fn) {}513  ~RunOnDestruction() { fn_(); }514 515 private:516  Fn fn_;517};518 519template <typename Fn>520RunOnDestruction<Fn> at_scope_exit(Fn fn) {521  return RunOnDestruction<Fn>(fn);522}523 524static fdio_spawn_action_t clone_fd_action(int localFd, int targetFd) {525  return {526      .action = FDIO_SPAWN_ACTION_CLONE_FD,527      .fd =528          {529              .local_fd = localFd,530              .target_fd = targetFd,531          },532  };533}534 535int ExecuteCommand(const Command &Cmd) {536  zx_status_t rc;537 538  // Convert arguments to C array539  auto Args = Cmd.getArguments();540  size_t Argc = Args.size();541  assert(Argc != 0);542  std::unique_ptr<const char *[]> Argv(new const char *[Argc + 1]);543  for (size_t i = 0; i < Argc; ++i)544    Argv[i] = Args[i].c_str();545  Argv[Argc] = nullptr;546 547  // Determine output.  On Fuchsia, the fuzzer is typically run as a component548  // that lacks a mutable working directory. Fortunately, when this is the case549  // a mutable output directory must be specified using "-artifact_prefix=...",550  // so write the log file(s) there.551  // However, we don't want to apply this logic for absolute paths.552  int FdOut = STDOUT_FILENO;553  bool discardStdout = false;554  bool discardStderr = false;555 556  if (Cmd.hasOutputFile()) {557    std::string Path = Cmd.getOutputFile();558    if (Path == getDevNull()) {559      // On Fuchsia, there's no "/dev/null" like-file, so we560      // just don't copy the FDs into the spawned process.561      discardStdout = true;562    } else {563      bool IsAbsolutePath = Path.length() > 1 && Path[0] == '/';564      if (!IsAbsolutePath && Cmd.hasFlag("artifact_prefix"))565        Path = Cmd.getFlagValue("artifact_prefix") + "/" + Path;566 567      FdOut = open(Path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0);568      if (FdOut == -1) {569        Printf("libFuzzer: failed to open %s: %s\n", Path.c_str(),570               strerror(errno));571        return ZX_ERR_IO;572      }573    }574  }575  auto CloseFdOut = at_scope_exit([FdOut]() {576    if (FdOut != STDOUT_FILENO)577      close(FdOut);578  });579 580  // Determine stderr581  int FdErr = STDERR_FILENO;582  if (Cmd.isOutAndErrCombined()) {583    FdErr = FdOut;584    if (discardStdout)585      discardStderr = true;586  }587 588  // Clone the file descriptors into the new process589  std::vector<fdio_spawn_action_t> SpawnActions;590  SpawnActions.push_back(clone_fd_action(STDIN_FILENO, STDIN_FILENO));591 592  if (!discardStdout)593    SpawnActions.push_back(clone_fd_action(FdOut, STDOUT_FILENO));594  if (!discardStderr)595    SpawnActions.push_back(clone_fd_action(FdErr, STDERR_FILENO));596 597  // Start the process.598  char ErrorMsg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];599  zx_handle_t ProcessHandle = ZX_HANDLE_INVALID;600  rc = fdio_spawn_etc(ZX_HANDLE_INVALID,601                      FDIO_SPAWN_CLONE_ALL & (~FDIO_SPAWN_CLONE_STDIO), Argv[0],602                      Argv.get(), nullptr, SpawnActions.size(),603                      SpawnActions.data(), &ProcessHandle, ErrorMsg);604 605  if (rc != ZX_OK) {606    Printf("libFuzzer: failed to launch '%s': %s, %s\n", Argv[0], ErrorMsg,607           _zx_status_get_string(rc));608    return rc;609  }610  auto CloseHandle = at_scope_exit([&]() { _zx_handle_close(ProcessHandle); });611 612  // Now join the process and return the exit status.613  if ((rc = _zx_object_wait_one(ProcessHandle, ZX_PROCESS_TERMINATED,614                                ZX_TIME_INFINITE, nullptr)) != ZX_OK) {615    Printf("libFuzzer: failed to join '%s': %s\n", Argv[0],616           _zx_status_get_string(rc));617    return rc;618  }619 620  zx_info_process_t Info;621  if ((rc = _zx_object_get_info(ProcessHandle, ZX_INFO_PROCESS, &Info,622                                sizeof(Info), nullptr, nullptr)) != ZX_OK) {623    Printf("libFuzzer: unable to get return code from '%s': %s\n", Argv[0],624           _zx_status_get_string(rc));625    return rc;626  }627 628  return static_cast<int>(Info.return_code);629}630 631bool ExecuteCommand(const Command &BaseCmd, std::string *CmdOutput) {632  auto LogFilePath = TempPath("SimPopenOut", ".txt");633  Command Cmd(BaseCmd);634  Cmd.setOutputFile(LogFilePath);635  int Ret = ExecuteCommand(Cmd);636  *CmdOutput = FileToString(LogFilePath);637  RemoveFile(LogFilePath);638  return Ret == 0;639}640 641const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt,642                         size_t PattLen) {643  return memmem(Data, DataLen, Patt, PattLen);644}645 646// In fuchsia, accessing /dev/null is not supported. There's nothing647// similar to a file that discards everything that is written to it.648// The way of doing something similar in fuchsia is by using649// fdio_null_create and binding that to a file descriptor.650void DiscardOutput(int Fd) {651  fdio_t *fdio_null = fdio_null_create();652  if (fdio_null == nullptr) return;653  int nullfd = fdio_bind_to_fd(fdio_null, -1, 0);654  if (nullfd < 0) return;655  dup2(nullfd, Fd);656}657 658size_t PageSize() {659  static size_t PageSizeCached = _zx_system_get_page_size();660  return PageSizeCached;661}662 663void SetThreadName(std::thread &thread, const std::string &name) {664  if (zx_status_t s = zx_object_set_property(665          thread.native_handle(), ZX_PROP_NAME, name.data(), name.size());666      s != ZX_OK)667    Printf("SetThreadName for name %s failed: %s", name.c_str(),668           zx_status_get_string(s));669}670 671} // namespace fuzzer672 673#endif // LIBFUZZER_FUCHSIA674