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