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 @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -209,7 +210,100 @@ [StaticCrashHandler] "i"(StaticCrashHandler)); } +uintptr_t CreateStack(size_t Size, const std::string &Name) { + zx_handle_t Vmo; + ExitOnErr(_zx_vmo_create(Size, 0, &Vmo), "_zx_vmo_create"); + + const size_t PageSize = _zx_system_get_page_size(); + + ExitOnErr( + _zx_object_set_property(Vmo, ZX_PROP_NAME, Name.c_str(), Name.length()), + "_zx_object_set_property"); + + zx_handle_t Vmar = zx_vmar_root_self(); + uintptr_t Addr; + ExitOnErr(_zx_vmar_allocate(Vmar, + ZX_VM_CAN_MAP_READ | ZX_VM_CAN_MAP_WRITE | + ZX_VM_CAN_MAP_SPECIFIC, + 0, Size + 2 * PageSize, &Vmar, &Addr), + "_zx_vmar_allocate"); + + zx_vaddr_t StackBase; + zx_vm_option_t Opts = ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_SPECIFIC; + + // Map the Vmo leaving one guard page before and after. + ExitOnErr(_zx_vmar_map(Vmar, Opts, PageSize, Vmo, /* vmo_offset */ 0, Size, + &StackBase), + "_zx_vmar_map"); + + return static_cast(StackBase); +} + +zx_thread_state_general_regs_t +SetupTrampoline(const zx_thread_state_general_regs_t ®isters, + uintptr_t SafeStack, uintptr_t UnsafeStack, + uintptr_t ShadowCallStack) { + + // Align the stack to 16 bytes and copy crashed thread's regs so they + // can be restored by the unwinder. + uintptr_t StackPtr = + (SafeStack - (sizeof(registers) + 0xF)) & ~(uintptr_t)0xF; + __unsanitized_memcpy(reinterpret_cast(StackPtr), ®isters, + sizeof(registers)); + +#if defined(__x86_64__) + static_assert(!__has_feature(ShadowCallStack)); + zx_thread_state_general_regs_t res = { + .rdi = reinterpret_cast(StaticCrashHandler), + .rsp = StackPtr, + .rip = reinterpret_cast(CrashTrampolineAsm), + .rflags = registers.rflags, + .fs_base = registers.fs_base, + }; + + // The unsafe stack is located near the crashed threads Thread Pointer. + // If they have an invalid Thread Pointer, the fuzzer program will crash. + uintptr_t *UnsafeStackLocation = reinterpret_cast( + registers.fs_base + ZX_TLS_UNSAFE_SP_OFFSET); + *UnsafeStackLocation = UnsafeStack; +#elif defined(__aarch64__) + zx_thread_state_general_regs_t res = { + .r[0] = reinterpret_cast(StaticCrashHandler), + .sp = StackPtr, + .pc = reinterpret_cast(CrashTrampolineAsm), + .cpsr = registers.cpsr, + .tpidr = registers.tpidr, + }; + + // The unsafe stack is located near the crashed threads Thread Pointer. + // If they have an invalid Thread Pointer, the fuzzer program will crash. + uintptr_t *UnsafeStackLocation = + reinterpret_cast(registers.tpidr + ZX_TLS_UNSAFE_SP_OFFSET); + *UnsafeStackLocation = UnsafeStack; + + if constexpr (__has_feature(shadow_call_stack)) { + res.r[18] = ShadowCallStack; + } +#else +#error "Unsupported architecture for fuzzing on Fuchsia" +#endif + return res; +} + +constexpr size_t kCrashStackSize = 4096 * 16; + void CrashHandler(zx_handle_t *Event) { + uintptr_t SafeStack = + CreateStack(kCrashStackSize, "crash-safestack") + kCrashStackSize; + uintptr_t UnsafeStack = + CreateStack(kCrashStackSize, "crash-unsafestack") + kCrashStackSize; + + uintptr_t ShadowCallStack = 0x0; + if constexpr (__has_feature(shadow_call_stack)) { + // ShadowCallStack grows the other way around. No need to point to the end. + ShadowCallStack = CreateStack(kCrashStackSize, "crash-shadowcallstack"); + } + // ScopedHandle is a helper class that makes sure handles are closed when they // go out of scope. This also allows handles to be moved. class ScopedHandle { @@ -313,26 +407,8 @@ // 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(StackPtr), &GeneralRegisters, - sizeof(GeneralRegisters)); - GeneralRegisters.rsp = StackPtr; - GeneralRegisters.rip = reinterpret_cast(CrashTrampolineAsm); - -#elif defined(__aarch64__) - uintptr_t StackPtr = - (GeneralRegisters.sp - sizeof(GeneralRegisters)) & -(uintptr_t)16; - __unsanitized_memcpy(reinterpret_cast(StackPtr), &GeneralRegisters, - sizeof(GeneralRegisters)); - GeneralRegisters.sp = StackPtr; - GeneralRegisters.pc = reinterpret_cast(CrashTrampolineAsm); - -#else -#error "Unsupported architecture for fuzzing on Fuchsia" -#endif + GeneralRegisters = SetupTrampoline(GeneralRegisters, SafeStack, UnsafeStack, + ShadowCallStack); // Now force the crashing thread's state. ExitOnErr(