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 | 23c6936b7467f0f703c7100629d6fda3b3b5b501 (patch) | |
tree | 917a167769bd18ebe108c21a4ca615f71e19a433 /lib/gwp_asan | |
parent | d798ef3964f20a6b76e513637c965ddeb8f7b3a0 (diff) | |
download | compiler-rt-23c6936b7467f0f703c7100629d6fda3b3b5b501.tar.gz |
[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
git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@362138 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/gwp_asan')
-rw-r--r-- | lib/gwp_asan/CMakeLists.txt | 8 | ||||
-rw-r--r-- | lib/gwp_asan/mutex.h | 50 | ||||
-rw-r--r-- | lib/gwp_asan/platform_specific/mutex_posix.cpp | 30 | ||||
-rw-r--r-- | lib/gwp_asan/tests/CMakeLists.txt | 49 | ||||
-rw-r--r-- | lib/gwp_asan/tests/driver.cpp | 14 | ||||
-rw-r--r-- | lib/gwp_asan/tests/mutex_test.cpp | 89 |
6 files changed, 239 insertions, 1 deletions
diff --git a/lib/gwp_asan/CMakeLists.txt b/lib/gwp_asan/CMakeLists.txt index 1f6d973b3..6c83d86c6 100644 --- a/lib/gwp_asan/CMakeLists.txt +++ b/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/lib/gwp_asan/mutex.h b/lib/gwp_asan/mutex.h new file mode 100644 index 000000000..c29df4cde --- /dev/null +++ b/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/lib/gwp_asan/platform_specific/mutex_posix.cpp b/lib/gwp_asan/platform_specific/mutex_posix.cpp new file mode 100644 index 000000000..e15bca882 --- /dev/null +++ b/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/lib/gwp_asan/tests/CMakeLists.txt b/lib/gwp_asan/tests/CMakeLists.txt new file mode 100644 index 000000000..6a59be5bc --- /dev/null +++ b/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/lib/gwp_asan/tests/driver.cpp b/lib/gwp_asan/tests/driver.cpp new file mode 100644 index 000000000..b402cec11 --- /dev/null +++ b/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/lib/gwp_asan/tests/mutex_test.cpp b/lib/gwp_asan/tests/mutex_test.cpp new file mode 100644 index 000000000..36f7e1d23 --- /dev/null +++ b/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); +} |