From 6a42cbf6d2116b52cb59aa3e23bef93a30cf2dc8 Mon Sep 17 00:00:00 2001 From: Mitch Phillips <31459023+hctim@users.noreply.github.com> Date: Fri, 15 Jan 2021 12:57:00 -0800 Subject: [PATCH] [GWP-ASan] Add inbuilt options parser. 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.` 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 --- compiler-rt/lib/gwp_asan/CMakeLists.txt | 20 +- .../lib/gwp_asan/optional/options_parser.cpp | 249 +++++++++++++++--- .../lib/gwp_asan/optional/options_parser.h | 12 +- compiler-rt/lib/gwp_asan/options.inc | 15 ++ compiler-rt/lib/gwp_asan/tests/CMakeLists.txt | 4 +- compiler-rt/lib/gwp_asan/tests/options.cpp | 63 +++++ compiler-rt/lib/scudo/scudo_allocator.cpp | 3 +- .../lib/scudo/standalone/CMakeLists.txt | 2 +- compiler-rt/lib/scudo/standalone/combined.h | 10 +- compiler-rt/lib/scudo/standalone/flags.cpp | 15 -- .../lib/scudo/standalone/tests/CMakeLists.txt | 2 +- .../lib/scudo/standalone/tests/flags_test.cpp | 15 -- .../scudo/standalone/unit/lit.site.cfg.py.in | 2 +- compiler-rt/tools/gwp_asan/CMakeLists.txt | 15 +- .../tools/gwp_asan/options_parser_fuzzer.cpp | 12 + 15 files changed, 334 insertions(+), 105 deletions(-) create mode 100644 compiler-rt/lib/gwp_asan/tests/options.cpp create mode 100644 compiler-rt/tools/gwp_asan/options_parser_fuzzer.cpp diff --git a/compiler-rt/lib/gwp_asan/CMakeLists.txt b/compiler-rt/lib/gwp_asan/CMakeLists.txt index 92f578585b54..599fa9904e47 100644 --- a/compiler-rt/lib/gwp_asan/CMakeLists.txt +++ b/compiler-rt/lib/gwp_asan/CMakeLists.txt @@ -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} diff --git a/compiler-rt/lib/gwp_asan/optional/options_parser.cpp b/compiler-rt/lib/gwp_asan/optional/options_parser.cpp index 2e6386286745..60234124e8ed 100644 --- a/compiler-rt/lib/gwp_asan/optional/options_parser.cpp +++ b/compiler-rt/lib/gwp_asan/optional/options_parser.cpp @@ -7,84 +7,251 @@ //===----------------------------------------------------------------------===// #include "gwp_asan/optional/options_parser.h" +#include "gwp_asan/optional/printf.h" +#include "gwp_asan/utilities.h" +#include #include #include #include #include -#include "gwp_asan/options.h" -#include "sanitizer_common/sanitizer_common.h" -#include "sanitizer_common/sanitizer_flag_parser.h" -#include "sanitizer_common/sanitizer_flags.h" - -namespace gwp_asan { -namespace options { namespace { -void registerGwpAsanFlags(__sanitizer::FlagParser *parser, Options *o) { -#define GWP_ASAN_OPTION(Type, Name, DefaultValue, Description) \ - RegisterFlag(parser, #Name, Description, &o->Name); +enum class OptionType : uint8_t { + OT_bool, + OT_int, +}; + +#define InvokeIfNonNull(Printf, ...) \ + do { \ + if (Printf) \ + Printf(__VA_ARGS__); \ + } while (0); + +class OptionParser { +public: + explicit OptionParser(gwp_asan::Printf_t PrintfForWarnings) + : Printf(PrintfForWarnings) {} + void registerOption(const char *Name, const char *Desc, OptionType Type, + void *Var); + void parseString(const char *S); + void printOptionDescriptions(); + +private: + // Calculate at compile-time how many options are available. +#define GWP_ASAN_OPTION(...) +1 + static constexpr size_t MaxOptions = 0 #include "gwp_asan/options.inc" + ; #undef GWP_ASAN_OPTION + + struct Option { + const char *Name; + const char *Desc; + OptionType Type; + void *Var; + } Options[MaxOptions]; + + size_t NumberOfOptions = 0; + const char *Buffer = nullptr; + uintptr_t Pos = 0; + gwp_asan::Printf_t Printf = nullptr; + + void skipWhitespace(); + void parseOptions(); + bool parseOption(); + bool setOptionToValue(const char *Name, const char *Value); +}; + +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); } -const char *getCompileDefinitionGwpAsanDefaultOptions() { -#ifdef GWP_ASAN_DEFAULT_OPTIONS - return SANITIZER_STRINGIFY(GWP_ASAN_DEFAULT_OPTIONS); -#else - return ""; -#endif +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(Options[I].Var)); + if (!Ok) + InvokeIfNonNull( + Printf, "GWP-ASan: Invalid boolean value '%s' for option '%s'.\n", + Value, Options[I].Name); + break; + case OptionType::OT_int: + char *ValueEnd; + *reinterpret_cast(Options[I].Var) = + static_cast(strtol(Value, &ValueEnd, 10)); + Ok = + *ValueEnd == '"' || *ValueEnd == '\'' || isSeparatorOrNull(*ValueEnd); + if (!Ok) + InvokeIfNonNull( + Printf, "GWP-ASan: Invalid integer value '%s' for option '%s'.\n", + Value, Options[I].Name); + break; + } + return Ok; + } + + 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); + OptionParser Parser(PrintfForWarnings); + registerGwpAsanOptions(&Parser, o); - // Override from compile definition. - Parser.ParseString(getCompileDefinitionGwpAsanDefaultOptions()); + // Override from the weak function definition in this executable. + Parser.parseString(getGwpAsanDefaultOptions()); - // Override from user-specified string. - Parser.ParseString(getGwpAsanDefaultOptions()); + // Override from the provided options string. + Parser.parseString(OptionsStr); - // Override from environment. - Parser.ParseString(__sanitizer::GetEnv("GWP_ASAN_OPTIONS")); - - __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 diff --git a/compiler-rt/lib/gwp_asan/optional/options_parser.h b/compiler-rt/lib/gwp_asan/optional/options_parser.h index 7a6bfaf0ce3e..a5a062801f8f 100644 --- a/compiler-rt/lib/gwp_asan/optional/options_parser.h +++ b/compiler-rt/lib/gwp_asan/optional/options_parser.h @@ -9,14 +9,15 @@ #ifndef GWP_ASAN_OPTIONAL_OPTIONS_PARSER_H_ #define GWP_ASAN_OPTIONAL_OPTIONS_PARSER_H_ -#include "gwp_asan/optional/backtrace.h" +#include "gwp_asan/optional/printf.h" #include "gwp_asan/options.h" -#include "sanitizer_common/sanitizer_common.h" namespace gwp_asan { namespace options { -// Parse the options from the GWP_ASAN_FLAGS environment variable. -void initOptions(); +// 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_ diff --git a/compiler-rt/lib/gwp_asan/options.inc b/compiler-rt/lib/gwp_asan/options.inc index 67dcfae170a8..4834daef6003 100644 --- a/compiler-rt/lib/gwp_asan/options.inc +++ b/compiler-rt/lib/gwp_asan/options.inc @@ -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. diff --git a/compiler-rt/lib/gwp_asan/tests/CMakeLists.txt b/compiler-rt/lib/gwp_asan/tests/CMakeLists.txt index c9634e471d99..abc02a49637c 100644 --- a/compiler-rt/lib/gwp_asan/tests/CMakeLists.txt +++ b/compiler-rt/lib/gwp_asan/tests/CMakeLists.txt @@ -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) $ $ $ + $ $ $ $) diff --git a/compiler-rt/lib/gwp_asan/tests/options.cpp b/compiler-rt/lib/gwp_asan/tests/options.cpp new file mode 100644 index 000000000000..ffa4bca5a09d --- /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("==", "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"); +} diff --git a/compiler-rt/lib/scudo/scudo_allocator.cpp b/compiler-rt/lib/scudo/scudo_allocator.cpp index 954e51bdee63..82864405dfb0 100644 --- a/compiler-rt/lib/scudo/scudo_allocator.cpp +++ b/compiler-rt/lib/scudo/scudo_allocator.cpp @@ -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); diff --git a/compiler-rt/lib/scudo/standalone/CMakeLists.txt b/compiler-rt/lib/scudo/standalone/CMakeLists.txt index dfae6dde5e4d..52063f4581d4 100644 --- a/compiler-rt/lib/scudo/standalone/CMakeLists.txt +++ b/compiler-rt/lib/scudo/standalone/CMakeLists.txt @@ -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() diff --git a/compiler-rt/lib/scudo/standalone/combined.h b/compiler-rt/lib/scudo/standalone/combined.h index 11370be3fb4a..0df7a652ffa5 100644 --- 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,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. diff --git a/compiler-rt/lib/scudo/standalone/flags.cpp b/compiler-rt/lib/scudo/standalone/flags.cpp index de5153b288b1..285143a5d6bb 100644 --- a/compiler-rt/lib/scudo/standalone/flags.cpp +++ b/compiler-rt/lib/scudo/standalone/flags.cpp @@ -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(&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 index 78c297ae7e80..4c2164e1c689 100644 --- a/compiler-rt/lib/scudo/standalone/tests/CMakeLists.txt +++ b/compiler-rt/lib/scudo/standalone/tests/CMakeLists.txt @@ -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) diff --git a/compiler-rt/lib/scudo/standalone/tests/flags_test.cpp b/compiler-rt/lib/scudo/standalone/tests/flags_test.cpp index 0205052edd26..45918ad4d2ca 100644 --- a/compiler-rt/lib/scudo/standalone/tests/flags_test.cpp +++ b/compiler-rt/lib/scudo/standalone/tests/flags_test.cpp @@ -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 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 index 67a31973d15e..d6deb67aaa3c 100644 --- 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 @@ 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' diff --git a/compiler-rt/tools/gwp_asan/CMakeLists.txt b/compiler-rt/tools/gwp_asan/CMakeLists.txt index b0f9f0cf9e5d..4dc27cf5d7dd 100644 --- a/compiler-rt/tools/gwp_asan/CMakeLists.txt +++ b/compiler-rt/tools/gwp_asan/CMakeLists.txt @@ -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 index 000000000000..2d87f12c14f1 --- /dev/null +++ b/compiler-rt/tools/gwp_asan/options_parser_fuzzer.cpp @@ -0,0 +1,12 @@ +#include +#include + +#include + +#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; +}