summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/fuzzer/FuzzerUtilFuchsia.cpp273
1 files changed, 237 insertions, 36 deletions
diff --git a/lib/fuzzer/FuzzerUtilFuchsia.cpp b/lib/fuzzer/FuzzerUtilFuchsia.cpp
index 3dacbd05c..b9c70e461 100644
--- a/lib/fuzzer/FuzzerUtilFuchsia.cpp
+++ b/lib/fuzzer/FuzzerUtilFuchsia.cpp
@@ -25,19 +25,43 @@
#include <unistd.h>
#include <zircon/errors.h>
#include <zircon/process.h>
+#include <zircon/sanitizer.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>
+#include <zircon/syscalls/debug.h>
+#include <zircon/syscalls/exception.h>
#include <zircon/syscalls/port.h>
#include <zircon/types.h>
namespace fuzzer {
+// Given that Fuchsia doesn't have the POSIX signals that libFuzzer was written
+// around, the general approach is to spin up dedicated threads to watch for
+// each requested condition (alarm, interrupt, crash). Of these, the crash
+// handler is the most involved, as it requires resuming the crashed thread in
+// order to invoke the sanitizers to get the needed state.
+
+// Forward declaration of assembly trampoline needed to resume crashed threads.
+// This appears to have external linkage to C++, which is why it's not in the
+// anonymous namespace. The assembly definition inside MakeTrampoline()
+// actually defines the symbol with internal linkage only.
+void CrashTrampolineAsm() __asm__("CrashTrampolineAsm");
+
namespace {
// A magic value for the Zircon exception port, chosen to spell 'FUZZING'
// when interpreted as a byte sequence on little-endian platforms.
const uint64_t kFuzzingCrash = 0x474e495a5a5546;
+// Helper function to handle Zircon syscall failures.
+void ExitOnErr(zx_status_t Status, const char *Syscall) {
+ if (Status != ZX_OK) {
+ Printf("libFuzzer: %s failed: %s\n", Syscall,
+ _zx_status_get_string(Status));
+ exit(1);
+ }
+}
+
void AlarmHandler(int Seconds) {
while (true) {
SleepSeconds(Seconds);
@@ -56,32 +80,216 @@ void InterruptHandler() {
Fuzzer::StaticInterruptCallback();
}
-void CrashHandler(zx_handle_t *Port) {
- std::unique_ptr<zx_handle_t> ExceptionPort(Port);
- zx_port_packet_t Packet;
- _zx_port_wait(*ExceptionPort, ZX_TIME_INFINITE, &Packet);
- // Unbind as soon as possible so we don't receive exceptions from this thread.
- if (_zx_task_bind_exception_port(ZX_HANDLE_INVALID, ZX_HANDLE_INVALID,
- kFuzzingCrash, 0) != ZX_OK) {
- // Shouldn't happen; if it does the safest option is to just exit.
- Printf("libFuzzer: unable to unbind exception port; aborting!\n");
- exit(1);
- }
- if (Packet.key != kFuzzingCrash) {
- Printf("libFuzzer: invalid crash key: %" PRIx64 "; aborting!\n",
- Packet.key);
- exit(1);
- }
- // CrashCallback should not return from this call
+// For the crash handler, we need to call Fuzzer::StaticCrashSignalCallback
+// without POSIX signal handlers. To achieve this, we use an assembly function
+// to add the necessary CFI unwinding information and a C function to bridge
+// from that back into C++.
+
+// FIXME: This works as a short-term solution, but this code really shouldn't be
+// architecture dependent. A better long term solution is to implement remote
+// unwinding and expose the necessary APIs through sanitizer_common and/or ASAN
+// to allow the exception handling thread to gather the crash state directly.
+//
+// Alternatively, Fuchsia may in future actually implement basic signal
+// handling for the machine trap signals.
+#if defined(__x86_64__)
+#define FOREACH_REGISTER(OP_REG, OP_NUM) \
+ OP_REG(rax) \
+ OP_REG(rbx) \
+ OP_REG(rcx) \
+ OP_REG(rdx) \
+ OP_REG(rsi) \
+ OP_REG(rdi) \
+ OP_REG(rbp) \
+ OP_REG(rsp) \
+ OP_REG(r8) \
+ OP_REG(r9) \
+ OP_REG(r10) \
+ OP_REG(r11) \
+ OP_REG(r12) \
+ OP_REG(r13) \
+ OP_REG(r14) \
+ OP_REG(r15) \
+ OP_REG(rip)
+
+#elif defined(__aarch64__)
+#define FOREACH_REGISTER(OP_REG, OP_NUM) \
+ OP_NUM(0) \
+ OP_NUM(1) \
+ OP_NUM(2) \
+ OP_NUM(3) \
+ OP_NUM(4) \
+ OP_NUM(5) \
+ OP_NUM(6) \
+ OP_NUM(7) \
+ OP_NUM(8) \
+ OP_NUM(9) \
+ OP_NUM(10) \
+ OP_NUM(11) \
+ OP_NUM(12) \
+ OP_NUM(13) \
+ OP_NUM(14) \
+ OP_NUM(15) \
+ OP_NUM(16) \
+ OP_NUM(17) \
+ OP_NUM(18) \
+ OP_NUM(19) \
+ OP_NUM(20) \
+ OP_NUM(21) \
+ OP_NUM(22) \
+ OP_NUM(23) \
+ OP_NUM(24) \
+ OP_NUM(25) \
+ OP_NUM(26) \
+ OP_NUM(27) \
+ OP_NUM(28) \
+ OP_NUM(29) \
+ OP_NUM(30) \
+ OP_REG(sp)
+
+#else
+#error "Unsupported architecture for fuzzing on Fuchsia"
+#endif
+
+// Produces a CFI directive for the named or numbered register.
+#define CFI_OFFSET_REG(reg) ".cfi_offset " #reg ", %c[" #reg "]\n"
+#define CFI_OFFSET_NUM(num) CFI_OFFSET_REG(r##num)
+
+// Produces an assembler input operand for the named or numbered register.
+#define ASM_OPERAND_REG(reg) \
+ [reg] "i"(offsetof(zx_thread_state_general_regs_t, reg)),
+#define ASM_OPERAND_NUM(num) \
+ [r##num] "i"(offsetof(zx_thread_state_general_regs_t, r[num])),
+
+// Trampoline to bridge from the assembly below to the static C++ crash
+// callback.
+__attribute__((noreturn))
+static void StaticCrashHandler() {
Fuzzer::StaticCrashSignalCallback();
+ for (;;) {
+ _Exit(1);
+ }
+}
+
+// Creates the trampoline with the necessary CFI information to unwind through
+// to the crashing call stack. The attribute is necessary because the function
+// is never called; it's just a container around the assembly to allow it to
+// use operands for compile-time computed constants.
+__attribute__((used))
+void MakeTrampoline() {
+ __asm__(".cfi_endproc\n"
+ ".pushsection .text.CrashTrampolineAsm\n"
+ ".type CrashTrampolineAsm,STT_FUNC\n"
+"CrashTrampolineAsm:\n"
+ ".cfi_startproc simple\n"
+ ".cfi_signal_frame\n"
+#if defined(__x86_64__)
+ ".cfi_return_column rip\n"
+ ".cfi_def_cfa rsp, 0\n"
+ FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM)
+ "call %c[StaticCrashHandler]\n"
+ "ud2\n"
+#elif defined(__aarch64__)
+ ".cfi_return_column 33\n"
+ ".cfi_def_cfa sp, 0\n"
+ ".cfi_offset 33, %c[pc]\n"
+ FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM)
+ "bl %[StaticCrashHandler]\n"
+#else
+#error "Unsupported architecture for fuzzing on Fuchsia"
+#endif
+ ".cfi_endproc\n"
+ ".size CrashTrampolineAsm, . - CrashTrampolineAsm\n"
+ ".popsection\n"
+ ".cfi_startproc\n"
+ : // No outputs
+ : FOREACH_REGISTER(ASM_OPERAND_REG, ASM_OPERAND_NUM)
+#if defined(__aarch64__)
+ ASM_OPERAND_REG(pc)
+#endif
+ [StaticCrashHandler] "i" (StaticCrashHandler));
+}
+
+void CrashHandler(zx_handle_t *Event) {
+ // This structure is used to ensure we close handles to objects we create in
+ // this handler.
+ struct ScopedHandle {
+ ~ScopedHandle() { _zx_handle_close(Handle); }
+ zx_handle_t Handle = ZX_HANDLE_INVALID;
+ };
+
+ // Create and bind the exception port. We need to claim to be a "debugger" so
+ // the kernel will allow us to modify and resume dying threads (see below).
+ // Once the port is set, we can signal the main thread to continue and wait
+ // for the exception to arrive.
+ ScopedHandle Port;
+ ExitOnErr(_zx_port_create(0, &Port.Handle), "_zx_port_create");
+ zx_handle_t Self = _zx_process_self();
+
+ ExitOnErr(_zx_task_bind_exception_port(Self, Port.Handle, kFuzzingCrash,
+ ZX_EXCEPTION_PORT_DEBUGGER),
+ "_zx_task_bind_exception_port");
+
+ ExitOnErr(_zx_object_signal(*Event, 0, ZX_USER_SIGNAL_0),
+ "_zx_object_signal");
+
+ zx_port_packet_t Packet;
+ ExitOnErr(_zx_port_wait(Port.Handle, ZX_TIME_INFINITE, &Packet),
+ "_zx_port_wait");
+
+ // At this point, we want to get the state of the crashing thread, but
+ // libFuzzer and the sanitizers assume this will happen from that same thread
+ // via a POSIX signal handler. "Resurrecting" the thread in the middle of the
+ // appropriate callback is as simple as forcibly setting the instruction
+ // pointer/program counter, provided we NEVER EVER return from that function
+ // (since otherwise our stack will not be valid).
+ ScopedHandle Thread;
+ ExitOnErr(_zx_object_get_child(Self, Packet.exception.tid,
+ ZX_RIGHT_SAME_RIGHTS, &Thread.Handle),
+ "_zx_object_get_child");
+
+ zx_thread_state_general_regs_t GeneralRegisters;
+ ExitOnErr(_zx_thread_read_state(Thread.Handle, ZX_THREAD_STATE_GENERAL_REGS,
+ &GeneralRegisters, sizeof(GeneralRegisters)),
+ "_zx_thread_read_state");
+
+ // To unwind properly, we need to push the crashing thread's register state
+ // onto the stack and jump into a trampoline with CFI instructions on how
+ // to restore it.
+#if defined(__x86_64__)
+ uintptr_t StackPtr =
+ (GeneralRegisters.rsp - (128 + sizeof(GeneralRegisters))) &
+ -(uintptr_t)16;
+ __unsanitized_memcpy(reinterpret_cast<void *>(StackPtr), &GeneralRegisters,
+ sizeof(GeneralRegisters));
+ GeneralRegisters.rsp = StackPtr;
+ GeneralRegisters.rip = reinterpret_cast<zx_vaddr_t>(CrashTrampolineAsm);
+
+#elif defined(__aarch64__)
+ uintptr_t StackPtr =
+ (GeneralRegisters.sp - sizeof(GeneralRegisters)) & -(uintptr_t)16;
+ __unsanitized_memcpy(reinterpret_cast<void *>(StackPtr), &GeneralRegisters,
+ sizeof(GeneralRegisters));
+ GeneralRegisters.sp = StackPtr;
+ GeneralRegisters.pc = reinterpret_cast<zx_vaddr_t>(CrashTrampolineAsm);
+
+#else
+#error "Unsupported architecture for fuzzing on Fuchsia"
+#endif
+
+ // Now force the crashing thread's state.
+ ExitOnErr(_zx_thread_write_state(Thread.Handle, ZX_THREAD_STATE_GENERAL_REGS,
+ &GeneralRegisters, sizeof(GeneralRegisters)),
+ "_zx_thread_write_state");
+
+ ExitOnErr(_zx_task_resume_from_exception(Thread.Handle, Port.Handle, 0),
+ "_zx_task_resume_from_exception");
}
} // namespace
// Platform specific functions.
void SetSignalHandler(const FuzzingOptions &Options) {
- zx_status_t rc;
-
// Set up alarm handler if needed.
if (Options.UnitTimeoutSec > 0) {
std::thread T(AlarmHandler, Options.UnitTimeoutSec / 2 + 1);
@@ -99,23 +307,16 @@ void SetSignalHandler(const FuzzingOptions &Options) {
!Options.HandleFpe && !Options.HandleAbrt)
return;
- // Create an exception port
- zx_handle_t *ExceptionPort = new zx_handle_t;
- if ((rc = _zx_port_create(0, ExceptionPort)) != ZX_OK) {
- Printf("libFuzzer: zx_port_create failed: %s\n", _zx_status_get_string(rc));
- exit(1);
- }
+ // Set up the crash handler and wait until it is ready before proceeding.
+ zx_handle_t Event;
+ ExitOnErr(_zx_event_create(0, &Event), "_zx_event_create");
- // Bind the port to receive exceptions from our process
- if ((rc = _zx_task_bind_exception_port(_zx_process_self(), *ExceptionPort,
- kFuzzingCrash, 0)) != ZX_OK) {
- Printf("libFuzzer: unable to bind exception port: %s\n",
- _zx_status_get_string(rc));
- exit(1);
- }
+ std::thread T(CrashHandler, &Event);
+ zx_status_t Status =
+ _zx_object_wait_one(Event, ZX_USER_SIGNAL_0, ZX_TIME_INFINITE, nullptr);
+ _zx_handle_close(Event);
+ ExitOnErr(Status, "_zx_object_wait_one");
- // Set up the crash handler.
- std::thread T(CrashHandler, ExceptionPort);
T.detach();
}
@@ -127,7 +328,7 @@ unsigned long GetPid() {
zx_status_t rc;
zx_info_handle_basic_t Info;
if ((rc = _zx_object_get_info(_zx_process_self(), ZX_INFO_HANDLE_BASIC, &Info,
- sizeof(Info), NULL, NULL)) != ZX_OK) {
+ sizeof(Info), NULL, NULL)) != ZX_OK) {
Printf("libFuzzer: unable to get info about self: %s\n",
_zx_status_get_string(rc));
exit(1);
@@ -139,7 +340,7 @@ size_t GetPeakRSSMb() {
zx_status_t rc;
zx_info_task_stats_t Info;
if ((rc = _zx_object_get_info(_zx_process_self(), ZX_INFO_TASK_STATS, &Info,
- sizeof(Info), NULL, NULL)) != ZX_OK) {
+ sizeof(Info), NULL, NULL)) != ZX_OK) {
Printf("libFuzzer: unable to get info about self: %s\n",
_zx_status_get_string(rc));
exit(1);