diff --git a/compiler-rt/lib/gwp_asan/CMakeLists.txt b/compiler-rt/lib/gwp_asan/CMakeLists.txt --- a/compiler-rt/lib/gwp_asan/CMakeLists.txt +++ b/compiler-rt/lib/gwp_asan/CMakeLists.txt @@ -41,11 +41,10 @@ # Remove -stdlib= which is unused when passing -nostdinc++. string(REGEX REPLACE "-stdlib=[a-zA-Z+]*" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) -# Options parsing support is optional. GwpAsan is totally independent of -# sanitizer_common, the options parser is not. This is an optional library -# that can be used by an allocator to automatically parse GwpAsan options from -# the environment variable GWP_ASAN_FLAGS, but the allocator can choose to -# implement its own options parsing and populate the Options struct itself. +# Options parsing support is optional. This is an optional library that can be +# used by an allocator to automatically parse GwpAsan options from the +# environment variable GWP_ASAN_FLAGS, but the allocator can choose to implement +# its own options parsing and populate the Options struct itself. set(GWP_ASAN_OPTIONS_PARSER_SOURCES optional/options_parser.cpp ) @@ -64,11 +63,7 @@ options.h) set(GWP_ASAN_OPTIONS_PARSER_CFLAGS - ${GWP_ASAN_CFLAGS} - ${SANITIZER_COMMON_CFLAGS}) -set(GWP_ASAN_OPTIONS_PARSER_OBJECT_LIBS - RTSanitizerCommon - RTSanitizerCommonNoLibc) + ${GWP_ASAN_CFLAGS}) if (COMPILER_RT_HAS_GWP_ASAN) foreach(arch ${GWP_ASAN_SUPPORTED_ARCH}) @@ -89,11 +84,6 @@ ADDITIONAL_HEADERS ${GWP_ASAN_HEADERS} CFLAGS ${GWP_ASAN_CFLAGS}) - # Note: If you choose to add this as an object library, ensure you also - # include the sanitizer_common flag parsing object lib (generally - # 'RTSanitizerCommonNoTermination'). Also, you'll need to either implement - # your own backtrace support (see optional/backtrace.h), or choose between one - # of the pre-implemented backtrace support options (see below). add_compiler_rt_object_libraries(RTGwpAsanOptionsParser ARCHS ${GWP_ASAN_SUPPORTED_ARCH} SOURCES ${GWP_ASAN_OPTIONS_PARSER_SOURCES} 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,7 +14,9 @@ #include #include "gwp_asan/optional/backtrace.h" +#include "gwp_asan/optional/printf.h" #include "gwp_asan/options.h" +#include "gwp_asan/definitions.h" namespace { size_t Backtrace(uintptr_t *TraceBuffer, size_t Size) { @@ -32,7 +34,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 +57,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/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/optional/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 @@ -23,7 +23,8 @@ thread_contention.cpp harness.cpp enable_disable.cpp - late_init.cpp) + late_init.cpp + options.cpp) set(GWP_ASAN_UNIT_TEST_HEADERS ${GWP_ASAN_HEADERS} @@ -48,6 +49,7 @@ $ $ $ + $ $ $ $) 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: diff --git a/compiler-rt/lib/gwp_asan/tests/options.cpp b/compiler-rt/lib/gwp_asan/tests/options.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/lib/gwp_asan/tests/options.cpp @@ -0,0 +1,63 @@ +//===-- options.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/tests/harness.h" + +#include "gwp_asan/optional/options_parser.h" +#include "gwp_asan/options.h" + +#include + +static char Message[1024]; +void MessageRecorder(const char *Format, ...) { + va_list Args; + va_start(Args, Format); + vsprintf(Message + strlen(Message), Format, Args); + va_end(Args); +} + +TEST(GwpAsanOptionsTest, Basic) { + Message[0] = '\0'; + gwp_asan::options::initOptions("Enabled=0:SampleRate=4:" + "InstallSignalHandlers=false", + MessageRecorder); + gwp_asan::options::Options Opts = gwp_asan::options::getOptions(); + EXPECT_EQ('\0', Message[0]); + EXPECT_FALSE(Opts.Enabled); + EXPECT_FALSE(Opts.InstallSignalHandlers); + EXPECT_EQ(4, Opts.SampleRate); +} + +void RunErrorTest(const char *OptionsStr, const char *ErrorNeedle) { + Message[0] = '\0'; + gwp_asan::options::initOptions(OptionsStr, MessageRecorder); + EXPECT_NE('\0', Message[0]) + << "Options string \"" << OptionsStr << "\" didn't generate an error."; + EXPECT_NE(nullptr, strstr(Message, ErrorNeedle)) + << "Couldn't find error needle \"" << ErrorNeedle + << "\" in haystack created by options string \"" << OptionsStr + << "\". Error was: \"" << Message << "\"."; +} + +TEST(GwpAsanOptionsTest, FailureModes) { + RunErrorTest("Enabled=2", "Invalid boolean value '2' for option 'Enabled'"); + RunErrorTest("Enabled=1:MaxSimultaneousAllocations=0", + "MaxSimultaneousAllocations must be > 0"); + RunErrorTest("Enabled=1:MaxSimultaneousAllocations=-1", + "MaxSimultaneousAllocations must be > 0"); + RunErrorTest("Enabled=1:SampleRate=0", "SampleRate must be > 0"); + RunErrorTest("Enabled=1:SampleRate=-1", "SampleRate must be > 0"); + RunErrorTest("Enabled=", "Invalid boolean value '' for option 'Enabled'"); + RunErrorTest("==", "Found 1 unrecognized Option"); + RunErrorTest("Enabled==0", "Invalid boolean value '=0' for option 'Enabled'"); + RunErrorTest("Enabled:", "Expected '=' when parsing option 'Enabled:'"); + RunErrorTest("Enabled:=", "Expected '=' when parsing option 'Enabled:='"); + RunErrorTest("SampleRate=NOT_A_NUMBER", + "Invalid integer value 'NOT_A_NUMBER' for option 'SampleRate'"); + RunErrorTest("NOT_A_VALUE=0", "Found 1 unrecognized Option"); +} diff --git a/compiler-rt/lib/scudo/scudo_allocator.cpp b/compiler-rt/lib/scudo/scudo_allocator.cpp --- a/compiler-rt/lib/scudo/scudo_allocator.cpp +++ b/compiler-rt/lib/scudo/scudo_allocator.cpp @@ -672,16 +672,16 @@ void initScudo() { Instance.init(); #ifdef GWP_ASAN_HOOKS - gwp_asan::options::initOptions(); + gwp_asan::options::initOptions(__sanitizer::GetEnv("GWP_ASAN_OPTIONS"), Printf); gwp_asan::options::Options &Opts = gwp_asan::options::getOptions(); - Opts.Backtrace = gwp_asan::options::getBacktraceFunction(); + Opts.Backtrace = gwp_asan::backtrace::getBacktraceFunction(); GuardedAlloc.init(Opts); if (Opts.InstallSignalHandlers) - gwp_asan::crash_handler::installSignalHandlers( + gwp_asan::segv_handler::installSignalHandlers( &GuardedAlloc, __sanitizer::Printf, - gwp_asan::options::getPrintBacktraceFunction(), - gwp_asan::crash_handler::getSegvBacktraceFunction()); + gwp_asan::backtrace::getPrintBacktraceFunction(), + gwp_asan::backtrace::getSegvBacktraceFunction()); #endif // GWP_ASAN_HOOKS } diff --git a/compiler-rt/lib/scudo/standalone/CMakeLists.txt b/compiler-rt/lib/scudo/standalone/CMakeLists.txt --- a/compiler-rt/lib/scudo/standalone/CMakeLists.txt +++ b/compiler-rt/lib/scudo/standalone/CMakeLists.txt @@ -120,7 +120,7 @@ if (COMPILER_RT_HAS_GWP_ASAN) list(APPEND SCUDO_OBJECT_LIBS - RTGwpAsan RTGwpAsanBacktraceLibc RTGwpAsanSegvHandler) + RTGwpAsan RTGwpAsanBacktraceLibc RTGwpAsanSegvHandler RTGwpAsanOptionsParser) list(APPEND SCUDO_CFLAGS -DGWP_ASAN_HOOKS) endif() diff --git a/compiler-rt/lib/scudo/standalone/combined.h b/compiler-rt/lib/scudo/standalone/combined.h --- a/compiler-rt/lib/scudo/standalone/combined.h +++ b/compiler-rt/lib/scudo/standalone/combined.h @@ -28,6 +28,7 @@ #ifdef GWP_ASAN_HOOKS #include "gwp_asan/guarded_pool_allocator.h" #include "gwp_asan/optional/backtrace.h" +#include "gwp_asan/optional/options_parser.h" #include "gwp_asan/optional/segv_handler.h" #endif // GWP_ASAN_HOOKS @@ -183,28 +184,24 @@ // be functional, best called from PostInitCallback. void initGwpAsan() { #ifdef GWP_ASAN_HOOKS - gwp_asan::options::Options Opt; - Opt.Enabled = getFlags()->GWP_ASAN_Enabled; // Bear in mind - Scudo has its own alignment guarantees that are strictly // enforced. Scudo exposes the same allocation function for everything from // malloc() to posix_memalign, so in general this flag goes unused, as Scudo // will always ask GWP-ASan for an aligned amount of bytes. - Opt.PerfectlyRightAlign = getFlags()->GWP_ASAN_PerfectlyRightAlign; - Opt.MaxSimultaneousAllocations = - getFlags()->GWP_ASAN_MaxSimultaneousAllocations; - Opt.SampleRate = getFlags()->GWP_ASAN_SampleRate; - Opt.InstallSignalHandlers = getFlags()->GWP_ASAN_InstallSignalHandlers; + gwp_asan::options::initOptions(getEnv("GWP_ASAN_OPTIONS"), Printf); + gwp_asan::options::Options Opt = gwp_asan::options::getOptions(); // Embedded GWP-ASan is locked through the Scudo atfork handler (via // Allocator::disable calling GWPASan.disable). Disable GWP-ASan's atfork // handler. Opt.InstallForkHandlers = false; - Opt.Backtrace = gwp_asan::options::getBacktraceFunction(); + Opt.Backtrace = gwp_asan::backtrace::getBacktraceFunction(); GuardedAlloc.init(Opt); if (Opt.InstallSignalHandlers) - gwp_asan::crash_handler::installSignalHandlers( - &GuardedAlloc, Printf, gwp_asan::options::getPrintBacktraceFunction(), - gwp_asan::crash_handler::getSegvBacktraceFunction()); + gwp_asan::segv_handler::installSignalHandlers( + &GuardedAlloc, Printf, + gwp_asan::backtrace::getPrintBacktraceFunction(), + gwp_asan::backtrace::getSegvBacktraceFunction()); #endif // GWP_ASAN_HOOKS } @@ -219,7 +216,7 @@ Primary.unmapTestOnly(); #ifdef GWP_ASAN_HOOKS if (getFlags()->GWP_ASAN_InstallSignalHandlers) - gwp_asan::crash_handler::uninstallSignalHandlers(); + gwp_asan::segv_handler::uninstallSignalHandlers(); GuardedAlloc.uninitTestOnly(); #endif // GWP_ASAN_HOOKS } diff --git a/compiler-rt/lib/scudo/standalone/flags.cpp b/compiler-rt/lib/scudo/standalone/flags.cpp --- a/compiler-rt/lib/scudo/standalone/flags.cpp +++ b/compiler-rt/lib/scudo/standalone/flags.cpp @@ -23,13 +23,6 @@ #define SCUDO_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue; #include "flags.inc" #undef SCUDO_FLAG - -#ifdef GWP_ASAN_HOOKS -#define GWP_ASAN_OPTION(Type, Name, DefaultValue, Description) \ - GWP_ASAN_##Name = DefaultValue; -#include "gwp_asan/options.inc" -#undef GWP_ASAN_OPTION -#endif // GWP_ASAN_HOOKS } void registerFlags(FlagParser *Parser, Flags *F) { @@ -38,14 +31,6 @@ reinterpret_cast(&F->Name)); #include "flags.inc" #undef SCUDO_FLAG - -#ifdef GWP_ASAN_HOOKS -#define GWP_ASAN_OPTION(Type, Name, DefaultValue, Description) \ - Parser->registerFlag("GWP_ASAN_" #Name, Description, FlagType::FT_##Type, \ - reinterpret_cast(&F->GWP_ASAN_##Name)); -#include "gwp_asan/options.inc" -#undef GWP_ASAN_OPTION -#endif // GWP_ASAN_HOOKS } static const char *getCompileDefinitionScudoDefaultOptions() { diff --git a/compiler-rt/lib/scudo/standalone/tests/CMakeLists.txt b/compiler-rt/lib/scudo/standalone/tests/CMakeLists.txt --- a/compiler-rt/lib/scudo/standalone/tests/CMakeLists.txt +++ b/compiler-rt/lib/scudo/standalone/tests/CMakeLists.txt @@ -45,7 +45,7 @@ cmake_parse_arguments(TEST "" "" "SOURCES;ADDITIONAL_RTOBJECTS" ${ARGN}) if (COMPILER_RT_HAS_GWP_ASAN) list(APPEND TEST_ADDITIONAL_RTOBJECTS - RTGwpAsan RTGwpAsanBacktraceLibc RTGwpAsanSegvHandler) + RTGwpAsan RTGwpAsanBacktraceLibc RTGwpAsanSegvHandler RTGwpAsanOptionsParser) endif() if(COMPILER_RT_HAS_SCUDO_STANDALONE) diff --git a/compiler-rt/lib/scudo/standalone/tests/flags_test.cpp b/compiler-rt/lib/scudo/standalone/tests/flags_test.cpp --- a/compiler-rt/lib/scudo/standalone/tests/flags_test.cpp +++ b/compiler-rt/lib/scudo/standalone/tests/flags_test.cpp @@ -117,18 +117,3 @@ EXPECT_TRUE(Flags.delete_size_mismatch); EXPECT_EQ(2048, Flags.quarantine_max_chunk_size); } - -#ifdef GWP_ASAN_HOOKS -TEST(ScudoFlagsTest, GWPASanFlags) { - scudo::FlagParser Parser; - scudo::Flags Flags; - scudo::registerFlags(&Parser, &Flags); - Flags.setDefaults(); - Flags.GWP_ASAN_Enabled = false; - Parser.parseString("GWP_ASAN_Enabled=true:GWP_ASAN_SampleRate=1:" - "GWP_ASAN_InstallSignalHandlers=false"); - EXPECT_TRUE(Flags.GWP_ASAN_Enabled); - EXPECT_FALSE(Flags.GWP_ASAN_InstallSignalHandlers); - EXPECT_EQ(1, Flags.GWP_ASAN_SampleRate); -} -#endif // GWP_ASAN_HOOKS diff --git a/compiler-rt/test/scudo/standalone/unit/lit.site.cfg.py.in b/compiler-rt/test/scudo/standalone/unit/lit.site.cfg.py.in --- a/compiler-rt/test/scudo/standalone/unit/lit.site.cfg.py.in +++ b/compiler-rt/test/scudo/standalone/unit/lit.site.cfg.py.in @@ -13,4 +13,4 @@ # Disable GWP-ASan for scudo internal tests. if config.gwp_asan: - config.environment['SCUDO_OPTIONS'] = 'GWP_ASAN_Enabled=0' + config.environment['GWP_ASAN_OPTIONS'] = 'Enabled=false'