diff options
Diffstat (limited to 'compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp')
-rw-r--r-- | compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp | 77 |
1 files changed, 61 insertions, 16 deletions
diff --git a/compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp b/compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp index 79fd950bbf97..bde9f68d62aa 100644 --- a/compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp @@ -18,6 +18,7 @@ #include <cinttypes> #include <cstdint> #include <fcntl.h> +#include <lib/fdio/fdio.h> #include <lib/fdio/spawn.h> #include <string> #include <sys/select.h> @@ -76,6 +77,23 @@ void InterruptHandler() { Fuzzer::StaticInterruptCallback(); } +// 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 @@ -140,7 +158,6 @@ void InterruptHandler() { OP_NUM(27) \ OP_NUM(28) \ OP_NUM(29) \ - OP_NUM(30) \ OP_REG(sp) #else @@ -148,14 +165,17 @@ void InterruptHandler() { #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(r##num) +#define CFI_OFFSET_NUM(num) CFI_OFFSET_REG(x##num) -// Produces an assembler input operand for the named or numbered register. +// 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)), + [reg] "i"(offsetof(zx_thread_state_general_regs_t, reg) - CFAOffset), #define ASM_OPERAND_NUM(num) \ - [r##num] "i"(offsetof(zx_thread_state_general_regs_t, r[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. @@ -168,7 +188,16 @@ static void StaticCrashHandler() { } // Creates the trampoline with the necessary CFI information to unwind through -// to the crashing call stack. The attribute is necessary because the function +// 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)) @@ -181,16 +210,21 @@ void MakeTrampoline() { ".cfi_signal_frame\n" #if defined(__x86_64__) ".cfi_return_column rip\n" - ".cfi_def_cfa rsp, 0\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" #elif defined(__aarch64__) ".cfi_return_column 33\n" - ".cfi_def_cfa sp, 0\n" - ".cfi_offset 33, %c[pc]\n" + ".cfi_def_cfa sp, %c[CFAOffset]\n" FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM) - "bl %[StaticCrashHandler]\n" + ".cfi_offset 33, %c[pc]\n" + ".cfi_offset 30, %c[lr]\n" + "bl %c[StaticCrashHandler]\n" + "brk 1\n" #else #error "Unsupported architecture for fuzzing on Fuchsia" #endif @@ -202,8 +236,10 @@ void MakeTrampoline() { : FOREACH_REGISTER(ASM_OPERAND_REG, ASM_OPERAND_NUM) #if defined(__aarch64__) ASM_OPERAND_REG(pc) + ASM_OPERAND_REG(lr) #endif - [StaticCrashHandler] "i" (StaticCrashHandler)); + [StaticCrashHandler] "i" (StaticCrashHandler), + [CFAOffset] "i" (CFAOffset)); } void CrashHandler(zx_handle_t *Event) { @@ -269,17 +305,14 @@ void CrashHandler(zx_handle_t *Event) { // 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; + uintptr_t StackPtr = GeneralRegisters.rsp - CFAOffset; __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; + uintptr_t StackPtr = GeneralRegisters.sp - CFAOffset; __unsanitized_memcpy(reinterpret_cast<void *>(StackPtr), &GeneralRegisters, sizeof(GeneralRegisters)); GeneralRegisters.sp = StackPtr; @@ -497,6 +530,18 @@ const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt, return memmem(Data, DataLen, Patt, PattLen); } +// In fuchsia, accessing /dev/null is not supported. There's nothing +// similar to a file that discards everything that is written to it. +// The way of doing something similar in fuchsia is by using +// fdio_null_create and binding that to a file descriptor. +void DiscardOutput(int Fd) { + fdio_t *fdio_null = fdio_null_create(); + if (fdio_null == nullptr) return; + int nullfd = fdio_bind_to_fd(fdio_null, -1, 0); + if (nullfd < 0) return; + dup2(nullfd, Fd); +} + } // namespace fuzzer #endif // LIBFUZZER_FUCHSIA |