diff options
| author | Mitch Phillips <mitchphillips@outlook.com> | 2019-05-30 19:45:32 +0000 | 
|---|---|---|
| committer | Mitch Phillips <mitchphillips@outlook.com> | 2019-05-30 19:45:32 +0000 | 
| commit | 5f0f4e3ae03afa84034c01ddaac559e0b5cb6532 (patch) | |
| tree | a4d1f0c352380fb97a3eef96bf4537a179f0dac8 /compiler-rt | |
| parent | 04a38b924e7e37ad166b3f73f229411e3a2dfa25 (diff) | |
| download | bcm5719-llvm-5f0f4e3ae03afa84034c01ddaac559e0b5cb6532.tar.gz bcm5719-llvm-5f0f4e3ae03afa84034c01ddaac559e0b5cb6532.zip  | |
[GWP-ASan] Mutex implementation [2].
Summary:
See D60593 for further information.
This patch pulls out the mutex implementation and the required definitions file.
We implement our own mutex for GWP-ASan currently, because:
1. We must be compatible with the sum of the most restrictive elements of the supporting allocator's build system. Current targets for GWP-ASan include Scudo (on Linux and Fuchsia), and bionic (on Android).
2. Scudo specifies `-nostdlib++ -nonodefaultlibs`, meaning we can't use `std::mutex` or `mtx_t`.
3. We can't use `sanitizer_common`'s mutex, as the supporting allocators cannot afford the extra maintenance (Android, Fuchsia) and code size (Fuchsia) overheads that this would incur.
In future, we would like to implement a shared base mutex for GWP-ASan, Scudo and sanitizer_common. This will likely happen when both GWP-ASan and Scudo standalone are not in the development phase, at which point they will have stable requirements.
Reviewers: vlad.tsyrklevich, morehouse, jfb
Reviewed By: morehouse
Subscribers: dexonsmith, srhines, cfe-commits, kubamracek, mgorny, cryptoad, jfb, #sanitizers, llvm-commits, vitalybuka, eugenis
Tags: #sanitizers, #llvm, #clang
Differential Revision: https://reviews.llvm.org/D61923
llvm-svn: 362138
Diffstat (limited to 'compiler-rt')
| -rw-r--r-- | compiler-rt/lib/gwp_asan/CMakeLists.txt | 8 | ||||
| -rw-r--r-- | compiler-rt/lib/gwp_asan/mutex.h | 50 | ||||
| -rw-r--r-- | compiler-rt/lib/gwp_asan/platform_specific/mutex_posix.cpp | 30 | ||||
| -rw-r--r-- | compiler-rt/lib/gwp_asan/tests/CMakeLists.txt | 49 | ||||
| -rw-r--r-- | compiler-rt/lib/gwp_asan/tests/driver.cpp | 14 | ||||
| -rw-r--r-- | compiler-rt/lib/gwp_asan/tests/mutex_test.cpp | 89 | ||||
| -rw-r--r-- | compiler-rt/test/gwp_asan/CMakeLists.txt | 45 | ||||
| -rw-r--r-- | compiler-rt/test/gwp_asan/dummy_test.cc | 4 | ||||
| -rw-r--r-- | compiler-rt/test/gwp_asan/lit.cfg | 31 | ||||
| -rw-r--r-- | compiler-rt/test/gwp_asan/lit.site.cfg.in | 11 | ||||
| -rw-r--r-- | compiler-rt/test/gwp_asan/unit/lit.site.cfg.in | 9 | 
11 files changed, 339 insertions, 1 deletions
diff --git a/compiler-rt/lib/gwp_asan/CMakeLists.txt b/compiler-rt/lib/gwp_asan/CMakeLists.txt index 1f6d973b3a6..6c83d86c6c8 100644 --- a/compiler-rt/lib/gwp_asan/CMakeLists.txt +++ b/compiler-rt/lib/gwp_asan/CMakeLists.txt @@ -3,17 +3,19 @@ add_compiler_rt_component(gwp_asan)  include_directories(..)  set(GWP_ASAN_SOURCES +  platform_specific/mutex_posix.cpp    random.cpp  )  set(GWP_ASAN_HEADERS +  mutex.h    random.h  )  # Ensure that GWP-ASan meets the delegated requirements of some supporting  # allocators. Some supporting allocators (e.g. scudo standalone) cannot use any  # parts of the C++ standard library. -set(GWP_ASAN_CFLAGS -fno-rtti -fno-exceptions -nostdinc++) +set(GWP_ASAN_CFLAGS -fno-rtti -fno-exceptions -nostdinc++ -pthread)  # Remove -stdlib= which is unused when passing -nostdinc++.  string(REGEX REPLACE "-stdlib=[a-zA-Z+]*" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) @@ -37,3 +39,7 @@ if (COMPILER_RT_HAS_GWP_ASAN)        ADDITIONAL_HEADERS ${GWP_ASAN_HEADERS}        CFLAGS ${GWP_ASAN_CFLAGS})  endif() + +if(COMPILER_RT_INCLUDE_TESTS) +  add_subdirectory(tests) +endif() diff --git a/compiler-rt/lib/gwp_asan/mutex.h b/compiler-rt/lib/gwp_asan/mutex.h new file mode 100644 index 00000000000..c29df4cde16 --- /dev/null +++ b/compiler-rt/lib/gwp_asan/mutex.h @@ -0,0 +1,50 @@ +//===-- mutex.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_MUTEX_H_ +#define GWP_ASAN_MUTEX_H_ + +#ifdef __unix__ +#include <pthread.h> +#else +#error "GWP-ASan is not supported on this platform." +#endif + +namespace gwp_asan { +class Mutex { +public: +  constexpr Mutex() = default; +  ~Mutex() = default; +  Mutex(const Mutex &) = delete; +  Mutex &operator=(const Mutex &) = delete; +  // Lock the mutex. +  void lock(); +  // Nonblocking trylock of the mutex. Returns true if the lock was acquired. +  bool tryLock(); +  // Unlock the mutex. +  void unlock(); + +private: +#ifdef __unix__ +  pthread_mutex_t Mu = PTHREAD_MUTEX_INITIALIZER; +#endif // defined(__unix__) +}; + +class ScopedLock { +public: +  explicit ScopedLock(Mutex &Mx) : Mu(Mx) { Mu.lock(); } +  ~ScopedLock() { Mu.unlock(); } +  ScopedLock(const ScopedLock &) = delete; +  ScopedLock &operator=(const ScopedLock &) = delete; + +private: +  Mutex Μ +}; +} // namespace gwp_asan + +#endif // GWP_ASAN_MUTEX_H_ diff --git a/compiler-rt/lib/gwp_asan/platform_specific/mutex_posix.cpp b/compiler-rt/lib/gwp_asan/platform_specific/mutex_posix.cpp new file mode 100644 index 00000000000..e15bca88257 --- /dev/null +++ b/compiler-rt/lib/gwp_asan/platform_specific/mutex_posix.cpp @@ -0,0 +1,30 @@ +//===-- mutex_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 "mutex.h" + +#include <assert.h> +#include <pthread.h> + +namespace gwp_asan { +void Mutex::lock() { +  int Status = pthread_mutex_lock(&Mu); +  assert(Status == 0); +  // Remove warning for non-debug builds. +  (void)Status; +} + +bool Mutex::tryLock() { return pthread_mutex_trylock(&Mu) == 0; } + +void Mutex::unlock() { +  int Status = pthread_mutex_unlock(&Mu); +  assert(Status == 0); +  // Remove warning for non-debug builds. +  (void)Status; +} +} // namespace gwp_asan diff --git a/compiler-rt/lib/gwp_asan/tests/CMakeLists.txt b/compiler-rt/lib/gwp_asan/tests/CMakeLists.txt new file mode 100644 index 00000000000..6a59be5bca6 --- /dev/null +++ b/compiler-rt/lib/gwp_asan/tests/CMakeLists.txt @@ -0,0 +1,49 @@ +include(CompilerRTCompile) + +set(GWP_ASAN_UNITTEST_CFLAGS +  ${COMPILER_RT_UNITTEST_CFLAGS} +  ${COMPILER_RT_GTEST_CFLAGS} +  -I${COMPILER_RT_SOURCE_DIR}/lib/ +  -O2) + +file(GLOB GWP_ASAN_HEADERS ../*.h) +file(GLOB GWP_ASAN_UNITTESTS *.cpp) +set(GWP_ASAN_UNIT_TEST_HEADERS +  ${GWP_ASAN_HEADERS}) + +add_custom_target(GwpAsanUnitTests) +set_target_properties(GwpAsanUnitTests PROPERTIES FOLDER "Compiler-RT Tests") + +set(GWP_ASAN_UNITTEST_LINK_FLAGS ${COMPILER_RT_UNITTEST_LINK_FLAGS}) +list(APPEND GWP_ASAN_UNITTEST_LINK_FLAGS --driver-mode=g++) +if(NOT WIN32) +  list(APPEND GWP_ASAN_UNITTEST_LINK_FLAGS -lpthread) +endif() + +if(COMPILER_RT_DEFAULT_TARGET_ARCH IN_LIST GWP_ASAN_SUPPORTED_ARCH) +  # GWP-ASan unit tests are only run on the host machine. +  set(arch ${COMPILER_RT_DEFAULT_TARGET_ARCH}) + +  set(GWP_ASAN_TEST_RUNTIME RTGwpAsanTest.${arch}) + +  set(GWP_ASAN_TEST_RUNTIME_OBJECTS +    $<TARGET_OBJECTS:RTGwpAsan.${arch}>) + +  add_library(${GWP_ASAN_TEST_RUNTIME} STATIC +    ${GWP_ASAN_TEST_RUNTIME_OBJECTS}) + +  set_target_properties(${GWP_ASAN_TEST_RUNTIME} PROPERTIES +    ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +    FOLDER "Compiler-RT Runtime tests") + +  set(GwpAsanTestObjects) +  generate_compiler_rt_tests(GwpAsanTestObjects +    GwpAsanUnitTests "GwpAsan-${arch}-Test" ${arch} +    SOURCES ${GWP_ASAN_UNITTESTS} ${COMPILER_RT_GTEST_SOURCE} +    RUNTIME ${GWP_ASAN_TEST_RUNTIME} +    DEPS gtest ${GWP_ASAN_UNIT_TEST_HEADERS} +    CFLAGS ${GWP_ASAN_UNITTEST_CFLAGS} +    LINK_FLAGS ${GWP_ASAN_UNITTEST_LINK_FLAGS}) +  set_target_properties(GwpAsanUnitTests PROPERTIES +    RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) +endif() diff --git a/compiler-rt/lib/gwp_asan/tests/driver.cpp b/compiler-rt/lib/gwp_asan/tests/driver.cpp new file mode 100644 index 00000000000..b402cec1126 --- /dev/null +++ b/compiler-rt/lib/gwp_asan/tests/driver.cpp @@ -0,0 +1,14 @@ +//===-- driver.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 "gtest/gtest.h" + +int main(int argc, char **argv) { +  testing::InitGoogleTest(&argc, argv); +  return RUN_ALL_TESTS(); +} diff --git a/compiler-rt/lib/gwp_asan/tests/mutex_test.cpp b/compiler-rt/lib/gwp_asan/tests/mutex_test.cpp new file mode 100644 index 00000000000..36f7e1d2323 --- /dev/null +++ b/compiler-rt/lib/gwp_asan/tests/mutex_test.cpp @@ -0,0 +1,89 @@ +//===-- mutex_test.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/mutex.h" +#include "gtest/gtest.h" + +#include <atomic> +#include <mutex> +#include <thread> +#include <vector> + +using gwp_asan::Mutex; +using gwp_asan::ScopedLock; + +TEST(GwpAsanMutexTest, LockUnlockTest) { +  Mutex Mu; + +  ASSERT_TRUE(Mu.tryLock()); +  ASSERT_FALSE(Mu.tryLock()); +  Mu.unlock(); + +  Mu.lock(); +  Mu.unlock(); + +  // Ensure that the mutex actually unlocked. +  ASSERT_TRUE(Mu.tryLock()); +  Mu.unlock(); +} + +TEST(GwpAsanMutexTest, ScopedLockUnlockTest) { +  Mutex Mu; +  { ScopedLock L(Mu); } +  // Locking will fail here if the scoped lock failed to unlock. +  EXPECT_TRUE(Mu.tryLock()); +  Mu.unlock(); + +  { +    ScopedLock L(Mu); +    EXPECT_FALSE(Mu.tryLock()); // Check that the c'tor did lock. + +    // Manually unlock and check that this succeeds. +    Mu.unlock(); +    EXPECT_TRUE(Mu.tryLock()); // Manually lock. +  } +  EXPECT_TRUE(Mu.tryLock()); // Assert that the scoped destructor did unlock. +  Mu.unlock(); +} + +static void synchronousIncrementTask(std::atomic<bool> *StartingGun, Mutex *Mu, +                                     unsigned *Counter, +                                     unsigned NumIterations) { +  while (!StartingGun) { +    // Wait for starting gun. +  } +  for (unsigned i = 0; i < NumIterations; ++i) { +    ScopedLock L(*Mu); +    (*Counter)++; +  } +} + +static void runSynchronisedTest(unsigned NumThreads, unsigned CounterMax) { +  std::vector<std::thread> Threads; + +  ASSERT_TRUE(CounterMax % NumThreads == 0); + +  std::atomic<bool> StartingGun{false}; +  Mutex Mu; +  unsigned Counter = 0; + +  for (unsigned i = 0; i < NumThreads; ++i) +    Threads.emplace_back(synchronousIncrementTask, &StartingGun, &Mu, &Counter, +                         CounterMax / NumThreads); + +  StartingGun = true; +  for (auto &T : Threads) +    T.join(); + +  EXPECT_EQ(CounterMax, Counter); +} + +TEST(GwpAsanMutexTest, SynchronisedCounterTest) { +  runSynchronisedTest(4, 100000); +  runSynchronisedTest(1000, 1000000); +} diff --git a/compiler-rt/test/gwp_asan/CMakeLists.txt b/compiler-rt/test/gwp_asan/CMakeLists.txt index e69de29bb2d..2782b9a0c11 100644 --- a/compiler-rt/test/gwp_asan/CMakeLists.txt +++ b/compiler-rt/test/gwp_asan/CMakeLists.txt @@ -0,0 +1,45 @@ +set(GWP_ASAN_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(GWP_ASAN_LIT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) + +set(GWP_ASAN_TESTSUITES) + +set(GWP_ASAN_UNITTEST_DEPS) +set(GWP_ASAN_TEST_DEPS +  ${SANITIZER_COMMON_LIT_TEST_DEPS} +  gwp_asan) + +if (COMPILER_RT_INCLUDE_TESTS) +  list(APPEND GWP_ASAN_TEST_DEPS GwpAsanUnitTests) +  configure_lit_site_cfg( +    ${CMAKE_CURRENT_SOURCE_DIR}/unit/lit.site.cfg.in +    ${CMAKE_CURRENT_BINARY_DIR}/unit/lit.site.cfg) +  add_lit_testsuite(check-gwp_asan-unit "Running GWP-ASan unit tests" +    ${CMAKE_CURRENT_BINARY_DIR}/unit +    DEPENDS ${GWP_ASAN_TEST_DEPS}) +  set_target_properties(check-gwp_asan-unit PROPERTIES FOLDER +    "Compiler-RT Tests") +    list(APPEND GWP_ASAN_TEST_DEPS check-gwp_asan-unit) +endif() + +configure_lit_site_cfg( +  ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in +  ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg +  ) + +foreach(arch ${GWP_ASAN_SUPPORTED_ARCH}) +  set(GWP_ASAN_TEST_TARGET_ARCH ${arch}) +  string(TOLOWER "-${arch}" GWP_ASAN_TEST_CONFIG_SUFFIX) +  get_test_cc_for_arch(${arch} GWP_ASAN_TEST_TARGET_CC GWP_ASAN_TEST_TARGET_CFLAGS) +  string(TOUPPER ${arch} ARCH_UPPER_CASE) +  set(CONFIG_NAME ${ARCH_UPPER_CASE}${OS_NAME}Config) + +  configure_lit_site_cfg( +    ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in +    ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/lit.site.cfg) +  list(APPEND GWP_ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}) +endforeach() + +add_lit_testsuite(check-gwp_asan "Running the GWP-ASan tests" +  ${GWP_ASAN_TESTSUITES} +  DEPENDS ${GWP_ASAN_TEST_DEPS}) +set_target_properties(check-gwp_asan PROPERTIES FOLDER "Compiler-RT Misc") diff --git a/compiler-rt/test/gwp_asan/dummy_test.cc b/compiler-rt/test/gwp_asan/dummy_test.cc new file mode 100644 index 00000000000..93e522ab9f0 --- /dev/null +++ b/compiler-rt/test/gwp_asan/dummy_test.cc @@ -0,0 +1,4 @@ +// Exists to simply stop warnings about lit not discovering any tests here. +// RUN: %clang %s + +int main() { return 0; } diff --git a/compiler-rt/test/gwp_asan/lit.cfg b/compiler-rt/test/gwp_asan/lit.cfg new file mode 100644 index 00000000000..e8ef2925e79 --- /dev/null +++ b/compiler-rt/test/gwp_asan/lit.cfg @@ -0,0 +1,31 @@ +# -*- Python -*- + +import os + +# Setup config name. +config.name = 'GWP-ASan' + config.name_suffix + +# Setup source root. +config.test_source_root = os.path.dirname(__file__) + +# Test suffixes. +config.suffixes = ['.c', '.cc', '.cpp', '.test'] + +# C & CXX flags. +c_flags = ([config.target_cflags]) + +# Android doesn't want -lrt. +if not config.android: +  c_flags += ["-lrt"] + +cxx_flags = (c_flags + config.cxx_mode_flags + ["-std=c++11"]) + +def build_invocation(compile_flags): +  return " " + " ".join([config.clang] + compile_flags) + " " + +# Add substitutions. +config.substitutions.append(("%clang ", build_invocation(c_flags))) + +# GWP-ASan tests are currently supported on Linux only. +if config.host_os not in ['Linux']: +   config.unsupported = True diff --git a/compiler-rt/test/gwp_asan/lit.site.cfg.in b/compiler-rt/test/gwp_asan/lit.site.cfg.in new file mode 100644 index 00000000000..1fb1c1955f6 --- /dev/null +++ b/compiler-rt/test/gwp_asan/lit.site.cfg.in @@ -0,0 +1,11 @@ +@LIT_SITE_CFG_IN_HEADER@ + +config.name_suffix = "@GWP_ASAN_TEST_CONFIG_SUFFIX@" +config.target_arch = "@GWP_ASAN_TEST_TARGET_ARCH@" +config.target_cflags = "@GWP_ASAN_TEST_TARGET_CFLAGS@" + +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured") + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@GWP_ASAN_LIT_SOURCE_DIR@/lit.cfg") diff --git a/compiler-rt/test/gwp_asan/unit/lit.site.cfg.in b/compiler-rt/test/gwp_asan/unit/lit.site.cfg.in new file mode 100644 index 00000000000..9378004b8dc --- /dev/null +++ b/compiler-rt/test/gwp_asan/unit/lit.site.cfg.in @@ -0,0 +1,9 @@ +@LIT_SITE_CFG_IN_HEADER@ + +config.name = "GwpAsan-Unittest" +# Load common config for all compiler-rt unit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/unittests/lit.common.unit.configured") + +config.test_exec_root = os.path.join("@COMPILER_RT_BINARY_DIR@", +                                     "lib", "gwp_asan", "tests") +config.test_source_root = config.test_exec_root  | 

