diff --git a/compiler-rt/lib/asan/asan_fuchsia.cpp b/compiler-rt/lib/asan/asan_fuchsia.cpp --- a/compiler-rt/lib/asan/asan_fuchsia.cpp +++ b/compiler-rt/lib/asan/asan_fuchsia.cpp @@ -62,7 +62,29 @@ UNIMPLEMENTED(); } -bool PlatformUnpoisonStacks() { return false; } +bool PlatformUnpoisonStacks() { + // Is the current stack the default one? If not, + // we are in a different stack (might be a crash stack from fuzzing). + // Unpoison the original stack and the current stack page. + AsanThread *curr_thread = GetCurrentThread(); + CHECK(curr_thread != nullptr); + uptr top = curr_thread->stack_top(); + uptr bottom = curr_thread->stack_bottom(); + + uptr local_stack = reinterpret_cast(__builtin_frame_address(0)); + + if (local_stack < bottom || local_stack > top) { + // The current stack is not the default stack. + // Unpoison the entire default stack and the current stack page. + UnpoisonStack(bottom, top, "default"); + bottom = RoundDownTo(local_stack, GetPageSize()); + top = bottom + GetPageSize(); + UnpoisonStack(bottom, top, "unknown"); + return true; + } + + return false; +} // We can use a plain thread_local variable for TSD. static thread_local void *per_thread; diff --git a/compiler-rt/lib/fuzzer/CMakeLists.txt b/compiler-rt/lib/fuzzer/CMakeLists.txt --- a/compiler-rt/lib/fuzzer/CMakeLists.txt +++ b/compiler-rt/lib/fuzzer/CMakeLists.txt @@ -15,6 +15,7 @@ FuzzerMutate.cpp FuzzerSHA1.cpp FuzzerTracePC.cpp + FuzzerUnwindFuchsia.S FuzzerUtil.cpp FuzzerUtilDarwin.cpp FuzzerUtilFuchsia.cpp @@ -43,6 +44,7 @@ FuzzerRandom.h FuzzerSHA1.h FuzzerTracePC.h + FuzzerUnwindFuchsia.h FuzzerUtil.h FuzzerValueBitMap.h) diff --git a/compiler-rt/lib/fuzzer/FuzzerUnwindFuchsia.h b/compiler-rt/lib/fuzzer/FuzzerUnwindFuchsia.h new file mode 100644 --- /dev/null +++ b/compiler-rt/lib/fuzzer/FuzzerUnwindFuchsia.h @@ -0,0 +1,164 @@ +//===- FuzzerUnwindFuchsia.h - Unwind information for Fuchsia. -------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// This file contains dwarf registers numbering and the offsets of each +// register into the zx_thread_state_general_regs_t struct. This is to be +// consumed from assembly. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" + +#if LIBFUZZER_FUCHSIA + +#if defined(__x86_64__) +// DWARF Register Number Mappings for x86-64 +// Extracted from: +// System V Application Binary Interface +// AMD64 Architecture Processor Supplement +// Draft Version 0.99.7 +// https://www.uclibc.org/docs/psABI-x86_64.pdf + +#define REG_DWARF_NUMBER_rax 0 +#define REG_DWARF_NUMBER_rbx 3 +#define REG_DWARF_NUMBER_rcx 2 +#define REG_DWARF_NUMBER_rdx 1 +#define REG_DWARF_NUMBER_rsi 4 +#define REG_DWARF_NUMBER_rdi 5 +#define REG_DWARF_NUMBER_rbp 6 +#define REG_DWARF_NUMBER_rsp 7 +#define REG_DWARF_NUMBER_r8 8 +#define REG_DWARF_NUMBER_r9 9 +#define REG_DWARF_NUMBER_r10 10 +#define REG_DWARF_NUMBER_r11 11 +#define REG_DWARF_NUMBER_r12 12 +#define REG_DWARF_NUMBER_r13 13 +#define REG_DWARF_NUMBER_r14 14 +#define REG_DWARF_NUMBER_r15 15 +#define REG_DWARF_NUMBER_rip 16 + +#define REG_DWARF_NUMBER(reg) (REG_DWARF_NUMBER_##reg) + +// Offsets for different x86-64 registers into the +// zx_thread_state_general_regs_t structure. +#define REG_OFFSET_rax 0 +#define REG_OFFSET_rbx 8 +#define REG_OFFSET_rcx 16 +#define REG_OFFSET_rdx 24 +#define REG_OFFSET_rsi 32 +#define REG_OFFSET_rdi 40 +#define REG_OFFSET_rbp 48 +#define REG_OFFSET_rsp 56 +#define REG_OFFSET_r8 64 +#define REG_OFFSET_r9 72 +#define REG_OFFSET_r10 80 +#define REG_OFFSET_r11 88 +#define REG_OFFSET_r12 96 +#define REG_OFFSET_r13 104 +#define REG_OFFSET_r14 112 +#define REG_OFFSET_r15 120 +#define REG_OFFSET_rip 128 +#define REG_OFFSET(reg) (REG_OFFSET_##reg) + +#define REG_FOREACH(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__) + +// DWARF Register Number Mappings for ARM64 +// Extracted from: +// DWARF for the ARM 64-bit Architecture +// 2020Q4 +// https://github.com/ARM-software/abi-aa/blob/f52e1ad3f81254497a83578dc102f6aac89e52d0/aadwarf64/aadwarf64.rst + +#define REG_DWARF_NUMBER_x(num) (num) +#define REG_DWARF_NUMBER_lr (30) +#define REG_DWARF_NUMBER_sp (31) +#define REG_DWARF_NUMBER_pc (32) +#define REG_DWARF_NUMBER(reg) (REG_DWARF_NUMBER_##reg) + +// Offsets for different arm64 registers into the +// zx_thread_state_general_regs_t structure. +#define REG_OFFSET_lr (30 * 8) +#define REG_OFFSET_sp (31 * 8) +#define REG_OFFSET_pc (32 * 8) +#define REG_OFFSET_x(num) (num * 8) +#define REG_OFFSET(reg) (REG_OFFSET_##reg) + +#define REG_FOREACH(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_REG(lr) \ + OP_REG(sp) \ + OP_REG(pc) + +#else +#error "Unsupported architecture for fuzzing on Fuchsia" +#endif + +#ifdef __cplusplus + +#include + +extern "C" void CrashTrampoline(void); + +// The register offsets have to match the zx_thread_state_general_regs_t layout. +#define CHECK_OFFSET(reg) \ + static_assert(offsetof(zx_thread_state_general_regs_t, reg) == \ + REG_OFFSET(reg), \ + "invalid offset"); +#define CHECK_OFFSET_NUM(num) \ + static_assert(offsetof(zx_thread_state_general_regs_t, r[num]) == \ + REG_OFFSET_x(num), \ + "invalid offset"); + +REG_FOREACH(CHECK_OFFSET, CHECK_OFFSET_NUM) +#endif // __cplusplus + +#endif // LIBFUZZER_FUCHSIA diff --git a/compiler-rt/lib/fuzzer/FuzzerUnwindFuchsia.S b/compiler-rt/lib/fuzzer/FuzzerUnwindFuchsia.S new file mode 100644 --- /dev/null +++ b/compiler-rt/lib/fuzzer/FuzzerUnwindFuchsia.S @@ -0,0 +1,130 @@ +//===- FuzzerFuchsiaCrashTrampoline.S - Crash Trampoline function for Fuchsia.// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// CrashTrampoline function to be used by libfuzzer in Fuchsia while catching +// crashes. The CrashTrampoline function just calls into a function it receives +// as parameter. It has the appropriate CFI directives for libunwind to unwind +// the full backtrace. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" +#include "FuzzerUnwindFuchsia.h" + +#if LIBFUZZER_FUCHSIA + +// These macros expand into the low and high bytes of the 2-byte +// ULEB128 encoding of the number X. +// The low byte is bits [0, 7) of X, with a 1 in bit 7. +// The high byte is bits [7, 14) of X, with a 0 in bit 15. +// It is only valid to call these macros with 0 <= X < 0x3FFF +#define ULEB128_2_LO(X) (((X) & 0x7F) | 0x80) +#define ULEB128_2_HI(X) (((X) >> 7) & 0x7F) + +#define DW_CFA_def_cfa_expression 0x0f +#define DW_CFA_expression 0x10 +#define DW_OP_breg7 0x77 +#define DW_OP_breg31 0x8f +#define DW_OP_deref 0x06 + +// The trampoline function gets called with a new stack that contains the +// registers state at the point of the crash. In order to unwind it correctly, +// we need to set the CFA to the previous stack pointer (which is stored in +// the stack), and add information to restore the register values, so +// unwinding can continue. +// +// Sadly, all the cfi macros to specify register values are relative to +// the CFA, which in this case is far away from where the registers are +// stored. Thus, we have to use the .cfi_escape macro to add custom +// directives. These directives are just dwarf expressions that tells where +// each register is located relative to the current stack pointer. +#if defined(__x86_64__) + +#define cfi_cfa_deref_sp(offset) .cfi_escape \ + DW_CFA_def_cfa_expression, \ + 0x04, /* size */ \ + DW_OP_breg7, \ + ULEB128_2_LO(offset), /* offset*/ \ + ULEB128_2_HI(offset), /* offset*/ \ + DW_OP_deref + +#define cfi_reg_from_sp(regno, offset) .cfi_escape \ + DW_CFA_expression, \ + regno, /* register number */ \ + 0x03, /* size */ \ + DW_OP_breg7, \ + ULEB128_2_LO(offset), /* offset */\ + ULEB128_2_HI(offset) /* offset */ + +#define cfi_num(num) +#define cfi_reg(reg) cfi_reg_from_sp(REG_DWARF_NUMBER(reg), REG_OFFSET(reg)) + +#elif defined(__aarch64__) + +#define cfi_cfa_deref_sp(offset) .cfi_escape \ + DW_CFA_def_cfa_expression, \ + 0x04, /* size */ \ + DW_OP_breg31, \ + ULEB128_2_LO(offset), /* offset*/ \ + ULEB128_2_HI(offset), /* offset*/ \ + DW_OP_deref + +#define cfi_reg_from_sp(regno, offset) .cfi_escape \ + DW_CFA_expression, \ + regno, /* register number */ \ + 0x03, /* size */ \ + DW_OP_breg31, \ + ULEB128_2_LO(offset), /* offset */\ + ULEB128_2_HI(offset) /* offset */ + +#define cfi_num(num) cfi_reg_from_sp(REG_DWARF_NUMBER_x(num), REG_OFFSET_x(num)) +#define cfi_reg(reg) cfi_reg_from_sp(REG_DWARF_NUMBER(reg), REG_OFFSET(reg)) + +#else +#error "Unsupported architecture for fuzzing on Fuchsia" +#endif + +// Trampoline function with the necessary CFI information to unwind through +// to the crashing call stack. This function gets called with a new stack, that +// contains the registers at the point of the crash, and also receives a +// function pointer to the libfuzzer crash handler so it can call it. +// +// The function contains the following CFI directives for the unwinder: +// +// * cfi_cfa_deref_sp: defines the CFA so that it points to the stack pointer +// at the point of crash. +// * cfi_return_column: when unwinding, the return address will be stored in +// the pc register. +// * cfi_reg/cfi_num: tells the unwinder how to restore each of the registers +// from the stack. + + .text + .global CrashTrampoline + .type CrashTrampoline, %function +CrashTrampoline: + .cfi_startproc simple + .cfi_signal_frame + +#if defined(__x86_64__) + cfi_cfa_deref_sp(REG_OFFSET(rsp)) + .cfi_return_column rip + REG_FOREACH(cfi_reg, cfi_num) + callq *%rdi + ud2 + +#elif defined(__aarch64__) + cfi_cfa_deref_sp(REG_OFFSET(sp)) + .cfi_return_column REG_DWARF_NUMBER(pc) + REG_FOREACH(cfi_reg, cfi_num) + blr x0 + brk 1 + +#else +#error "Unsupported architecture for fuzzing on Fuchsia" +#endif + .cfi_endproc + .size CrashTrampoline, . - CrashTrampoline + +#endif // LIBFUZZER_FUCHSIA diff --git a/compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp b/compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp --- a/compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp @@ -12,6 +12,7 @@ #if LIBFUZZER_FUCHSIA #include "FuzzerInternal.h" +#include "FuzzerUnwindFuchsia.h" #include "FuzzerUtil.h" #include #include @@ -44,12 +45,6 @@ // 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 { // Helper function to handle Zircon syscall failures. @@ -68,106 +63,6 @@ } } -// CFAOffset is used to reference the stack pointer before entering the -// trampoline (Stack Pointer + CFAOffset = prev Stack Pointer). Before jumping -// to the trampoline we copy all the registers onto the stack. We need to make -// sure that the new stack has enough space to store all the registers. -// -// The trampoline holds CFI information regarding the registers stored in the -// stack, which is then used by the unwinder to restore them. -#if defined(__x86_64__) -// In x86_64 the crashing function might also be using the red zone (128 bytes -// on top of their rsp). -constexpr size_t CFAOffset = 128 + sizeof(zx_thread_state_general_regs_t); -#elif defined(__aarch64__) -// In aarch64 we need to always have the stack pointer aligned to 16 bytes, so we -// make sure that we are keeping that same alignment. -constexpr size_t CFAOffset = (sizeof(zx_thread_state_general_regs_t) + 15) & -(uintptr_t)16; -#endif - -// 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_REG(sp) - -#else -#error "Unsupported architecture for fuzzing on Fuchsia" -#endif - -// Produces a CFI directive for the named or numbered register. -// The value used refers to an assembler immediate operand with the same name -// as the register (see ASM_OPERAND_REG). -#define CFI_OFFSET_REG(reg) ".cfi_offset " #reg ", %c[" #reg "]\n" -#define CFI_OFFSET_NUM(num) CFI_OFFSET_REG(x##num) - -// Produces an assembler immediate operand for the named or numbered register. -// This operand contains the offset of the register relative to the CFA. -#define ASM_OPERAND_REG(reg) \ - [reg] "i"(offsetof(zx_thread_state_general_regs_t, reg) - CFAOffset), -#define ASM_OPERAND_NUM(num) \ - [x##num] "i"(offsetof(zx_thread_state_general_regs_t, r[num]) - CFAOffset), - // Trampoline to bridge from the assembly below to the static C++ crash // callback. __attribute__((noreturn)) @@ -178,66 +73,72 @@ } } -// Creates the trampoline with the necessary CFI information to unwind through -// to the crashing call stack: -// * Defining the CFA so that it points to the stack pointer at the point -// of crash. -// * Storing all registers at the point of crash in the stack and refer to them -// via CFI information (relative to the CFA). -// * Setting the return column so the unwinder knows how to continue unwinding. -// * (x86_64) making sure rsp is aligned before calling StaticCrashHandler. -// * Calling StaticCrashHandler that will trigger the unwinder. -// -// The __attribute__((used)) 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" +// The crashing thread might not have a valid stack. +// Before executing the crash trampoline, we set up a new stack for them. +constexpr size_t kCrashStackSize = 4096 * 4; +uint8_t CrashStack[kCrashStackSize] __attribute__((aligned(16))); + +zx_thread_state_general_regs_t +SetupCrashTrampoline(const zx_thread_state_general_regs_t ®isters) { + // 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. Make sure that the size is aligned to 16 bytes, as in arm + // the stack needs to follow that alignment. + uintptr_t StackPtr = + reinterpret_cast(&CrashStack[kCrashStackSize]) - + ((sizeof(registers) + 15) & ~(uintptr_t)15); + + __unsanitized_memcpy(reinterpret_cast(StackPtr), ®isters, + sizeof(registers)); + + // Minimal registers needed for continuing execution: + // + A new stack that holds the register values at point of crash. + // + Resume execution from the CrashTrampoline which contains + // CFI directives for restoring registers from the stack and unwind. + // + A pointer to the static crash handler. + // + Platform reserved registers (fs/tpidr for TLS, r18 for ShadowCallStack) #if defined(__x86_64__) - ".cfi_return_column rip\n" - ".cfi_def_cfa rsp, %c[CFAOffset]\n" - FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM) - "mov %%rsp, %%rbp\n" - ".cfi_def_cfa_register rbp\n" - "andq $-16, %%rsp\n" - "call %c[StaticCrashHandler]\n" - "ud2\n" + zx_thread_state_general_regs_t res = { + .rdi = reinterpret_cast(StaticCrashHandler), + .rsp = StackPtr, + .rip = reinterpret_cast(CrashTrampoline), + .fs_base = registers.fs_base, + .rflags = registers.rflags, + }; #elif defined(__aarch64__) - ".cfi_return_column 33\n" - ".cfi_def_cfa sp, %c[CFAOffset]\n" - FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM) - ".cfi_offset 33, %c[pc]\n" - ".cfi_offset 30, %c[lr]\n" - "bl %c[StaticCrashHandler]\n" - "brk 1\n" + zx_thread_state_general_regs_t res = { + .r[0] = reinterpret_cast(StaticCrashHandler), + .r[18] = registers.r[18], + .sp = StackPtr, + .pc = reinterpret_cast(CrashTrampoline), + .cpsr = registers.cpsr, + .tpidr = registers.tpidr, + }; #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) - ASM_OPERAND_REG(lr) -#endif - [StaticCrashHandler] "i" (StaticCrashHandler), - [CFAOffset] "i" (CFAOffset)); + return res; } 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); } + class ScopedHandle { + public: + ScopedHandle() { reset(); } + explicit ScopedHandle(zx_handle_t H) { reset(H); } + ScopedHandle(ScopedHandle &&Other) : Handle(Other.release()) {} + ScopedHandle &operator=(ScopedHandle &&Other) { + Handle = Other.release(); + return *this; + } + ScopedHandle(const ScopedHandle &Other) = delete; + ScopedHandle &operator=(const ScopedHandle &) = delete; + ~ScopedHandle() { reset(Handle); } + zx_handle_t release() { return std::exchange(Handle, ZX_HANDLE_INVALID); } + void reset(zx_handle_t H = ZX_HANDLE_INVALID) { + _zx_handle_close(std::exchange(Handle, H)); + } zx_handle_t Handle = ZX_HANDLE_INVALID; }; @@ -254,6 +155,9 @@ ExitOnErr(_zx_object_signal(*Event, 0, ZX_USER_SIGNAL_0), "_zx_object_signal"); + zx_koid_t crashed_tid = ZX_KOID_INVALID; + std::vector unhandled_exceptions; + // This thread lives as long as the process in order to keep handling // crashes. In practice, the first crashed thread to reach the end of the // StaticCrashHandler will end the process. @@ -276,6 +180,10 @@ continue; } + // 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. + // 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 @@ -292,28 +200,28 @@ 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 - CFAOffset; - __unsanitized_memcpy(reinterpret_cast(StackPtr), &GeneralRegisters, - sizeof(GeneralRegisters)); - GeneralRegisters.rsp = StackPtr; - GeneralRegisters.rip = reinterpret_cast(CrashTrampolineAsm); - -#elif defined(__aarch64__) - uintptr_t StackPtr = GeneralRegisters.sp - CFAOffset; - __unsanitized_memcpy(reinterpret_cast(StackPtr), &GeneralRegisters, - sizeof(GeneralRegisters)); - GeneralRegisters.sp = StackPtr; - GeneralRegisters.pc = reinterpret_cast(CrashTrampolineAsm); + if (crashed_tid != ZX_KOID_INVALID) { + // If we reach this point it means that we received more than + // one exception. This new exception could either be from the + // same thread as the first exception or from another thread. + Printf("libFuzzer: An exception occurred while handling a previous " + "crash.\n"); + if (crashed_tid == ExceptionInfo.tid) { + // Exception came from the same thread. This means that the exception + // handling code crashed. This is bad. Generate a crashing artifact and + // exit. + exit(1); + } else { + // We received a crash from a different thread. Leave it suspended and + // wait for the next exception. + unhandled_exceptions.push_back(std::move(Exception)); + continue; + } + } -#else -#error "Unsupported architecture for fuzzing on Fuchsia" -#endif + crashed_tid = ExceptionInfo.tid; + GeneralRegisters = SetupCrashTrampoline(GeneralRegisters); - // Now force the crashing thread's state. ExitOnErr( _zx_thread_write_state(Thread.Handle, ZX_THREAD_STATE_GENERAL_REGS, &GeneralRegisters, sizeof(GeneralRegisters)),