diff options
Diffstat (limited to 'contrib/llvm-project/compiler-rt/lib/gwp_asan/optional')
10 files changed, 897 insertions, 0 deletions
diff --git a/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/backtrace.h b/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/backtrace.h new file mode 100644 index 000000000000..9bb12af206a5 --- /dev/null +++ b/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/backtrace.h @@ -0,0 +1,53 @@ +//===-- backtrace.h ---------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef GWP_ASAN_OPTIONAL_BACKTRACE_H_ +#define GWP_ASAN_OPTIONAL_BACKTRACE_H_ + +#include "gwp_asan/optional/printf.h" +#include "gwp_asan/options.h" + +namespace gwp_asan { +namespace backtrace { +// ================================ Description ================================ +// This function shall take the backtrace provided in `TraceBuffer`, and print +// it in a human-readable format using `Print`. Generally, this function shall +// resolve raw pointers to section offsets and print them with the following +// sanitizer-common format: +// " #{frame_number} {pointer} in {function name} ({binary name}+{offset}" +// e.g. " #5 0x420459 in _start (/tmp/uaf+0x420459)" +// This format allows the backtrace to be symbolized offline successfully using +// llvm-symbolizer. +// =================================== Notes =================================== +// This function may directly or indirectly call malloc(), as the +// GuardedPoolAllocator contains a reentrancy barrier to prevent infinite +// recursion. Any allocation made inside this function will be served by the +// supporting allocator, and will not have GWP-ASan protections. +typedef void (*PrintBacktrace_t)(uintptr_t *TraceBuffer, size_t TraceLength, + Printf_t Print); + +// Returns a function pointer to a backtrace function that's suitable for +// unwinding through a signal handler. This is important primarily for frame- +// pointer based unwinders, DWARF or other unwinders can simply provide the +// normal backtrace function as the implementation here. On POSIX, SignalContext +// should be the `ucontext_t` from the signal handler. +typedef size_t (*SegvBacktrace_t)(uintptr_t *TraceBuffer, size_t Size, + void *SignalContext); + +// Returns platform-specific provided implementations of Backtrace_t for use +// inside the GWP-ASan core allocator. +options::Backtrace_t getBacktraceFunction(); + +// Returns platform-specific provided implementations of PrintBacktrace_t and +// SegvBacktrace_t for use in the optional SEGV handler. +PrintBacktrace_t getPrintBacktraceFunction(); +SegvBacktrace_t getSegvBacktraceFunction(); +} // namespace backtrace +} // namespace gwp_asan + +#endif // GWP_ASAN_OPTIONAL_BACKTRACE_H_ diff --git a/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/backtrace_fuchsia.cpp b/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/backtrace_fuchsia.cpp new file mode 100644 index 000000000000..09b0325a6fc7 --- /dev/null +++ b/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/backtrace_fuchsia.cpp @@ -0,0 +1,27 @@ +//===-- backtrace_fuchsia.cpp -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "gwp_asan/optional/backtrace.h" + +#include <zircon/sanitizer.h> + +namespace gwp_asan { +namespace backtrace { + +// Fuchsia's C library provides safe, fast, best-effort backtraces itself. +options::Backtrace_t getBacktraceFunction() { + return __sanitizer_fast_backtrace; +} + +// These are only used in fatal signal handling, which is not used on Fuchsia. + +PrintBacktrace_t getPrintBacktraceFunction() { return nullptr; } +SegvBacktrace_t getSegvBacktraceFunction() { return nullptr; } + +} // namespace backtrace +} // namespace gwp_asan diff --git a/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/backtrace_linux_libc.cpp b/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/backtrace_linux_libc.cpp new file mode 100644 index 000000000000..ea8e72be287d --- /dev/null +++ b/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/backtrace_linux_libc.cpp @@ -0,0 +1,67 @@ +//===-- backtrace_linux_libc.cpp --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <assert.h> +#include <execinfo.h> +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include "gwp_asan/definitions.h" +#include "gwp_asan/optional/backtrace.h" +#include "gwp_asan/optional/printf.h" +#include "gwp_asan/options.h" + +namespace { +size_t Backtrace(uintptr_t *TraceBuffer, size_t Size) { + static_assert(sizeof(uintptr_t) == sizeof(void *), "uintptr_t is not void*"); + + return backtrace(reinterpret_cast<void **>(TraceBuffer), Size); +} + +// We don't need any custom handling for the Segv backtrace - the libc unwinder +// has no problems with unwinding through a signal handler. Force inlining here +// to avoid the additional frame. +GWP_ASAN_ALWAYS_INLINE size_t SegvBacktrace(uintptr_t *TraceBuffer, size_t Size, + void * /*Context*/) { + return Backtrace(TraceBuffer, Size); +} + +static void PrintBacktrace(uintptr_t *Trace, size_t TraceLength, + gwp_asan::Printf_t Printf) { + if (TraceLength == 0) { + Printf(" <not found (does your allocator support backtracing?)>\n\n"); + return; + } + + char **BacktraceSymbols = + backtrace_symbols(reinterpret_cast<void **>(Trace), TraceLength); + + for (size_t i = 0; i < TraceLength; ++i) { + if (!BacktraceSymbols) + Printf(" #%zu %p\n", i, Trace[i]); + else + Printf(" #%zu %s\n", i, BacktraceSymbols[i]); + } + + Printf("\n"); + if (BacktraceSymbols) + free(BacktraceSymbols); +} +} // anonymous namespace + +namespace gwp_asan { +namespace backtrace { + +options::Backtrace_t getBacktraceFunction() { return Backtrace; } +PrintBacktrace_t getPrintBacktraceFunction() { return PrintBacktrace; } +SegvBacktrace_t getSegvBacktraceFunction() { return SegvBacktrace; } + +} // namespace backtrace +} // namespace gwp_asan diff --git a/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/backtrace_sanitizer_common.cpp b/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/backtrace_sanitizer_common.cpp new file mode 100644 index 000000000000..f8b9cbdb7935 --- /dev/null +++ b/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/backtrace_sanitizer_common.cpp @@ -0,0 +1,101 @@ +//===-- backtrace_sanitizer_common.cpp --------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <assert.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +#include "gwp_asan/optional/backtrace.h" +#include "gwp_asan/options.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flag_parser.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_stacktrace.h" + +void __sanitizer::BufferedStackTrace::UnwindImpl(uptr pc, uptr bp, + void *context, + bool request_fast, + u32 max_depth) { + if (!StackTrace::WillUseFastUnwind(request_fast)) + return Unwind(max_depth, pc, 0, context, 0, 0, false); + + uptr top = 0; + uptr bottom = 0; + GetThreadStackTopAndBottom(/*at_initialization*/ false, &top, &bottom); + + return Unwind(max_depth, pc, bp, context, top, bottom, request_fast); +} + +namespace { +size_t BacktraceCommon(uintptr_t *TraceBuffer, size_t Size, void *Context) { + // Use the slow sanitizer unwinder in the segv handler. Fast frame pointer + // unwinders can end up dropping frames because the kernel sigreturn() frame's + // return address is the return address at time of fault. This has the result + // of never actually capturing the PC where the signal was raised. + bool UseFastUnwind = (Context == nullptr); + + __sanitizer::BufferedStackTrace Trace; + Trace.Reset(); + if (Size > __sanitizer::kStackTraceMax) + Size = __sanitizer::kStackTraceMax; + + Trace.Unwind((__sanitizer::uptr)__builtin_return_address(0), + (__sanitizer::uptr)__builtin_frame_address(0), Context, + UseFastUnwind, Size - 1); + + memcpy(TraceBuffer, Trace.trace, Trace.size * sizeof(uintptr_t)); + return Trace.size; +} + +size_t Backtrace(uintptr_t *TraceBuffer, size_t Size) { + return BacktraceCommon(TraceBuffer, Size, nullptr); +} + +size_t SegvBacktrace(uintptr_t *TraceBuffer, size_t Size, void *Context) { + return BacktraceCommon(TraceBuffer, Size, Context); +} + +static void PrintBacktrace(uintptr_t *Trace, size_t TraceLength, + gwp_asan::Printf_t Printf) { + __sanitizer::StackTrace StackTrace; + StackTrace.trace = reinterpret_cast<__sanitizer::uptr *>(Trace); + StackTrace.size = TraceLength; + + if (StackTrace.size == 0) { + Printf(" <unknown (does your allocator support backtracing?)>\n\n"); + return; + } + + __sanitizer::InternalScopedString buffer; + StackTrace.PrintTo(&buffer); + Printf("%s\n", buffer.data()); +} +} // anonymous namespace + +namespace gwp_asan { +namespace backtrace { + +// This function is thread-compatible. It must be synchronised in respect to any +// other calls to getBacktraceFunction(), calls to getPrintBacktraceFunction(), +// and calls to either of the functions that they return. Furthermore, this may +// require synchronisation with any calls to sanitizer_common that use flags. +// Generally, this function will be called during the initialisation of the +// allocator, which is done in a thread-compatible manner. +options::Backtrace_t getBacktraceFunction() { + // The unwinder requires the default flags to be set. + __sanitizer::SetCommonFlagsDefaults(); + __sanitizer::InitializeCommonFlags(); + return Backtrace; +} + +PrintBacktrace_t getPrintBacktraceFunction() { return PrintBacktrace; } +SegvBacktrace_t getSegvBacktraceFunction() { return SegvBacktrace; } + +} // namespace backtrace +} // namespace gwp_asan diff --git a/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/options_parser.cpp b/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/options_parser.cpp new file mode 100644 index 000000000000..60234124e8ed --- /dev/null +++ b/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/options_parser.cpp @@ -0,0 +1,258 @@ +//===-- options_parser.cpp --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "gwp_asan/optional/options_parser.h" +#include "gwp_asan/optional/printf.h" +#include "gwp_asan/utilities.h" + +#include <assert.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +namespace { +enum class OptionType : uint8_t { + OT_bool, + OT_int, +}; + +#define InvokeIfNonNull(Printf, ...) \ + do { \ + if (Printf) \ + Printf(__VA_ARGS__); \ + } while (0); + +class OptionParser { +public: + explicit OptionParser(gwp_asan::Printf_t PrintfForWarnings) + : Printf(PrintfForWarnings) {} + void registerOption(const char *Name, const char *Desc, OptionType Type, + void *Var); + void parseString(const char *S); + void printOptionDescriptions(); + +private: + // Calculate at compile-time how many options are available. +#define GWP_ASAN_OPTION(...) +1 + static constexpr size_t MaxOptions = 0 +#include "gwp_asan/options.inc" + ; +#undef GWP_ASAN_OPTION + + struct Option { + const char *Name; + const char *Desc; + OptionType Type; + void *Var; + } Options[MaxOptions]; + + size_t NumberOfOptions = 0; + const char *Buffer = nullptr; + uintptr_t Pos = 0; + gwp_asan::Printf_t Printf = nullptr; + + void skipWhitespace(); + void parseOptions(); + bool parseOption(); + bool setOptionToValue(const char *Name, const char *Value); +}; + +void OptionParser::printOptionDescriptions() { + InvokeIfNonNull(Printf, "GWP-ASan: Available options:\n"); + for (size_t I = 0; I < NumberOfOptions; ++I) + InvokeIfNonNull(Printf, "\t%s\n\t\t- %s\n", Options[I].Name, + Options[I].Desc); +} + +bool isSeparator(char C) { + return C == ' ' || C == ',' || C == ':' || C == '\n' || C == '\t' || + C == '\r'; +} + +bool isSeparatorOrNull(char C) { return !C || isSeparator(C); } + +void OptionParser::skipWhitespace() { + while (isSeparator(Buffer[Pos])) + ++Pos; +} + +bool OptionParser::parseOption() { + const uintptr_t NameStart = Pos; + while (Buffer[Pos] != '=' && !isSeparatorOrNull(Buffer[Pos])) + ++Pos; + + const char *Name = Buffer + NameStart; + if (Buffer[Pos] != '=') { + InvokeIfNonNull(Printf, "GWP-ASan: Expected '=' when parsing option '%s'.", + Name); + return false; + } + const uintptr_t ValueStart = ++Pos; + const char *Value; + if (Buffer[Pos] == '\'' || Buffer[Pos] == '"') { + const char Quote = Buffer[Pos++]; + while (Buffer[Pos] != 0 && Buffer[Pos] != Quote) + ++Pos; + if (Buffer[Pos] == 0) { + InvokeIfNonNull(Printf, "GWP-ASan: Unterminated string in option '%s'.", + Name); + return false; + } + Value = Buffer + ValueStart + 1; + ++Pos; // consume the closing quote + } else { + while (!isSeparatorOrNull(Buffer[Pos])) + ++Pos; + Value = Buffer + ValueStart; + } + + return setOptionToValue(Name, Value); +} + +void OptionParser::parseOptions() { + while (true) { + skipWhitespace(); + if (Buffer[Pos] == 0) + break; + if (!parseOption()) { + InvokeIfNonNull(Printf, "GWP-ASan: Options parsing failed.\n"); + return; + } + } +} + +void OptionParser::parseString(const char *S) { + if (!S) + return; + Buffer = S; + Pos = 0; + parseOptions(); +} + +bool parseBool(const char *Value, bool *b) { + if (strncmp(Value, "0", 1) == 0 || strncmp(Value, "no", 2) == 0 || + strncmp(Value, "false", 5) == 0) { + *b = false; + return true; + } + if (strncmp(Value, "1", 1) == 0 || strncmp(Value, "yes", 3) == 0 || + strncmp(Value, "true", 4) == 0) { + *b = true; + return true; + } + return false; +} + +bool OptionParser::setOptionToValue(const char *Name, const char *Value) { + for (size_t I = 0; I < NumberOfOptions; ++I) { + const uintptr_t Len = strlen(Options[I].Name); + if (strncmp(Name, Options[I].Name, Len) != 0 || Name[Len] != '=') + continue; + bool Ok = false; + switch (Options[I].Type) { + case OptionType::OT_bool: + Ok = parseBool(Value, reinterpret_cast<bool *>(Options[I].Var)); + if (!Ok) + InvokeIfNonNull( + Printf, "GWP-ASan: Invalid boolean value '%s' for option '%s'.\n", + Value, Options[I].Name); + break; + case OptionType::OT_int: + char *ValueEnd; + *reinterpret_cast<int *>(Options[I].Var) = + static_cast<int>(strtol(Value, &ValueEnd, 10)); + Ok = + *ValueEnd == '"' || *ValueEnd == '\'' || isSeparatorOrNull(*ValueEnd); + if (!Ok) + InvokeIfNonNull( + Printf, "GWP-ASan: Invalid integer value '%s' for option '%s'.\n", + Value, Options[I].Name); + break; + } + return Ok; + } + + InvokeIfNonNull(Printf, "GWP-ASan: Unknown option '%s'.", Name); + return true; +} + +void OptionParser::registerOption(const char *Name, const char *Desc, + OptionType Type, void *Var) { + assert(NumberOfOptions < MaxOptions && + "GWP-ASan Error: Ran out of space for options.\n"); + Options[NumberOfOptions].Name = Name; + Options[NumberOfOptions].Desc = Desc; + Options[NumberOfOptions].Type = Type; + Options[NumberOfOptions].Var = Var; + ++NumberOfOptions; +} + +void registerGwpAsanOptions(OptionParser *parser, + gwp_asan::options::Options *o) { +#define GWP_ASAN_OPTION(Type, Name, DefaultValue, Description) \ + parser->registerOption(#Name, Description, OptionType::OT_##Type, &o->Name); +#include "gwp_asan/options.inc" +#undef GWP_ASAN_OPTION +} + +const char *getGwpAsanDefaultOptions() { + return (__gwp_asan_default_options) ? __gwp_asan_default_options() : ""; +} + +gwp_asan::options::Options *getOptionsInternal() { + static gwp_asan::options::Options GwpAsanOptions; + return &GwpAsanOptions; +} +} // anonymous namespace + +namespace gwp_asan { +namespace options { + +void initOptions(const char *OptionsStr, Printf_t PrintfForWarnings) { + Options *o = getOptionsInternal(); + o->setDefaults(); + + OptionParser Parser(PrintfForWarnings); + registerGwpAsanOptions(&Parser, o); + + // Override from the weak function definition in this executable. + Parser.parseString(getGwpAsanDefaultOptions()); + + // Override from the provided options string. + Parser.parseString(OptionsStr); + + if (o->help) + Parser.printOptionDescriptions(); + + if (!o->Enabled) + return; + + if (o->MaxSimultaneousAllocations <= 0) { + InvokeIfNonNull( + PrintfForWarnings, + "GWP-ASan ERROR: MaxSimultaneousAllocations must be > 0 when GWP-ASan " + "is enabled.\n"); + o->Enabled = false; + } + if (o->SampleRate <= 0) { + InvokeIfNonNull( + PrintfForWarnings, + "GWP-ASan ERROR: SampleRate must be > 0 when GWP-ASan is enabled.\n"); + o->Enabled = false; + } +} + +void initOptions(Printf_t PrintfForWarnings) { + initOptions(getenv("GWP_ASAN_OPTIONS"), PrintfForWarnings); +} + +Options &getOptions() { return *getOptionsInternal(); } + +} // namespace options +} // namespace gwp_asan diff --git a/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/options_parser.h b/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/options_parser.h new file mode 100644 index 000000000000..a5a062801f8f --- /dev/null +++ b/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/options_parser.h @@ -0,0 +1,31 @@ +//===-- options_parser.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef GWP_ASAN_OPTIONAL_OPTIONS_PARSER_H_ +#define GWP_ASAN_OPTIONAL_OPTIONS_PARSER_H_ + +#include "gwp_asan/optional/printf.h" +#include "gwp_asan/options.h" + +namespace gwp_asan { +namespace options { +// Parse the options from the GWP_ASAN_OPTIONS environment variable. +void initOptions(Printf_t PrintfForWarnings = nullptr); +// Parse the options from the provided string. +void initOptions(const char *OptionsStr, Printf_t PrintfForWarnings = nullptr); +// Returns the initialised options. Call initOptions() prior to calling this +// function. +Options &getOptions(); +} // namespace options +} // namespace gwp_asan + +extern "C" { +__attribute__((weak)) const char *__gwp_asan_default_options(); +} + +#endif // GWP_ASAN_OPTIONAL_OPTIONS_PARSER_H_ diff --git a/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/printf.h b/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/printf.h new file mode 100644 index 000000000000..1004a2c24989 --- /dev/null +++ b/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/printf.h @@ -0,0 +1,33 @@ +//===-- printf.h ------------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef GWP_ASAN_OPTIONAL_PRINTF_H_ +#define GWP_ASAN_OPTIONAL_PRINTF_H_ + +namespace gwp_asan { + +// ================================ Requirements =============================== +// This function is required to be provided by the supporting allocator iff the +// allocator wants to use any of the optional components. +// ================================ Description ================================ +// This function shall produce output according to a strict subset of the C +// standard library's printf() family. This function must support printing the +// following formats: +// 1. integers: "%([0-9]*)?(z|ll)?{d,u,x,X}" +// 2. pointers: "%p" +// 3. strings: "%[-]([0-9]*)?(\\.\\*)?s" +// 4. chars: "%c" +// This function must be implemented in a signal-safe manner, and thus must not +// malloc(). +// =================================== Notes =================================== +// This function has a slightly different signature than the C standard +// library's printf(). Notably, it returns 'void' rather than 'int'. +typedef void (*Printf_t)(const char *Format, ...); + +} // namespace gwp_asan +#endif // GWP_ASAN_OPTIONAL_PRINTF_H_ diff --git a/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/segv_handler.h b/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/segv_handler.h new file mode 100644 index 000000000000..72105ded7d55 --- /dev/null +++ b/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/segv_handler.h @@ -0,0 +1,34 @@ +//===-- segv_handler.h ------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef GWP_ASAN_OPTIONAL_SEGV_HANDLER_H_ +#define GWP_ASAN_OPTIONAL_SEGV_HANDLER_H_ + +#include "gwp_asan/guarded_pool_allocator.h" +#include "gwp_asan/optional/backtrace.h" +#include "gwp_asan/optional/printf.h" + +namespace gwp_asan { +namespace segv_handler { +// Install the SIGSEGV crash handler for printing use-after-free and heap- +// buffer-{under|over}flow exceptions if the user asked for it. This is platform +// specific as even though POSIX and Windows both support registering handlers +// through signal(), we have to use platform-specific signal handlers to obtain +// the address that caused the SIGSEGV exception. GPA->init() must be called +// before this function. +void installSignalHandlers(gwp_asan::GuardedPoolAllocator *GPA, Printf_t Printf, + gwp_asan::backtrace::PrintBacktrace_t PrintBacktrace, + gwp_asan::backtrace::SegvBacktrace_t SegvBacktrace, + bool Recoverable = false); + +// Uninistall the signal handlers, test-only. +void uninstallSignalHandlers(); +} // namespace segv_handler +} // namespace gwp_asan + +#endif // GWP_ASAN_OPTIONAL_SEGV_HANDLER_H_ diff --git a/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/segv_handler_fuchsia.cpp b/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/segv_handler_fuchsia.cpp new file mode 100644 index 000000000000..f5ff35e27ac2 --- /dev/null +++ b/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/segv_handler_fuchsia.cpp @@ -0,0 +1,23 @@ +//===-- segv_handler_fuchsia.cpp --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "gwp_asan/optional/segv_handler.h" + +// GWP-ASan on Fuchsia doesn't currently support signal handlers. + +namespace gwp_asan { +namespace segv_handler { +void installSignalHandlers(gwp_asan::GuardedPoolAllocator * /* GPA */, + Printf_t /* Printf */, + backtrace::PrintBacktrace_t /* PrintBacktrace */, + backtrace::SegvBacktrace_t /* SegvBacktrace */, + bool /* Recoverable */) {} + +void uninstallSignalHandlers() {} +} // namespace segv_handler +} // namespace gwp_asan diff --git a/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/segv_handler_posix.cpp b/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/segv_handler_posix.cpp new file mode 100644 index 000000000000..ae4493723a47 --- /dev/null +++ b/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/segv_handler_posix.cpp @@ -0,0 +1,270 @@ +//===-- segv_handler_posix.cpp ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "gwp_asan/common.h" +#include "gwp_asan/crash_handler.h" +#include "gwp_asan/guarded_pool_allocator.h" +#include "gwp_asan/optional/segv_handler.h" +#include "gwp_asan/options.h" + +// RHEL creates the PRIu64 format macro (for printing uint64_t's) only when this +// macro is defined before including <inttypes.h>. +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS 1 +#endif + +#include <assert.h> +#include <inttypes.h> +#include <signal.h> +#include <stdio.h> + +using gwp_asan::AllocationMetadata; +using gwp_asan::Error; +using gwp_asan::GuardedPoolAllocator; +using gwp_asan::Printf_t; +using gwp_asan::backtrace::PrintBacktrace_t; +using gwp_asan::backtrace::SegvBacktrace_t; + +namespace { + +struct ScopedEndOfReportDecorator { + ScopedEndOfReportDecorator(gwp_asan::Printf_t Printf) : Printf(Printf) {} + ~ScopedEndOfReportDecorator() { Printf("*** End GWP-ASan report ***\n"); } + gwp_asan::Printf_t Printf; +}; + +// Prints the provided error and metadata information. +void printHeader(Error E, uintptr_t AccessPtr, + const gwp_asan::AllocationMetadata *Metadata, + Printf_t Printf) { + // Print using intermediate strings. Platforms like Android don't like when + // you print multiple times to the same line, as there may be a newline + // appended to a log file automatically per Printf() call. + constexpr size_t kDescriptionBufferLen = 128; + char DescriptionBuffer[kDescriptionBufferLen] = ""; + + bool AccessWasInBounds = false; + if (E != Error::UNKNOWN && Metadata != nullptr) { + uintptr_t Address = __gwp_asan_get_allocation_address(Metadata); + size_t Size = __gwp_asan_get_allocation_size(Metadata); + if (AccessPtr < Address) { + snprintf(DescriptionBuffer, kDescriptionBufferLen, + "(%zu byte%s to the left of a %zu-byte allocation at 0x%zx) ", + Address - AccessPtr, (Address - AccessPtr == 1) ? "" : "s", Size, + Address); + } else if (AccessPtr > Address) { + snprintf(DescriptionBuffer, kDescriptionBufferLen, + "(%zu byte%s to the right of a %zu-byte allocation at 0x%zx) ", + AccessPtr - Address, (AccessPtr - Address == 1) ? "" : "s", Size, + Address); + } else if (E == Error::DOUBLE_FREE) { + snprintf(DescriptionBuffer, kDescriptionBufferLen, + "(a %zu-byte allocation) ", Size); + } else { + AccessWasInBounds = true; + snprintf(DescriptionBuffer, kDescriptionBufferLen, + "(%zu byte%s into a %zu-byte allocation at 0x%zx) ", + AccessPtr - Address, (AccessPtr - Address == 1) ? "" : "s", Size, + Address); + } + } + + // Possible number of digits of a 64-bit number: ceil(log10(2^64)) == 20. Add + // a null terminator, and round to the nearest 8-byte boundary. + uint64_t ThreadID = gwp_asan::getThreadID(); + constexpr size_t kThreadBufferLen = 24; + char ThreadBuffer[kThreadBufferLen]; + if (ThreadID == gwp_asan::kInvalidThreadID) + snprintf(ThreadBuffer, kThreadBufferLen, "<unknown>"); + else + snprintf(ThreadBuffer, kThreadBufferLen, "%" PRIu64, ThreadID); + + const char *OutOfBoundsAndUseAfterFreeWarning = ""; + if (E == Error::USE_AFTER_FREE && !AccessWasInBounds) { + OutOfBoundsAndUseAfterFreeWarning = + " (warning: buffer overflow/underflow detected on a free()'d " + "allocation. This either means you have a buffer-overflow and a " + "use-after-free at the same time, or you have a long-lived " + "use-after-free bug where the allocation/deallocation metadata below " + "has already been overwritten and is likely bogus)"; + } + + Printf("%s%s at 0x%zx %sby thread %s here:\n", gwp_asan::ErrorToString(E), + OutOfBoundsAndUseAfterFreeWarning, AccessPtr, DescriptionBuffer, + ThreadBuffer); +} + +static bool HasReportedBadPoolAccess = false; +static const char *kUnknownCrashText = + "GWP-ASan cannot provide any more information about this error. This may " + "occur due to a wild memory access into the GWP-ASan pool, or an " + "overflow/underflow that is > 512B in length.\n"; + +void dumpReport(uintptr_t ErrorPtr, const gwp_asan::AllocatorState *State, + const gwp_asan::AllocationMetadata *Metadata, + SegvBacktrace_t SegvBacktrace, Printf_t Printf, + PrintBacktrace_t PrintBacktrace, void *Context) { + assert(State && "dumpReport missing Allocator State."); + assert(Metadata && "dumpReport missing Metadata."); + assert(Printf && "dumpReport missing Printf."); + assert(__gwp_asan_error_is_mine(State, ErrorPtr) && + "dumpReport() called on a non-GWP-ASan error."); + + uintptr_t InternalErrorPtr = + __gwp_asan_get_internal_crash_address(State, ErrorPtr); + if (InternalErrorPtr) + ErrorPtr = InternalErrorPtr; + + const gwp_asan::AllocationMetadata *AllocMeta = + __gwp_asan_get_metadata(State, Metadata, ErrorPtr); + + if (AllocMeta == nullptr) { + if (HasReportedBadPoolAccess) return; + HasReportedBadPoolAccess = true; + Printf("*** GWP-ASan detected a memory error ***\n"); + ScopedEndOfReportDecorator Decorator(Printf); + Printf(kUnknownCrashText); + return; + } + + // It's unusual for a signal handler to be invoked multiple times for the same + // allocation, but it's possible in various scenarios, like: + // 1. A double-free or invalid-free was invoked in one thread at the same + // time as a buffer-overflow or use-after-free in another thread, or + // 2. Two threads do a use-after-free or buffer-overflow at the same time. + // In these instances, we've already dumped a report for this allocation, so + // skip dumping this issue as well. + if (AllocMeta->HasCrashed) + return; + + Printf("*** GWP-ASan detected a memory error ***\n"); + ScopedEndOfReportDecorator Decorator(Printf); + + Error E = __gwp_asan_diagnose_error(State, Metadata, ErrorPtr); + if (E == Error::UNKNOWN) { + Printf(kUnknownCrashText); + return; + } + + // Print the error header. + printHeader(E, ErrorPtr, AllocMeta, Printf); + + // Print the fault backtrace. + static constexpr unsigned kMaximumStackFramesForCrashTrace = 512; + uintptr_t Trace[kMaximumStackFramesForCrashTrace]; + size_t TraceLength = + SegvBacktrace(Trace, kMaximumStackFramesForCrashTrace, Context); + + PrintBacktrace(Trace, TraceLength, Printf); + + // Maybe print the deallocation trace. + if (__gwp_asan_is_deallocated(AllocMeta)) { + uint64_t ThreadID = __gwp_asan_get_deallocation_thread_id(AllocMeta); + if (ThreadID == gwp_asan::kInvalidThreadID) + Printf("0x%zx was deallocated by thread <unknown> here:\n", ErrorPtr); + else + Printf("0x%zx was deallocated by thread %zu here:\n", ErrorPtr, ThreadID); + TraceLength = __gwp_asan_get_deallocation_trace( + AllocMeta, Trace, kMaximumStackFramesForCrashTrace); + PrintBacktrace(Trace, TraceLength, Printf); + } + + // Print the allocation trace. + uint64_t ThreadID = __gwp_asan_get_allocation_thread_id(AllocMeta); + if (ThreadID == gwp_asan::kInvalidThreadID) + Printf("0x%zx was allocated by thread <unknown> here:\n", ErrorPtr); + else + Printf("0x%zx was allocated by thread %zu here:\n", ErrorPtr, ThreadID); + TraceLength = __gwp_asan_get_allocation_trace( + AllocMeta, Trace, kMaximumStackFramesForCrashTrace); + PrintBacktrace(Trace, TraceLength, Printf); +} + +struct sigaction PreviousHandler; +bool SignalHandlerInstalled; +bool RecoverableSignal; +gwp_asan::GuardedPoolAllocator *GPAForSignalHandler; +Printf_t PrintfForSignalHandler; +PrintBacktrace_t PrintBacktraceForSignalHandler; +SegvBacktrace_t BacktraceForSignalHandler; + +static void sigSegvHandler(int sig, siginfo_t *info, void *ucontext) { + const gwp_asan::AllocatorState *State = + GPAForSignalHandler->getAllocatorState(); + void *FaultAddr = info->si_addr; + uintptr_t FaultAddrUPtr = reinterpret_cast<uintptr_t>(FaultAddr); + + if (__gwp_asan_error_is_mine(State, FaultAddrUPtr)) { + GPAForSignalHandler->preCrashReport(FaultAddr); + + dumpReport(FaultAddrUPtr, State, GPAForSignalHandler->getMetadataRegion(), + BacktraceForSignalHandler, PrintfForSignalHandler, + PrintBacktraceForSignalHandler, ucontext); + + if (RecoverableSignal) { + GPAForSignalHandler->postCrashReportRecoverableOnly(FaultAddr); + return; + } + } + + // Process any previous handlers as long as the crash wasn't a GWP-ASan crash + // in recoverable mode. + if (PreviousHandler.sa_flags & SA_SIGINFO) { + PreviousHandler.sa_sigaction(sig, info, ucontext); + } else if (PreviousHandler.sa_handler == SIG_DFL) { + // If the previous handler was the default handler, cause a core dump. + signal(SIGSEGV, SIG_DFL); + raise(SIGSEGV); + } else if (PreviousHandler.sa_handler == SIG_IGN) { + // If the previous segv handler was SIGIGN, crash iff we were responsible + // for the crash. + if (__gwp_asan_error_is_mine(GPAForSignalHandler->getAllocatorState(), + reinterpret_cast<uintptr_t>(info->si_addr))) { + signal(SIGSEGV, SIG_DFL); + raise(SIGSEGV); + } + } else { + PreviousHandler.sa_handler(sig); + } +} +} // anonymous namespace + +namespace gwp_asan { +namespace segv_handler { + +void installSignalHandlers(gwp_asan::GuardedPoolAllocator *GPA, Printf_t Printf, + PrintBacktrace_t PrintBacktrace, + SegvBacktrace_t SegvBacktrace, bool Recoverable) { + assert(GPA && "GPA wasn't provided to installSignalHandlers."); + assert(Printf && "Printf wasn't provided to installSignalHandlers."); + assert(PrintBacktrace && + "PrintBacktrace wasn't provided to installSignalHandlers."); + assert(SegvBacktrace && + "SegvBacktrace wasn't provided to installSignalHandlers."); + GPAForSignalHandler = GPA; + PrintfForSignalHandler = Printf; + PrintBacktraceForSignalHandler = PrintBacktrace; + BacktraceForSignalHandler = SegvBacktrace; + RecoverableSignal = Recoverable; + + struct sigaction Action = {}; + Action.sa_sigaction = sigSegvHandler; + Action.sa_flags = SA_SIGINFO; + sigaction(SIGSEGV, &Action, &PreviousHandler); + SignalHandlerInstalled = true; + HasReportedBadPoolAccess = false; +} + +void uninstallSignalHandlers() { + if (SignalHandlerInstalled) { + sigaction(SIGSEGV, &PreviousHandler, nullptr); + SignalHandlerInstalled = false; + } +} +} // namespace segv_handler +} // namespace gwp_asan |