diff options
| -rw-r--r-- | compiler-rt/lib/scudo/standalone/CMakeLists.txt | 10 | ||||
| -rw-r--r-- | compiler-rt/lib/scudo/standalone/combined.h | 63 | ||||
| -rw-r--r-- | compiler-rt/lib/scudo/standalone/flags.cpp | 15 | ||||
| -rw-r--r-- | compiler-rt/lib/scudo/standalone/flags.h | 8 | ||||
| -rw-r--r-- | compiler-rt/lib/scudo/standalone/flags_parser.h | 2 | ||||
| -rw-r--r-- | compiler-rt/lib/scudo/standalone/tests/CMakeLists.txt | 8 | ||||
| -rw-r--r-- | compiler-rt/lib/scudo/standalone/tests/flags_test.cpp | 17 | ||||
| -rw-r--r-- | compiler-rt/test/scudo/standalone/unit/lit.site.cfg.py.in | 4 | ||||
| -rw-r--r-- | compiler-rt/unittests/lit.common.unit.configured.in | 1 | 
9 files changed, 127 insertions, 1 deletions
diff --git a/compiler-rt/lib/scudo/standalone/CMakeLists.txt b/compiler-rt/lib/scudo/standalone/CMakeLists.txt index 08a093ce69f..6b3532b9d6b 100644 --- a/compiler-rt/lib/scudo/standalone/CMakeLists.txt +++ b/compiler-rt/lib/scudo/standalone/CMakeLists.txt @@ -1,4 +1,5 @@  add_compiler_rt_component(scudo_standalone) +add_dependencies(scudo_standalone gwp_asan)  include_directories(../..) @@ -101,6 +102,13 @@ set(SCUDO_SOURCES_CXX_WRAPPERS    wrappers_cpp.cpp    ) +set(SCUDO_OBJECT_LIBS) + +if (COMPILER_RT_HAS_GWP_ASAN) +  list(APPEND SCUDO_OBJECT_LIBS RTGwpAsan) +  list(APPEND SCUDO_CFLAGS -DGWP_ASAN_HOOKS) +endif() +  if(COMPILER_RT_HAS_SCUDO_STANDALONE)    add_compiler_rt_object_libraries(RTScudoStandalone      ARCHS ${SCUDO_STANDALONE_SUPPORTED_ARCH} @@ -124,6 +132,7 @@ if(COMPILER_RT_HAS_SCUDO_STANDALONE)      SOURCES ${SCUDO_SOURCES} ${SCUDO_SOURCES_C_WRAPPERS}      ADDITIONAL_HEADERS ${SCUDO_HEADERS}      CFLAGS ${SCUDO_CFLAGS} +    OBJECT_LIBS ${SCUDO_OBJECT_LIBS}      PARENT_TARGET scudo_standalone)    add_compiler_rt_runtime(clang_rt.scudo_standalone_cxx      STATIC @@ -131,6 +140,7 @@ if(COMPILER_RT_HAS_SCUDO_STANDALONE)      SOURCES ${SCUDO_SOURCES_CXX_WRAPPERS}      ADDITIONAL_HEADERS ${SCUDO_HEADERS}      CFLAGS ${SCUDO_CFLAGS} +    OBJECT_LIBS ${SCUDO_OBJECT_LIBS}      PARENT_TARGET scudo_standalone)    add_subdirectory(benchmarks) diff --git a/compiler-rt/lib/scudo/standalone/combined.h b/compiler-rt/lib/scudo/standalone/combined.h index 53e0bf7d730..864436de547 100644 --- a/compiler-rt/lib/scudo/standalone/combined.h +++ b/compiler-rt/lib/scudo/standalone/combined.h @@ -18,8 +18,19 @@  #include "quarantine.h"  #include "report.h"  #include "secondary.h" +#include "string_utils.h"  #include "tsd.h" +#ifdef GWP_ASAN_HOOKS +# include "gwp_asan/guarded_pool_allocator.h" +// GWP-ASan is declared here in order to avoid indirect call overhead. It's also +// instantiated outside of the Allocator class, as the allocator is only +// zero-initialised. GWP-ASan requires constant initialisation, and the Scudo +// allocator doesn't have a constexpr constructor (see discussion here: +// https://reviews.llvm.org/D69265#inline-624315). +static gwp_asan::GuardedPoolAllocator GuardedAlloc; +#endif // GWP_ASAN_HOOKS +  namespace scudo {  template <class Params> class Allocator { @@ -133,6 +144,22 @@ public:      Quarantine.init(          static_cast<uptr>(getFlags()->quarantine_size_kb << 10),          static_cast<uptr>(getFlags()->thread_local_quarantine_size_kb << 10)); + +#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; +    Opt.Printf = Printf; +    GuardedAlloc.init(Opt); +#endif // GWP_ASAN_HOOKS    }    void reset() { memset(this, 0, sizeof(*this)); } @@ -164,6 +191,14 @@ public:                            uptr Alignment = MinAlignment,                            bool ZeroContents = false) {      initThreadMaybe(); + +#ifdef GWP_ASAN_HOOKS +    if (UNLIKELY(GuardedAlloc.shouldSample())) { +      if (void *Ptr = GuardedAlloc.allocate(roundUpTo(Size, Alignment))) +        return Ptr; +    } +#endif // GWP_ASAN_HOOKS +      ZeroContents |= static_cast<bool>(Options.ZeroContents);      if (UNLIKELY(Alignment > MaxAlignment)) { @@ -261,6 +296,13 @@ public:      // being destroyed properly. Any other heap operation will do a full init.      initThreadMaybe(/*MinimalInit=*/true); +#ifdef GWP_ASAN_HOOKS +    if (UNLIKELY(GuardedAlloc.pointerIsMine(Ptr))) { +      GuardedAlloc.deallocate(Ptr); +      return; +    } +#endif // GWP_ASAN_HOOKS +      if (&__scudo_deallocate_hook)        __scudo_deallocate_hook(Ptr); @@ -300,6 +342,17 @@ public:      DCHECK_NE(OldPtr, nullptr);      DCHECK_NE(NewSize, 0); +#ifdef GWP_ASAN_HOOKS +    if (UNLIKELY(GuardedAlloc.pointerIsMine(OldPtr))) { +      uptr OldSize = GuardedAlloc.getSize(OldPtr); +      void *NewPtr = allocate(NewSize, Chunk::Origin::Malloc, Alignment); +      if (NewPtr) +        memcpy(NewPtr, OldPtr, (NewSize < OldSize) ? NewSize : OldSize); +      GuardedAlloc.deallocate(OldPtr); +      return NewPtr; +    } +#endif // GWP_ASAN_HOOKS +      if (UNLIKELY(!isAligned(reinterpret_cast<uptr>(OldPtr), MinAlignment)))        reportMisalignedPointer(AllocatorAction::Reallocating, OldPtr); @@ -446,6 +499,12 @@ public:      initThreadMaybe();      if (UNLIKELY(!Ptr))        return 0; + +#ifdef GWP_ASAN_HOOKS +    if (UNLIKELY(GuardedAlloc.pointerIsMine(Ptr))) +      return GuardedAlloc.getSize(Ptr); +#endif // GWP_ASAN_HOOKS +      Chunk::UnpackedHeader Header;      Chunk::loadHeader(Cookie, Ptr, &Header);      // Getting the usable size of a chunk only makes sense if it's allocated. @@ -464,6 +523,10 @@ public:    // A corrupted chunk will not be reported as owned, which is WAI.    bool isOwned(const void *Ptr) {      initThreadMaybe(); +#ifdef GWP_ASAN_HOOKS +    if (GuardedAlloc.pointerIsMine(Ptr)) +      return true; +#endif // GWP_ASAN_HOOKS      if (!Ptr || !isAligned(reinterpret_cast<uptr>(Ptr), MinAlignment))        return false;      Chunk::UnpackedHeader Header; diff --git a/compiler-rt/lib/scudo/standalone/flags.cpp b/compiler-rt/lib/scudo/standalone/flags.cpp index 1e970ae4950..25407899fd4 100644 --- a/compiler-rt/lib/scudo/standalone/flags.cpp +++ b/compiler-rt/lib/scudo/standalone/flags.cpp @@ -22,6 +22,13 @@ 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) { @@ -30,6 +37,14 @@ 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() { diff --git a/compiler-rt/lib/scudo/standalone/flags.h b/compiler-rt/lib/scudo/standalone/flags.h index edd39a1b8ba..2cd0a5b1334 100644 --- a/compiler-rt/lib/scudo/standalone/flags.h +++ b/compiler-rt/lib/scudo/standalone/flags.h @@ -17,6 +17,14 @@ struct Flags {  #define SCUDO_FLAG(Type, Name, DefaultValue, Description) Type Name;  #include "flags.inc"  #undef SCUDO_FLAG + +#ifdef GWP_ASAN_HOOKS +#define GWP_ASAN_OPTION(Type, Name, DefaultValue, Description)                 \ +  Type GWP_ASAN_##Name; +#include "gwp_asan/options.inc" +#undef GWP_ASAN_OPTION +#endif // GWP_ASAN_HOOKS +    void setDefaults();  }; diff --git a/compiler-rt/lib/scudo/standalone/flags_parser.h b/compiler-rt/lib/scudo/standalone/flags_parser.h index 857b50e880e..32511f768c6 100644 --- a/compiler-rt/lib/scudo/standalone/flags_parser.h +++ b/compiler-rt/lib/scudo/standalone/flags_parser.h @@ -29,7 +29,7 @@ public:    void printFlagDescriptions();  private: -  static const u32 MaxFlags = 12; +  static const u32 MaxFlags = 16;    struct Flag {      const char *Name;      const char *Desc; diff --git a/compiler-rt/lib/scudo/standalone/tests/CMakeLists.txt b/compiler-rt/lib/scudo/standalone/tests/CMakeLists.txt index f1f9400ff95..470f89df022 100644 --- a/compiler-rt/lib/scudo/standalone/tests/CMakeLists.txt +++ b/compiler-rt/lib/scudo/standalone/tests/CMakeLists.txt @@ -20,6 +20,10 @@ if(ANDROID)    list(APPEND SCUDO_UNITTEST_CFLAGS -fno-emulated-tls)  endif() +if (COMPILER_RT_HAS_GWP_ASAN) +  list(APPEND SCUDO_UNITTEST_CFLAGS -DGWP_ASAN_HOOKS) +endif() +  set(SCUDO_TEST_ARCH ${SCUDO_STANDALONE_SUPPORTED_ARCH})  # gtests requires c++ @@ -38,6 +42,10 @@ endforeach()  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) +  endif() +    if(COMPILER_RT_HAS_SCUDO_STANDALONE)      foreach(arch ${SCUDO_TEST_ARCH})        # Additional runtime objects get added along RTScudoStandalone diff --git a/compiler-rt/lib/scudo/standalone/tests/flags_test.cpp b/compiler-rt/lib/scudo/standalone/tests/flags_test.cpp index 45918ad4d2c..85ae422e70f 100644 --- a/compiler-rt/lib/scudo/standalone/tests/flags_test.cpp +++ b/compiler-rt/lib/scudo/standalone/tests/flags_test.cpp @@ -117,3 +117,20 @@ TEST(ScudoFlagsTest, AllocatorFlags) {    EXPECT_TRUE(Flags.delete_size_mismatch);    EXPECT_EQ(2048, Flags.quarantine_max_chunk_size);  } + +TEST(ScudoFlagsTest, GWPASanFlags) { +#ifndef GWP_ASAN_HOOKS +  GTEST_SKIP() << "GWP-ASan wasn't built as part of Scudo Standalone."; +#endif // GWP_ASAN_HOOKS + +  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); +} 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 ef34739b807..67a31973d15 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 @@ -10,3 +10,7 @@ config.name = 'ScudoStandalone-Unit'  # For unit tests, we define it as build directory with unit tests.  config.test_exec_root = "@COMPILER_RT_BINARY_DIR@/lib/scudo/standalone/tests"  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' diff --git a/compiler-rt/unittests/lit.common.unit.configured.in b/compiler-rt/unittests/lit.common.unit.configured.in index 55c6b341660..d959d43989c 100644 --- a/compiler-rt/unittests/lit.common.unit.configured.in +++ b/compiler-rt/unittests/lit.common.unit.configured.in @@ -11,6 +11,7 @@ config.llvm_build_mode = "@LLVM_BUILD_MODE@"  config.host_arch = "@HOST_ARCH@"  config.host_os = "@HOST_OS@"  config.llvm_lib_dir = "@LLVM_LIBRARY_DIR@" +config.gwp_asan = @COMPILER_RT_HAS_GWP_ASAN_PYBOOL@  # LLVM tools dir and build mode can be passed in lit parameters,  # so try to apply substitution.  | 

