aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/compiler-rt/lib/gwp_asan/optional')
-rw-r--r--contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/backtrace.h53
-rw-r--r--contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/backtrace_fuchsia.cpp27
-rw-r--r--contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/backtrace_linux_libc.cpp67
-rw-r--r--contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/backtrace_sanitizer_common.cpp101
-rw-r--r--contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/options_parser.cpp258
-rw-r--r--contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/options_parser.h31
-rw-r--r--contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/printf.h33
-rw-r--r--contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/segv_handler.h34
-rw-r--r--contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/segv_handler_fuchsia.cpp23
-rw-r--r--contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/segv_handler_posix.cpp270
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