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-exception
6//
7//===----------------------------------------------------------------------===//
8// Misc utils implementation using Fuchsia/Zircon APIs.
9//===----------------------------------------------------------------------===//
10#include "FuzzerPlatform.h"
11
12#if LIBFUZZER_FUCHSIA
13
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 written
42// around, the general approach is to spin up dedicated threads to watch for
43// each requested condition (alarm, interrupt, crash). Of these, the crash
44// handler is the most involved, as it requires resuming the crashed thread in
45// 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 the
49// 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 threads
56// into libFuzzer's POSIX signal handlers. The associated event is used to
57// 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::StaticCrashSignalCallback
81// without POSIX signal handlers. To achieve this, we use an assembly function
82// to add the necessary CFI unwinding information and a C function to bridge
83// from that back into C++.
84
85// FIXME: This works as a short-term solution, but this code really shouldn't be
86// architecture dependent. A better long term solution is to implement remote
87// unwinding and expose the necessary APIs through sanitizer_common and/or ASAN
88// to allow the exception handling thread to gather the crash state directly.
89//
90// Alternatively, Fuchsia may in future actually implement basic signal
91// 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#else
184#error "Unsupported architecture for fuzzing on Fuchsia"
185#endif
186
187// Produces a CFI directive for the named or numbered register.
188// The value used refers to an assembler immediate operand with the same name
189// 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++ crash
201// 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 unwind
211// 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 exit
215// the fuzzer, generating a crash artifact.
216//
217// The __attribute__((used)) is necessary because the function
218// is never called; it's just a container around the assembly to allow it to
219// 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#else
251#error "Unsupported architecture for fuzzing on Fuchsia"
252#endif
253 ".cfi_endproc\n"
254 ".size CrashTrampolineAsm, . - CrashTrampolineAsm\n"
255 ".popsection\n"
256 ".cfi_startproc\n"
257 : // No outputs
258 : FOREACH_REGISTER(ASM_OPERAND_REG, ASM_OPERAND_NUM)
259#if defined(__aarch64__) || defined(__riscv)
260 ASM_OPERAND_REG(pc)
261#endif
262#if defined(__aarch64__)
263 ASM_OPERAND_REG(lr)
264#endif
265 [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 in
272 // 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 the
279 // kernel will allow us to modify and resume dying threads (see below). Once
280 // the channel is set, we can signal the main thread to continue and wait
281 // 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 handling
293 // crashes. In practice, the first crashed thread to reach the end of the
294 // 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, but
329 // libFuzzer and the sanitizers assume this will happen from that same
330 // thread via a POSIX signal handler. "Resurrecting" the thread in the
331 // middle of the appropriate callback is as simple as forcibly setting the
332 // instruction pointer/program counter, provided we NEVER EVER return from
333 // 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 state
345 // onto the stack and jump into a trampoline with CFI instructions on how
346 // 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#else
367#error "Unsupported architecture for fuzzing on Fuchsia"
368#endif
369
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 suspend
396 // it while the crash handler is attempting to handle the
397 // ZX_EXCP_THREAD_STARTING exception. If the crash handler is suspended by the
398 // lsan machinery, then there's no way for this thread to indicate it's
399 // 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} // namespace
411
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 to
436 // reassemble. `__sanitizer_log_write` has the added benefit of ensuring the
437 // 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 accidentally
454 // suspend it while the crash handler is attempting to handle the
455 // ZX_EXCP_THREAD_STARTING exception. If the crash handler is suspended by
456 // the lsan machinery, then there's no way for this thread to indicate it's
457 // suspended because it's blocked on waiting for the exception to be
458 // 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 Fuchsia
467
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 array
539 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 component
548 // that lacks a mutable working directory. Fortunately, when this is the case
549 // 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 we
560 // 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 stderr
581 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 process
589 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 nothing
647// similar to a file that discards everything that is written to it.
648// The way of doing something similar in fuchsia is by using
649// 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 fuzzer
672
673#endif // LIBFUZZER_FUCHSIA
674