aboutsummaryrefslogtreecommitdiff
path: root/compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp')
-rw-r--r--compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp77
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