[compiler-rt] ed4618e - [Scudo] [GWP-ASan] Add GWP-ASan to Scudo Standalone.

Mitch Phillips via llvm-commits llvm-commits at lists.llvm.org
Fri Dec 13 09:19:45 PST 2019


Author: Mitch Phillips
Date: 2019-12-13T09:09:41-08:00
New Revision: ed4618edb35688567fcf107785776e27028f4062

URL: https://github.com/llvm/llvm-project/commit/ed4618edb35688567fcf107785776e27028f4062
DIFF: https://github.com/llvm/llvm-project/commit/ed4618edb35688567fcf107785776e27028f4062.diff

LOG: [Scudo] [GWP-ASan] Add GWP-ASan to Scudo Standalone.

Summary:
Adds GWP-ASan to Scudo standalone. Default parameters are pulled across from the
GWP-ASan build. No backtrace support as of yet.

Reviewers: cryptoad, eugenis, pcc

Reviewed By: cryptoad

Subscribers: merge_guards_bot, mgorny, #sanitizers, llvm-commits, cferris, vlad.tsyrklevich, pcc

Tags: #sanitizers, #llvm

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

Added: 
    

Modified: 
    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/flags.h
    compiler-rt/lib/scudo/standalone/flags_parser.h
    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/unittests/lit.common.unit.configured.in

Removed: 
    


################################################################################
diff  --git a/compiler-rt/lib/scudo/standalone/CMakeLists.txt b/compiler-rt/lib/scudo/standalone/CMakeLists.txt
index 08a093ce69ff..6b3532b9d6b5 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 53e0bf7d7302..864436de547b 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 @@ template <class Params> class Allocator {
     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 @@ template <class Params> class Allocator {
                           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 @@ template <class Params> class Allocator {
     // 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 @@ template <class Params> class Allocator {
     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 @@ template <class Params> class Allocator {
     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 @@ template <class Params> class Allocator {
   // 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 1e970ae49505..25407899fd40 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 edd39a1b8ba9..2cd0a5b1334b 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 857b50e880ec..32511f768c66 100644
--- a/compiler-rt/lib/scudo/standalone/flags_parser.h
+++ b/compiler-rt/lib/scudo/standalone/flags_parser.h
@@ -29,7 +29,7 @@ class FlagParser {
   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 f1f9400ff951..470f89df0227 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 45918ad4d2ca..85ae422e70f4 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 ef34739b807d..67a31973d15e 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 55c6b3416603..d959d43989ca 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.


        


More information about the llvm-commits mailing list