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 @@ -7,7 +7,6 @@ FuzzerExtFunctionsWindows.cpp FuzzerExtraCounters.cpp FuzzerFork.cpp - FuzzerFuchsiaCrashTrampoline.S FuzzerIO.cpp FuzzerIOPosix.cpp FuzzerIOWindows.cpp @@ -16,6 +15,7 @@ FuzzerMutate.cpp FuzzerSHA1.cpp FuzzerTracePC.cpp + FuzzerUnwindFuchsia.S FuzzerUtil.cpp FuzzerUtilDarwin.cpp FuzzerUtilFuchsia.cpp @@ -35,7 +35,6 @@ FuzzerExtFunctions.h FuzzerFlags.def FuzzerFork.h - FuzzerFuchsiaRegisters.h FuzzerIO.h FuzzerInterface.h FuzzerInternal.h @@ -45,6 +44,7 @@ FuzzerRandom.h FuzzerSHA1.h FuzzerTracePC.h + FuzzerUnwindFuchsia.h FuzzerUtil.h FuzzerValueBitMap.h) diff --git a/compiler-rt/lib/fuzzer/FuzzerFuchsiaRegisters.h b/compiler-rt/lib/fuzzer/FuzzerUnwindFuchsia.h rename from compiler-rt/lib/fuzzer/FuzzerFuchsiaRegisters.h rename to compiler-rt/lib/fuzzer/FuzzerUnwindFuchsia.h --- a/compiler-rt/lib/fuzzer/FuzzerFuchsiaRegisters.h +++ b/compiler-rt/lib/fuzzer/FuzzerUnwindFuchsia.h @@ -1,4 +1,4 @@ -//===- FuzzerFuchsiaRegisters.h - Register information for Fuchsia. -------===// +//===- 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. @@ -142,4 +142,23 @@ #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/FuzzerFuchsiaCrashTrampoline.S b/compiler-rt/lib/fuzzer/FuzzerUnwindFuchsia.S rename from compiler-rt/lib/fuzzer/FuzzerFuchsiaCrashTrampoline.S rename to compiler-rt/lib/fuzzer/FuzzerUnwindFuchsia.S --- a/compiler-rt/lib/fuzzer/FuzzerFuchsiaCrashTrampoline.S +++ b/compiler-rt/lib/fuzzer/FuzzerUnwindFuchsia.S @@ -11,9 +11,9 @@ // the full backtrace. //===----------------------------------------------------------------------===// #include "FuzzerPlatform.h" +#include "FuzzerUnwindFuchsia.h" #if LIBFUZZER_FUCHSIA -#include "FuzzerFuchsiaRegisters.h" // These macros expand into the low and high bytes of the 2-byte // ULEB128 encoding of the number X. @@ -86,8 +86,6 @@ #error "Unsupported architecture for fuzzing on Fuchsia" #endif -#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 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 @@ -11,8 +11,8 @@ #if LIBFUZZER_FUCHSIA -#include "FuzzerFuchsiaRegisters.h" #include "FuzzerInternal.h" +#include "FuzzerUnwindFuchsia.h" #include "FuzzerUtil.h" #include #include @@ -37,20 +37,6 @@ #include -extern "C" void CrashTrampoline(void); - -// The register offsets have to match the struct 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) - namespace fuzzer { // Given that Fuchsia doesn't have the POSIX signals that libFuzzer was written @@ -61,11 +47,6 @@ namespace { -// 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; -char *CrashStack[kCrashStackSize]; - // Helper function to handle Zircon syscall failures. void ExitOnErr(zx_status_t Status, const char *Syscall) { if (Status != ZX_OK) { @@ -92,16 +73,72 @@ } } +// 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; +char *CrashStack[kCrashStackSize]; + +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__) + 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__) + 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 + 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); } - ScopedHandle() : Handle(ZX_HANDLE_INVALID) {} - ScopedHandle(ScopedHandle &&Other) - : Handle(std::exchange(Other.Handle, ZX_HANDLE_INVALID)) {} + 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; }; @@ -171,8 +208,8 @@ "crash.\n"); if (crashed_tid == ExceptionInfo.tid) { // Exception came from the same thread. This means that the exception - // handling code crashed. This is bad. Exiting here at least - // will generate a crashing artifact. + // 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 @@ -183,31 +220,8 @@ } crashed_tid = ExceptionInfo.tid; + GeneralRegisters = SetupCrashTrampoline(GeneralRegisters); - // 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. - uintptr_t StackPtr = - reinterpret_cast(&CrashStack[kCrashStackSize]) - - sizeof(GeneralRegisters); - __unsanitized_memcpy(reinterpret_cast(StackPtr), &GeneralRegisters, - sizeof(GeneralRegisters)); - -#if defined(__x86_64__) - GeneralRegisters.rsp = StackPtr; - GeneralRegisters.rdi = reinterpret_cast(StaticCrashHandler); - GeneralRegisters.rip = reinterpret_cast(CrashTrampoline); - -#elif defined(__aarch64__) - GeneralRegisters.sp = StackPtr; - GeneralRegisters.r[0] = reinterpret_cast(StaticCrashHandler); - GeneralRegisters.pc = reinterpret_cast(CrashTrampoline); - -#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)),