diff --git a/compiler-rt/lib/gwp_asan/optional/backtrace.h b/compiler-rt/lib/gwp_asan/optional/backtrace.h --- a/compiler-rt/lib/gwp_asan/optional/backtrace.h +++ b/compiler-rt/lib/gwp_asan/optional/backtrace.h @@ -9,11 +9,41 @@ #ifndef GWP_ASAN_OPTIONAL_BACKTRACE_H_ #define GWP_ASAN_OPTIONAL_BACKTRACE_H_ -#include "gwp_asan/optional/segv_handler.h" +#include "gwp_asan/optional/printf.h" #include "gwp_asan/options.h" +// Implementations of PrintBacktrace and SegvBacktrace are only required if the +// optional SEGV handler is provided. We provide a few default implementations +// of these functions, as well as a default implementation of the Backtrace_t +// function that's required for GWP-ASan to record backtraces. + namespace gwp_asan { -namespace options { +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); + // Functions to get the platform-specific and implementation-specific backtrace // and backtrace printing functions when RTGwpAsanBacktraceLibc or // RTGwpAsanBacktraceSanitizerCommon are linked. Use these functions to get the @@ -21,9 +51,10 @@ // Options::PrintBacktrace when initialising the GuardedPoolAllocator. Please // note any thread-safety descriptions for the implementation of these functions // that you use. -Backtrace_t getBacktraceFunction(); -crash_handler::PrintBacktrace_t getPrintBacktraceFunction(); -} // namespace options +options::Backtrace_t getBacktraceFunction(); +PrintBacktrace_t getPrintBacktraceFunction(); +SegvBacktrace_t getSegvBacktraceFunction(); +} // namespace backtrace } // namespace gwp_asan #endif // GWP_ASAN_OPTIONAL_BACKTRACE_H_ diff --git a/compiler-rt/lib/gwp_asan/optional/backtrace_fuchsia.cpp b/compiler-rt/lib/gwp_asan/optional/backtrace_fuchsia.cpp --- a/compiler-rt/lib/gwp_asan/optional/backtrace_fuchsia.cpp +++ b/compiler-rt/lib/gwp_asan/optional/backtrace_fuchsia.cpp @@ -11,12 +11,11 @@ // GWP-ASan on Fuchsia doesn't currently support backtraces. namespace gwp_asan { -namespace options { -Backtrace_t getBacktraceFunction() { return nullptr; } -crash_handler::PrintBacktrace_t getPrintBacktraceFunction() { return nullptr; } -} // namespace options +namespace backtrace { -namespace crash_handler { +options::Backtrace_t getBacktraceFunction() { return nullptr; } +crash_handler::PrintBacktrace_t getPrintBacktraceFunction() { return nullptr; } SegvBacktrace_t getSegvBacktraceFunction() { return nullptr; } -} // namespace crash_handler + +} // namespace backtrace } // namespace gwp_asan diff --git a/compiler-rt/lib/gwp_asan/optional/backtrace_linux_libc.cpp b/compiler-rt/lib/gwp_asan/optional/backtrace_linux_libc.cpp --- a/compiler-rt/lib/gwp_asan/optional/backtrace_linux_libc.cpp +++ b/compiler-rt/lib/gwp_asan/optional/backtrace_linux_libc.cpp @@ -14,6 +14,7 @@ #include #include "gwp_asan/optional/backtrace.h" +#include "gwp_asan/optional/printf.h" #include "gwp_asan/options.h" namespace { @@ -32,7 +33,7 @@ } static void PrintBacktrace(uintptr_t *Trace, size_t TraceLength, - gwp_asan::crash_handler::Printf_t Printf) { + gwp_asan::Printf_t Printf) { if (TraceLength == 0) { Printf(" \n\n"); return; @@ -55,14 +56,11 @@ } // anonymous namespace namespace gwp_asan { -namespace options { -Backtrace_t getBacktraceFunction() { return Backtrace; } -crash_handler::PrintBacktrace_t getPrintBacktraceFunction() { - return PrintBacktrace; -} -} // namespace options +namespace backtrace { -namespace crash_handler { +options::Backtrace_t getBacktraceFunction() { return Backtrace; } +PrintBacktrace_t getPrintBacktraceFunction() { return PrintBacktrace; } SegvBacktrace_t getSegvBacktraceFunction() { return SegvBacktrace; } -} // namespace crash_handler + +} // namespace backtrace } // namespace gwp_asan diff --git a/compiler-rt/lib/gwp_asan/optional/backtrace_sanitizer_common.cpp b/compiler-rt/lib/gwp_asan/optional/backtrace_sanitizer_common.cpp --- a/compiler-rt/lib/gwp_asan/optional/backtrace_sanitizer_common.cpp +++ b/compiler-rt/lib/gwp_asan/optional/backtrace_sanitizer_common.cpp @@ -62,7 +62,7 @@ } static void PrintBacktrace(uintptr_t *Trace, size_t TraceLength, - gwp_asan::crash_handler::Printf_t Printf) { + gwp_asan::Printf_t Printf) { __sanitizer::StackTrace StackTrace; StackTrace.trace = reinterpret_cast<__sanitizer::uptr *>(Trace); StackTrace.size = TraceLength; @@ -77,25 +77,24 @@ } // anonymous namespace namespace gwp_asan { -namespace options { +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. -Backtrace_t getBacktraceFunction() { +options::Backtrace_t getBacktraceFunction() { // The unwinder requires the default flags to be set. __sanitizer::SetCommonFlagsDefaults(); __sanitizer::InitializeCommonFlags(); return Backtrace; } -crash_handler::PrintBacktrace_t getPrintBacktraceFunction() { - return PrintBacktrace; -} -} // namespace options -namespace crash_handler { +PrintBacktrace_t getPrintBacktraceFunction() { return PrintBacktrace; } + SegvBacktrace_t getSegvBacktraceFunction() { return SegvBacktrace; } -} // namespace crash_handler + +} // namespace backtrace } // namespace gwp_asan diff --git a/compiler-rt/lib/gwp_asan/optional/options_parser.h b/compiler-rt/lib/gwp_asan/optional/options_parser.h --- a/compiler-rt/lib/gwp_asan/optional/options_parser.h +++ b/compiler-rt/lib/gwp_asan/optional/options_parser.h @@ -9,14 +9,15 @@ #ifndef GWP_ASAN_OPTIONAL_OPTIONS_PARSER_H_ #define GWP_ASAN_OPTIONAL_OPTIONS_PARSER_H_ -#include "gwp_asan/optional/backtrace.h" +#include "gwp_asan/optional/printf.h" #include "gwp_asan/options.h" -#include "sanitizer_common/sanitizer_common.h" namespace gwp_asan { namespace options { // Parse the options from the GWP_ASAN_FLAGS environment variable. -void initOptions(); +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(); @@ -24,8 +25,7 @@ } // namespace gwp_asan extern "C" { -SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE const char * -__gwp_asan_default_options(); +__attribute__((weak)) const char *__gwp_asan_default_options(); } #endif // GWP_ASAN_OPTIONAL_OPTIONS_PARSER_H_ diff --git a/compiler-rt/lib/gwp_asan/optional/options_parser.cpp b/compiler-rt/lib/gwp_asan/optional/options_parser.cpp --- a/compiler-rt/lib/gwp_asan/optional/options_parser.cpp +++ b/compiler-rt/lib/gwp_asan/optional/options_parser.cpp @@ -7,84 +7,287 @@ //===----------------------------------------------------------------------===// #include "gwp_asan/optional/options_parser.h" +#include "gwp_asan/optional/printf.h" +#include "gwp_asan/utilities.h" #include #include #include #include -#include "gwp_asan/options.h" -#include "sanitizer_common/sanitizer_common.h" -#include "sanitizer_common/sanitizer_flag_parser.h" -#include "sanitizer_common/sanitizer_flags.h" - -namespace gwp_asan { -namespace options { namespace { -void registerGwpAsanFlags(__sanitizer::FlagParser *parser, Options *o) { -#define GWP_ASAN_OPTION(Type, Name, DefaultValue, Description) \ - RegisterFlag(parser, #Name, Description, &o->Name); +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); +}; + +class UnknownOptionsRegistry { + static const size_t MaxUnknownOptions = 16; + const char *UnknownOptionsNames[MaxUnknownOptions]; + size_t NumberOfUnknownOptions; + +public: + void add(const char *Name, gwp_asan::Printf_t Printf) { + if (NumberOfUnknownOptions >= MaxUnknownOptions) { + InvokeIfNonNull(Printf, "GWP-ASan: Too many unknown Options.\n"); + return; + } + UnknownOptionsNames[NumberOfUnknownOptions++] = Name; + } + + void report(gwp_asan::Printf_t Printf) { + if (!NumberOfUnknownOptions) + return; + InvokeIfNonNull(Printf, "GWP-ASan: Found %d unrecognized Option(s):\n", + NumberOfUnknownOptions); + for (size_t I = 0; I < NumberOfUnknownOptions; ++I) + InvokeIfNonNull(Printf, " %s\n", UnknownOptionsNames[I]); + NumberOfUnknownOptions = 0; + } +}; +UnknownOptionsRegistry UnknownOptions; + +void reportUnrecognizedOptions(gwp_asan::Printf_t Printf) { + UnknownOptions.report(Printf); +} + +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; } -const char *getCompileDefinitionGwpAsanDefaultOptions() { -#ifdef GWP_ASAN_DEFAULT_OPTIONS - return SANITIZER_STRINGIFY(GWP_ASAN_DEFAULT_OPTIONS); -#else - return ""; -#endif +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; + // Backup current parser state to allow nested parseString() calls. + const char *OldBuffer = Buffer; + const uintptr_t OldPos = Pos; + Buffer = S; + Pos = 0; + + parseOptions(); + + Buffer = OldBuffer; + Pos = OldPos; +} + +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(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(Options[I].Var) = + static_cast(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; + } + // Unrecognized Option. This is not a fatal error, we may print a warning + // later. + UnknownOptions.add(Name, Printf); + return true; +} + +void OptionParser::registerOption(const char *Name, const char *Desc, + OptionType Type, void *Var) { + 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() : ""; } -Options *getOptionsInternal() { - static Options GwpAsanFlags; - return &GwpAsanFlags; +gwp_asan::options::Options *getOptionsInternal() { + static gwp_asan::options::Options GwpAsanOptions; + return &GwpAsanOptions; } } // anonymous namespace -void initOptions() { - __sanitizer::SetCommonFlagsDefaults(); +namespace gwp_asan { +namespace options { +void initOptions(const char *OptionsStr, Printf_t PrintfForWarnings) { Options *o = getOptionsInternal(); o->setDefaults(); - __sanitizer::FlagParser Parser; - registerGwpAsanFlags(&Parser, o); - - // Override from compile definition. - Parser.ParseString(getCompileDefinitionGwpAsanDefaultOptions()); + OptionParser Parser(PrintfForWarnings); + registerGwpAsanOptions(&Parser, o); - // Override from user-specified string. - Parser.ParseString(getGwpAsanDefaultOptions()); + // Override from the weak function definition in this executable. + Parser.parseString(getGwpAsanDefaultOptions()); - // Override from environment. - Parser.ParseString(__sanitizer::GetEnv("GWP_ASAN_OPTIONS")); + // Override from the provided options string. + Parser.parseString(OptionsStr); - __sanitizer::InitializeCommonFlags(); - if (__sanitizer::Verbosity()) - __sanitizer::ReportUnrecognizedFlags(); + reportUnrecognizedOptions(PrintfForWarnings); + if (o->help) + Parser.printOptionDescriptions(); if (!o->Enabled) return; - // Sanity checks for the parameters. if (o->MaxSimultaneousAllocations <= 0) { - __sanitizer::Printf("GWP-ASan ERROR: MaxSimultaneousAllocations must be > " - "0 when GWP-ASan is enabled.\n"); - exit(EXIT_FAILURE); + InvokeIfNonNull( + PrintfForWarnings, + "GWP-ASan ERROR: MaxSimultaneousAllocations must be > 0 when GWP-ASan " + "is enabled.\n"); + o->Enabled = false; } - - if (o->SampleRate < 1) { - __sanitizer::Printf( + if (o->SampleRate <= 0) { + InvokeIfNonNull( + PrintfForWarnings, "GWP-ASan ERROR: SampleRate must be > 0 when GWP-ASan is enabled.\n"); - exit(EXIT_FAILURE); + o->Enabled = false; } } +void initOptions(Printf_t PrintfForWarnings) { + initOptions(getenv("GWP_ASAN_OPTIONS"), PrintfForWarnings); +} + Options &getOptions() { return *getOptionsInternal(); } } // namespace options diff --git a/compiler-rt/lib/gwp_asan/optional/printf.h b/compiler-rt/lib/gwp_asan/optional/printf.h new file mode 100644 --- /dev/null +++ b/compiler-rt/lib/gwp_asan/optional/printf.h @@ -0,0 +1,35 @@ +//===-- 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, ...); + +Printf_t getPrintfFunction(); + +} // namespace gwp_asan +#endif // GWP_ASAN_OPTIONAL_PRINTF_H_ diff --git a/compiler-rt/lib/gwp_asan/tests/optional/printf_sanitizer_common.cpp b/compiler-rt/lib/gwp_asan/optional/printf_sanitizer_common.cpp rename from compiler-rt/lib/gwp_asan/tests/optional/printf_sanitizer_common.cpp rename to compiler-rt/lib/gwp_asan/optional/printf_sanitizer_common.cpp --- a/compiler-rt/lib/gwp_asan/tests/optional/printf_sanitizer_common.cpp +++ b/compiler-rt/lib/gwp_asan/optional/printf_sanitizer_common.cpp @@ -6,15 +6,12 @@ // //===----------------------------------------------------------------------===// -#include "gwp_asan/optional/segv_handler.h" +#include "gwp_asan/printf.h" + #include "sanitizer_common/sanitizer_common.h" namespace gwp_asan { -namespace test { -// This printf-function getter allows other platforms (e.g. Android) to define -// their own signal-safe Printf function. In LLVM, we use -// `optional/printf_sanitizer_common.cpp` which supplies the __sanitizer::Printf -// for this purpose. -crash_handler::Printf_t getPrintfFunction() { return __sanitizer::Printf; } -}; // namespace test -}; // namespace gwp_asan + +Printf_t getPrintfFunction() { return __sanitizer::Printf; } + +} // namespace gwp_asan diff --git a/compiler-rt/lib/gwp_asan/optional/segv_handler.h b/compiler-rt/lib/gwp_asan/optional/segv_handler.h --- a/compiler-rt/lib/gwp_asan/optional/segv_handler.h +++ b/compiler-rt/lib/gwp_asan/optional/segv_handler.h @@ -10,64 +10,11 @@ #define GWP_ASAN_OPTIONAL_SEGV_HANDLER_H_ #include "gwp_asan/guarded_pool_allocator.h" -#include "gwp_asan/options.h" +#include "gwp_asan/optional/backtrace.h" +#include "gwp_asan/optional/printf.h" namespace gwp_asan { -namespace crash_handler { -// ================================ Requirements =============================== -// This function must be provided by the supporting allocator only when this -// provided crash handler is used to dump the generic report. -// sanitizer::Printf() function can be simply used here. -// ================================ 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, ...); - -// ================================ Requirements =============================== -// This function is required for the supporting allocator, but one of the three -// provided implementations may be used (RTGwpAsanBacktraceLibc, -// RTGwpAsanBacktraceSanitizerCommon, or BasicPrintBacktraceFunction). -// ================================ 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 basic PrintBacktrace implementation. This -// implementation simply prints the stack trace in a human readable fashion -// without any symbolization. -PrintBacktrace_t getBasicPrintBacktraceFunction(); - -// 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); -SegvBacktrace_t getSegvBacktraceFunction(); - +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 @@ -75,16 +22,12 @@ // the address that caused the SIGSEGV exception. GPA->init() must be called // before this function. void installSignalHandlers(gwp_asan::GuardedPoolAllocator *GPA, Printf_t Printf, - PrintBacktrace_t PrintBacktrace, - SegvBacktrace_t SegvBacktrace); + gwp_asan::backtrace::PrintBacktrace_t PrintBacktrace, + gwp_asan::backtrace::SegvBacktrace_t SegvBacktrace); +// Uninistall the signal handlers, test-only. void uninstallSignalHandlers(); - -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); -} // namespace crash_handler +} // namespace segv_handler } // namespace gwp_asan #endif // GWP_ASAN_OPTIONAL_SEGV_HANDLER_H_ diff --git a/compiler-rt/lib/gwp_asan/optional/segv_handler_fuchsia.cpp b/compiler-rt/lib/gwp_asan/optional/segv_handler_fuchsia.cpp --- a/compiler-rt/lib/gwp_asan/optional/segv_handler_fuchsia.cpp +++ b/compiler-rt/lib/gwp_asan/optional/segv_handler_fuchsia.cpp @@ -14,8 +14,8 @@ namespace crash_handler { void installSignalHandlers(gwp_asan::GuardedPoolAllocator * /* GPA */, Printf_t /* Printf */, - PrintBacktrace_t /* PrintBacktrace */, - SegvBacktrace_t /* SegvBacktrace */) {} + backtrace::PrintBacktrace_t /* PrintBacktrace */, + backtrace::SegvBacktrace_t /* SegvBacktrace */) {} void uninstallSignalHandlers() {} } // namespace crash_handler diff --git a/compiler-rt/lib/gwp_asan/optional/segv_handler_posix.cpp b/compiler-rt/lib/gwp_asan/optional/segv_handler_posix.cpp --- a/compiler-rt/lib/gwp_asan/optional/segv_handler_posix.cpp +++ b/compiler-rt/lib/gwp_asan/optional/segv_handler_posix.cpp @@ -12,62 +12,30 @@ #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 . +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS 1 +#endif + #include #include #include #include -namespace { using gwp_asan::AllocationMetadata; using gwp_asan::Error; using gwp_asan::GuardedPoolAllocator; -using gwp_asan::crash_handler::PrintBacktrace_t; -using gwp_asan::crash_handler::Printf_t; -using gwp_asan::crash_handler::SegvBacktrace_t; - -struct sigaction PreviousHandler; -bool SignalHandlerInstalled; -gwp_asan::GuardedPoolAllocator *GPAForSignalHandler; -Printf_t PrintfForSignalHandler; -PrintBacktrace_t PrintBacktraceForSignalHandler; -SegvBacktrace_t BacktraceForSignalHandler; - -static void sigSegvHandler(int sig, siginfo_t *info, void *ucontext) { - if (GPAForSignalHandler) { - GPAForSignalHandler->stop(); - - gwp_asan::crash_handler::dumpReport( - reinterpret_cast(info->si_addr), - GPAForSignalHandler->getAllocatorState(), - GPAForSignalHandler->getMetadataRegion(), BacktraceForSignalHandler, - PrintfForSignalHandler, PrintBacktraceForSignalHandler, ucontext); - } +using gwp_asan::Printf_t; +using gwp_asan::backtrace::PrintBacktrace_t; +using gwp_asan::backtrace::SegvBacktrace_t; - // Process any previous handlers. - 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(info->si_addr))) { - signal(SIGSEGV, SIG_DFL); - raise(SIGSEGV); - } - } else { - PreviousHandler.sa_handler(sig); - } -} +namespace { struct ScopedEndOfReportDecorator { - ScopedEndOfReportDecorator(gwp_asan::crash_handler::Printf_t Printf) - : Printf(Printf) {} + ScopedEndOfReportDecorator(gwp_asan::Printf_t Printf) : Printf(Printf) {} ~ScopedEndOfReportDecorator() { Printf("*** End GWP-ASan report ***\n"); } - gwp_asan::crash_handler::Printf_t Printf; + gwp_asan::Printf_t Printf; }; // Prints the provided error and metadata information. @@ -117,47 +85,6 @@ AccessPtr, DescriptionBuffer, ThreadBuffer); } -void defaultPrintStackTrace(uintptr_t *Trace, size_t TraceLength, - gwp_asan::crash_handler::Printf_t Printf) { - if (TraceLength == 0) - Printf(" \n"); - - for (size_t i = 0; i < TraceLength; ++i) { - Printf(" #%zu 0x%zx in \n", i, Trace[i]); - } - Printf("\n"); -} - -} // anonymous namespace - -namespace gwp_asan { -namespace crash_handler { -PrintBacktrace_t getBasicPrintBacktraceFunction() { - return defaultPrintStackTrace; -} - -void installSignalHandlers(gwp_asan::GuardedPoolAllocator *GPA, Printf_t Printf, - PrintBacktrace_t PrintBacktrace, - SegvBacktrace_t SegvBacktrace) { - GPAForSignalHandler = GPA; - PrintfForSignalHandler = Printf; - PrintBacktraceForSignalHandler = PrintBacktrace; - BacktraceForSignalHandler = SegvBacktrace; - - struct sigaction Action = {}; - Action.sa_sigaction = sigSegvHandler; - Action.sa_flags = SA_SIGINFO; - sigaction(SIGSEGV, &Action, &PreviousHandler); - SignalHandlerInstalled = true; -} - -void uninstallSignalHandlers() { - if (SignalHandlerInstalled) { - sigaction(SIGSEGV, &PreviousHandler, nullptr); - SignalHandlerInstalled = false; - } -} - void dumpReport(uintptr_t ErrorPtr, const gwp_asan::AllocatorState *State, const gwp_asan::AllocationMetadata *Metadata, SegvBacktrace_t SegvBacktrace, Printf_t Printf, @@ -205,7 +132,7 @@ // Maybe print the deallocation trace. if (__gwp_asan_is_deallocated(AllocMeta)) { uint64_t ThreadID = __gwp_asan_get_deallocation_thread_id(AllocMeta); - if (ThreadID == kInvalidThreadID) + if (ThreadID == gwp_asan::kInvalidThreadID) Printf("0x%zx was deallocated by thread here:\n", ErrorPtr); else Printf("0x%zx was deallocated by thread %zu here:\n", ErrorPtr, ThreadID); @@ -216,7 +143,7 @@ // Print the allocation trace. uint64_t ThreadID = __gwp_asan_get_allocation_thread_id(AllocMeta); - if (ThreadID == kInvalidThreadID) + if (ThreadID == gwp_asan::kInvalidThreadID) Printf("0x%zx was allocated by thread here:\n", ErrorPtr); else Printf("0x%zx was allocated by thread %zu here:\n", ErrorPtr, ThreadID); @@ -224,5 +151,69 @@ AllocMeta, Trace, kMaximumStackFramesForCrashTrace); PrintBacktrace(Trace, TraceLength, Printf); } -} // namespace crash_handler + +struct sigaction PreviousHandler; +bool SignalHandlerInstalled; +gwp_asan::GuardedPoolAllocator *GPAForSignalHandler; +Printf_t PrintfForSignalHandler; +PrintBacktrace_t PrintBacktraceForSignalHandler; +SegvBacktrace_t BacktraceForSignalHandler; + +static void sigSegvHandler(int sig, siginfo_t *info, void *ucontext) { + if (GPAForSignalHandler) { + GPAForSignalHandler->stop(); + + dumpReport(reinterpret_cast(info->si_addr), + GPAForSignalHandler->getAllocatorState(), + GPAForSignalHandler->getMetadataRegion(), + BacktraceForSignalHandler, PrintfForSignalHandler, + PrintBacktraceForSignalHandler, ucontext); + } + + // Process any previous handlers. + 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(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) { + GPAForSignalHandler = GPA; + PrintfForSignalHandler = Printf; + PrintBacktraceForSignalHandler = PrintBacktrace; + BacktraceForSignalHandler = SegvBacktrace; + + struct sigaction Action = {}; + Action.sa_sigaction = sigSegvHandler; + Action.sa_flags = SA_SIGINFO; + sigaction(SIGSEGV, &Action, &PreviousHandler); + SignalHandlerInstalled = true; +} + +void uninstallSignalHandlers() { + if (SignalHandlerInstalled) { + sigaction(SIGSEGV, &PreviousHandler, nullptr); + SignalHandlerInstalled = false; + } +} +} // namespace segv_handler } // namespace gwp_asan diff --git a/compiler-rt/lib/gwp_asan/options.inc b/compiler-rt/lib/gwp_asan/options.inc --- a/compiler-rt/lib/gwp_asan/options.inc +++ b/compiler-rt/lib/gwp_asan/options.inc @@ -62,3 +62,5 @@ GWP_ASAN_OPTION(bool, InstallForkHandlers, true, "Install GWP-ASan atfork handlers to acquire internal locks " "before fork and release them after.") + +GWP_ASAN_OPTION(bool, help, false, "Print a summary of the available options.") diff --git a/compiler-rt/lib/gwp_asan/tests/CMakeLists.txt b/compiler-rt/lib/gwp_asan/tests/CMakeLists.txt --- a/compiler-rt/lib/gwp_asan/tests/CMakeLists.txt +++ b/compiler-rt/lib/gwp_asan/tests/CMakeLists.txt @@ -10,7 +10,7 @@ file(GLOB GWP_ASAN_HEADERS ../*.h) set(GWP_ASAN_UNITTESTS - optional/printf_sanitizer_common.cpp + ../optional/printf_sanitizer_common.cpp alignment.cpp backtrace.cpp basic.cpp diff --git a/compiler-rt/lib/gwp_asan/tests/harness.h b/compiler-rt/lib/gwp_asan/tests/harness.h --- a/compiler-rt/lib/gwp_asan/tests/harness.h +++ b/compiler-rt/lib/gwp_asan/tests/harness.h @@ -21,16 +21,12 @@ #include "gwp_asan/guarded_pool_allocator.h" #include "gwp_asan/optional/backtrace.h" +#include "gwp_asan/optional/printf.h" #include "gwp_asan/optional/segv_handler.h" #include "gwp_asan/options.h" namespace gwp_asan { namespace test { -// This printf-function getter allows other platforms (e.g. Android) to define -// their own signal-safe Printf function. In LLVM, we use -// `optional/printf_sanitizer_common.cpp` which supplies the __sanitizer::Printf -// for this purpose. -crash_handler::Printf_t getPrintfFunction(); // First call returns true, all the following calls return false. bool OnlyOnce(); @@ -86,19 +82,19 @@ gwp_asan::options::Options Opts; Opts.setDefaults(); - Opts.Backtrace = gwp_asan::options::getBacktraceFunction(); + Opts.Backtrace = gwp_asan::backtrace::getBacktraceFunction(); Opts.InstallForkHandlers = gwp_asan::test::OnlyOnce(); GPA.init(Opts); - gwp_asan::crash_handler::installSignalHandlers( - &GPA, gwp_asan::test::getPrintfFunction(), - gwp_asan::options::getPrintBacktraceFunction(), - gwp_asan::crash_handler::getSegvBacktraceFunction()); + gwp_asan::segv_handler::installSignalHandlers( + &GPA, gwp_asan::getPrintfFunction(), + gwp_asan::backtrace::getPrintBacktraceFunction(), + gwp_asan::backtrace::getSegvBacktraceFunction()); } void TearDown() override { GPA.uninitTestOnly(); - gwp_asan::crash_handler::uninstallSignalHandlers(); + gwp_asan::segv_handler::uninstallSignalHandlers(); } protected: