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,30 @@ 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(); + + int local_var; + uptr local_stack = reinterpret_cast(&local_stack); + + 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/FuzzerUtilFuchsia.cpp b/compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp --- a/compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp @@ -52,6 +52,11 @@ 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) { @@ -238,6 +243,11 @@ // 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)) {} + ScopedHandle(const ScopedHandle &Other) = delete; + ScopedHandle &operator=(const ScopedHandle &) = delete; zx_handle_t Handle = ZX_HANDLE_INVALID; }; @@ -254,6 +264,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. @@ -292,18 +305,41 @@ sizeof(GeneralRegisters)), "_zx_thread_read_state"); + 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. Exiting here at least + // will generate a crashing artifact. + 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; + } + } + + crashed_tid = ExceptionInfo.tid; + // 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; + uintptr_t StackPtr = + reinterpret_cast(&CrashStack[kCrashStackSize]) - 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; + uintptr_t StackPtr = + reinterpret_cast(&CrashStack[kCrashStackSize]) - CFAOffset; __unsanitized_memcpy(reinterpret_cast(StackPtr), &GeneralRegisters, sizeof(GeneralRegisters)); GeneralRegisters.sp = StackPtr;