[GWP-ASan] Add inbuilt options parser.
authorMitch Phillips <31459023+hctim@users.noreply.github.com>
Fri, 15 Jan 2021 20:57:00 +0000 (12:57 -0800)
committerMitch Phillips <31459023+hctim@users.noreply.github.com>
Fri, 15 Jan 2021 20:57:05 +0000 (12:57 -0800)
Adds a modified options parser (shamefully pulled from Scudo, which
shamefully pulled it from sanitizer-common) to GWP-ASan. This allows
customers (Android) to parse options strings in a common way.

Depends on D94117.

AOSP side of these patches is staged at:

 - sepolicy (sysprops should only be settable by the shell, in both root and
 unrooted conditions):
 https://android-review.googlesource.com/c/platform/system/sepolicy/+/1517238

 - zygote updates:
 https://android-review.googlesource.com/c/platform/frameworks/base/+/1515009

 - bionic changes to add `gwp_asan.<process_name>` system property, and
 GWP_ASAN_OPTIONS environment variable:
 https://android-review.googlesource.com/c/platform/bionic/+/1514989

Reviewed By: eugenis

Differential Revision: https://reviews.llvm.org/D92696

15 files changed:
compiler-rt/lib/gwp_asan/CMakeLists.txt
compiler-rt/lib/gwp_asan/optional/options_parser.cpp
compiler-rt/lib/gwp_asan/optional/options_parser.h
compiler-rt/lib/gwp_asan/options.inc
compiler-rt/lib/gwp_asan/tests/CMakeLists.txt
compiler-rt/lib/gwp_asan/tests/options.cpp [new file with mode: 0644]
compiler-rt/lib/scudo/scudo_allocator.cpp
compiler-rt/lib/scudo/standalone/CMakeLists.txt
compiler-rt/lib/scudo/standalone/combined.h
compiler-rt/lib/scudo/standalone/flags.cpp
compiler-rt/lib/scudo/standalone/tests/CMakeLists.txt
compiler-rt/lib/scudo/standalone/tests/flags_test.cpp
compiler-rt/test/scudo/standalone/unit/lit.site.cfg.py.in
compiler-rt/tools/gwp_asan/CMakeLists.txt
compiler-rt/tools/gwp_asan/options_parser_fuzzer.cpp [new file with mode: 0644]

index 92f5785..599fa99 100644 (file)
@@ -41,11 +41,10 @@ append_list_if(COMPILER_RT_HAS_FPIC_FLAG -fPIC GWP_ASAN_CFLAGS)
 # 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 @@ set(GWP_ASAN_SEGV_HANDLER_HEADERS
   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 @@ if (COMPILER_RT_HAS_GWP_ASAN)
       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}
index 2e63862..6023412 100644 (file)
 //===----------------------------------------------------------------------===//
 
 #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>
 
-#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);
+};
+
+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);
 }
 
-const char *getCompileDefinitionGwpAsanDefaultOptions() {
-#ifdef GWP_ASAN_DEFAULT_OPTIONS
-  return SANITIZER_STRINGIFY(GWP_ASAN_DEFAULT_OPTIONS);
-#else
-  return "";
-#endif
+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() : "";
 }
 
-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();
+  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
index 7a6bfaf..a5a0628 100644 (file)
@@ -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();
+// 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();
@@ -24,8 +25,7 @@ Options &getOptions();
 } // 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_
index 67dcfae..4834dae 100644 (file)
@@ -62,3 +62,18 @@ GWP_ASAN_OPTION(
 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.")
+
+// =============================================================================
+// ==== WARNING
+// =============================================================================
+// If you are adding flags to GWP-ASan, please note that GWP-ASan flag strings
+// may be parsed by trusted system components (on Android, GWP-ASan flag strings
+// are parsed by libc during the dynamic loader). This means that GWP-ASan
+// should never feature flags like log paths on disk, because this can lead to
+// arbitrary file write and thus privilege escalation. For an example, see the
+// setuid ASan log_path exploits: https://www.exploit-db.com/exploits/46241.
+//
+// Please place all new flags above this warning, so that the warning always
+// stays at the bottom.
index c9634e4..abc02a4 100644 (file)
@@ -23,7 +23,8 @@ set(GWP_ASAN_UNITTESTS
   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 @@ if(COMPILER_RT_DEFAULT_TARGET_ARCH IN_LIST GWP_ASAN_SUPPORTED_ARCH)
     $<TARGET_OBJECTS:RTGwpAsan.${arch}>
     $<TARGET_OBJECTS:RTGwpAsanBacktraceSanitizerCommon.${arch}>
     $<TARGET_OBJECTS:RTGwpAsanSegvHandler.${arch}>
+    $<TARGET_OBJECTS:RTGwpAsanOptionsParser.${arch}>
     $<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
     $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>
     $<TARGET_OBJECTS:RTSanitizerCommonSymbolizer.${arch}>)
diff --git a/compiler-rt/lib/gwp_asan/tests/options.cpp b/compiler-rt/lib/gwp_asan/tests/options.cpp
new file mode 100644 (file)
index 0000000..ffa4bca
--- /dev/null
@@ -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 <stdarg.h>
+
+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("==", "Unknown 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", "Unknown option 'NOT_A_VALUE");
+}
index 954e51b..8286440 100644 (file)
@@ -672,7 +672,8 @@ static BackendT &getBackend() {
 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::backtrace::getBacktraceFunction();
   GuardedAlloc.init(Opts);
index dfae6dd..52063f4 100644 (file)
@@ -120,7 +120,7 @@ set(SCUDO_OBJECT_LIBS)
 
 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()
 
index 11370be..0df7a65 100644 (file)
@@ -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,17 +184,12 @@ public:
   // 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.
index de5153b..285143a 100644 (file)
@@ -23,13 +23,6 @@ void Flags::setDefaults() {
 #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 @@ void registerFlags(FlagParser *Parser, Flags *F) {
                        reinterpret_cast<void *>(&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<void *>(&F->GWP_ASAN_##Name));
-#include "gwp_asan/options.inc"
-#undef GWP_ASAN_OPTION
-#endif // GWP_ASAN_HOOKS
 }
 
 static const char *getCompileDefinitionScudoDefaultOptions() {
index 78c297a..4c2164e 100644 (file)
@@ -45,7 +45,7 @@ macro(add_scudo_unittest testname)
   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)
index 0205052..45918ad 100644 (file)
@@ -117,18 +117,3 @@ TEST(ScudoFlagsTest, AllocatorFlags) {
   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
index 67a3197..d6deb67 100644 (file)
@@ -13,4 +13,4 @@ config.test_source_root = config.test_exec_root
 
 # 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'
index b0f9f0c..4dc27cf 100644 (file)
@@ -14,7 +14,20 @@ if (LLVM_USE_SANITIZE_COVERAGE)
   target_include_directories(
       stack_trace_compressor_fuzzer PRIVATE ../../lib/)
 
+  add_executable(options_parser_fuzzer
+      ../../lib/gwp_asan/optional/options_parser.cpp
+      ../../lib/gwp_asan/optional/options_parser.h
+      options_parser_fuzzer.cpp)
+  set_target_properties(
+      options_parser_fuzzer PROPERTIES FOLDER "Fuzzers")
+  target_compile_options(
+      options_parser_fuzzer PRIVATE -fsanitize=fuzzer-no-link)
+  set_target_properties(
+      options_parser_fuzzer PROPERTIES LINK_FLAGS -fsanitize=fuzzer)
+  target_include_directories(
+      options_parser_fuzzer PRIVATE ../../lib/)
+
   if (TARGET gwp_asan)
-    add_dependencies(gwp_asan stack_trace_compressor_fuzzer)
+    add_dependencies(gwp_asan stack_trace_compressor_fuzzer options_parser_fuzzer)
   endif()
 endif()
diff --git a/compiler-rt/tools/gwp_asan/options_parser_fuzzer.cpp b/compiler-rt/tools/gwp_asan/options_parser_fuzzer.cpp
new file mode 100644 (file)
index 0000000..2d87f12
--- /dev/null
@@ -0,0 +1,12 @@
+#include <cstddef>
+#include <cstdint>
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include "gwp_asan/optional/options_parser.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+  FuzzedDataProvider Fdp(Data, Size);
+  gwp_asan::options::initOptions(Fdp.ConsumeRemainingBytesAsString().c_str());
+  return 0;
+}