diff options
Diffstat (limited to 'compiler-rt/lib/gwp_asan/tests')
| -rw-r--r-- | compiler-rt/lib/gwp_asan/tests/CMakeLists.txt | 8 | ||||
| -rw-r--r-- | compiler-rt/lib/gwp_asan/tests/alignment.cpp | 27 | ||||
| -rw-r--r-- | compiler-rt/lib/gwp_asan/tests/basic.cpp | 60 | ||||
| -rw-r--r-- | compiler-rt/lib/gwp_asan/tests/harness.h | 60 | ||||
| -rw-r--r-- | compiler-rt/lib/gwp_asan/tests/slot_reuse.cpp | 72 | ||||
| -rw-r--r-- | compiler-rt/lib/gwp_asan/tests/thread_contention.cpp | 69 |
6 files changed, 294 insertions, 2 deletions
diff --git a/compiler-rt/lib/gwp_asan/tests/CMakeLists.txt b/compiler-rt/lib/gwp_asan/tests/CMakeLists.txt index 6a59be5bca6..f2f72c85861 100644 --- a/compiler-rt/lib/gwp_asan/tests/CMakeLists.txt +++ b/compiler-rt/lib/gwp_asan/tests/CMakeLists.txt @@ -9,7 +9,8 @@ set(GWP_ASAN_UNITTEST_CFLAGS file(GLOB GWP_ASAN_HEADERS ../*.h) file(GLOB GWP_ASAN_UNITTESTS *.cpp) set(GWP_ASAN_UNIT_TEST_HEADERS - ${GWP_ASAN_HEADERS}) + ${GWP_ASAN_HEADERS} + harness.h) add_custom_target(GwpAsanUnitTests) set_target_properties(GwpAsanUnitTests PROPERTIES FOLDER "Compiler-RT Tests") @@ -26,8 +27,11 @@ if(COMPILER_RT_DEFAULT_TARGET_ARCH IN_LIST GWP_ASAN_SUPPORTED_ARCH) set(GWP_ASAN_TEST_RUNTIME RTGwpAsanTest.${arch}) + # RTSanitizerCommonNoTermination(NoLibc) required for __sanitizer::Printf. set(GWP_ASAN_TEST_RUNTIME_OBJECTS - $<TARGET_OBJECTS:RTGwpAsan.${arch}>) + $<TARGET_OBJECTS:RTGwpAsan.${arch}> + $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> + $<TARGET_OBJECTS:RTSanitizerCommonNoLibc.${arch}>) add_library(${GWP_ASAN_TEST_RUNTIME} STATIC ${GWP_ASAN_TEST_RUNTIME_OBJECTS}) diff --git a/compiler-rt/lib/gwp_asan/tests/alignment.cpp b/compiler-rt/lib/gwp_asan/tests/alignment.cpp new file mode 100644 index 00000000000..ffb91d5b57a --- /dev/null +++ b/compiler-rt/lib/gwp_asan/tests/alignment.cpp @@ -0,0 +1,27 @@ +//===-- alignment.cc --------------------------------------------*- 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" + +TEST_F(DefaultGuardedPoolAllocator, BasicAllocation) { + std::vector<std::pair<int, int>> AllocSizeToAlignment = { + {1, 1}, {2, 2}, {3, 4}, {4, 4}, {5, 8}, {7, 8}, + {8, 8}, {9, 16}, {15, 16}, {16, 16}, {17, 16}, {31, 16}, + {32, 16}, {33, 16}, {4095, 4096}, {4096, 4096}, + }; + + for (const auto &KV : AllocSizeToAlignment) { + void *Ptr = GPA.allocate(KV.first); + EXPECT_NE(nullptr, Ptr); + + // Check the alignment of the pointer is as expected. + EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(Ptr) % KV.second); + + GPA.deallocate(Ptr); + } +} diff --git a/compiler-rt/lib/gwp_asan/tests/basic.cpp b/compiler-rt/lib/gwp_asan/tests/basic.cpp new file mode 100644 index 00000000000..9c80e276510 --- /dev/null +++ b/compiler-rt/lib/gwp_asan/tests/basic.cpp @@ -0,0 +1,60 @@ +//===-- basic.cc ------------------------------------------------*- 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" + +TEST_F(CustomGuardedPoolAllocator, BasicAllocation) { + InitNumSlots(1); + void *Ptr = GPA.allocate(1); + EXPECT_NE(nullptr, Ptr); + EXPECT_TRUE(GPA.pointerIsMine(Ptr)); + EXPECT_EQ(1u, GPA.getSize(Ptr)); + GPA.deallocate(Ptr); +} + +TEST_F(DefaultGuardedPoolAllocator, NullptrIsNotMine) { + EXPECT_FALSE(GPA.pointerIsMine(nullptr)); +} + +TEST_F(CustomGuardedPoolAllocator, SizedAllocations) { + InitNumSlots(1); + + std::size_t MaxAllocSize = GPA.maximumAllocationSize(); + EXPECT_TRUE(MaxAllocSize > 0); + + for (unsigned AllocSize = 1; AllocSize <= MaxAllocSize; AllocSize <<= 1) { + void *Ptr = GPA.allocate(AllocSize); + EXPECT_NE(nullptr, Ptr); + EXPECT_TRUE(GPA.pointerIsMine(Ptr)); + EXPECT_EQ(AllocSize, GPA.getSize(Ptr)); + GPA.deallocate(Ptr); + } +} + +TEST_F(DefaultGuardedPoolAllocator, TooLargeAllocation) { + EXPECT_EQ(nullptr, GPA.allocate(GPA.maximumAllocationSize() + 1)); +} + +TEST_F(CustomGuardedPoolAllocator, AllocAllSlots) { + constexpr unsigned kNumSlots = 128; + InitNumSlots(kNumSlots); + void *Ptrs[kNumSlots]; + for (unsigned i = 0; i < kNumSlots; ++i) { + Ptrs[i] = GPA.allocate(1); + EXPECT_NE(nullptr, Ptrs[i]); + EXPECT_TRUE(GPA.pointerIsMine(Ptrs[i])); + } + + // This allocation should fail as all the slots are used. + void *Ptr = GPA.allocate(1); + EXPECT_EQ(nullptr, Ptr); + EXPECT_FALSE(GPA.pointerIsMine(nullptr)); + + for (unsigned i = 0; i < kNumSlots; ++i) + GPA.deallocate(Ptrs[i]); +} diff --git a/compiler-rt/lib/gwp_asan/tests/harness.h b/compiler-rt/lib/gwp_asan/tests/harness.h new file mode 100644 index 00000000000..987564dd9af --- /dev/null +++ b/compiler-rt/lib/gwp_asan/tests/harness.h @@ -0,0 +1,60 @@ +//===-- harness.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_TESTS_HARNESS_H_ +#define GWP_ASAN_TESTS_HARNESS_H_ + +#include "gtest/gtest.h" + +// Include sanitizer_common first as gwp_asan/guarded_pool_allocator.h +// transiently includes definitions.h, which overwrites some of the definitions +// in sanitizer_common. +#include "sanitizer_common/sanitizer_common.h" + +#include "gwp_asan/guarded_pool_allocator.h" +#include "gwp_asan/options.h" + +class DefaultGuardedPoolAllocator : public ::testing::Test { +public: + DefaultGuardedPoolAllocator() { + gwp_asan::options::Options Opts; + Opts.setDefaults(); + MaxSimultaneousAllocations = Opts.MaxSimultaneousAllocations; + + Opts.Printf = __sanitizer::Printf; + GPA.init(Opts); + } + +protected: + gwp_asan::GuardedPoolAllocator GPA; + decltype(gwp_asan::options::Options::MaxSimultaneousAllocations) + MaxSimultaneousAllocations; +}; + +class CustomGuardedPoolAllocator : public ::testing::Test { +public: + void + InitNumSlots(decltype(gwp_asan::options::Options::MaxSimultaneousAllocations) + MaxSimultaneousAllocationsArg) { + gwp_asan::options::Options Opts; + Opts.setDefaults(); + + Opts.MaxSimultaneousAllocations = MaxSimultaneousAllocationsArg; + MaxSimultaneousAllocations = MaxSimultaneousAllocationsArg; + + Opts.Printf = __sanitizer::Printf; + GPA.init(Opts); + } + +protected: + gwp_asan::GuardedPoolAllocator GPA; + decltype(gwp_asan::options::Options::MaxSimultaneousAllocations) + MaxSimultaneousAllocations; +}; + +#endif // GWP_ASAN_TESTS_HARNESS_H_ diff --git a/compiler-rt/lib/gwp_asan/tests/slot_reuse.cpp b/compiler-rt/lib/gwp_asan/tests/slot_reuse.cpp new file mode 100644 index 00000000000..e2437390231 --- /dev/null +++ b/compiler-rt/lib/gwp_asan/tests/slot_reuse.cpp @@ -0,0 +1,72 @@ +//===-- slot_reuse.cc -------------------------------------------*- 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" + +void singleByteGoodAllocDealloc(gwp_asan::GuardedPoolAllocator *GPA) { + void *Ptr = GPA->allocate(1); + EXPECT_NE(nullptr, Ptr); + EXPECT_TRUE(GPA->pointerIsMine(Ptr)); + EXPECT_EQ(1u, GPA->getSize(Ptr)); + GPA->deallocate(Ptr); +} + +TEST_F(CustomGuardedPoolAllocator, EnsureReuseOfQuarantine1) { + InitNumSlots(1); + for (unsigned i = 0; i < 128; ++i) + singleByteGoodAllocDealloc(&GPA); +} + +TEST_F(CustomGuardedPoolAllocator, EnsureReuseOfQuarantine2) { + InitNumSlots(2); + for (unsigned i = 0; i < 128; ++i) + singleByteGoodAllocDealloc(&GPA); +} + +TEST_F(CustomGuardedPoolAllocator, EnsureReuseOfQuarantine127) { + InitNumSlots(127); + for (unsigned i = 0; i < 128; ++i) + singleByteGoodAllocDealloc(&GPA); +} + +// This test ensures that our slots are not reused ahead of time. We increase +// the use-after-free detection by not reusing slots until all of them have been +// allocated. This is done by always using the slots from left-to-right in the +// pool before we used each slot once, at which point random selection takes +// over. +void runNoReuseBeforeNecessary(gwp_asan::GuardedPoolAllocator *GPA, + unsigned PoolSize) { + std::set<void *> Ptrs; + for (unsigned i = 0; i < PoolSize; ++i) { + void *Ptr = GPA->allocate(1); + + EXPECT_TRUE(GPA->pointerIsMine(Ptr)); + EXPECT_EQ(0u, Ptrs.count(Ptr)); + + Ptrs.insert(Ptr); + GPA->deallocate(Ptr); + } +} + +TEST_F(CustomGuardedPoolAllocator, NoReuseBeforeNecessary2) { + constexpr unsigned kPoolSize = 2; + InitNumSlots(kPoolSize); + runNoReuseBeforeNecessary(&GPA, kPoolSize); +} + +TEST_F(CustomGuardedPoolAllocator, NoReuseBeforeNecessary128) { + constexpr unsigned kPoolSize = 128; + InitNumSlots(kPoolSize); + runNoReuseBeforeNecessary(&GPA, kPoolSize); +} + +TEST_F(CustomGuardedPoolAllocator, NoReuseBeforeNecessary129) { + constexpr unsigned kPoolSize = 129; + InitNumSlots(kPoolSize); + runNoReuseBeforeNecessary(&GPA, kPoolSize); +} diff --git a/compiler-rt/lib/gwp_asan/tests/thread_contention.cpp b/compiler-rt/lib/gwp_asan/tests/thread_contention.cpp new file mode 100644 index 00000000000..1c00f4413dd --- /dev/null +++ b/compiler-rt/lib/gwp_asan/tests/thread_contention.cpp @@ -0,0 +1,69 @@ +//===-- thread_contention.cc ------------------------------------*- 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" + +// Note: Compilation of <atomic> and <thread> are extremely expensive for +// non-opt builds of clang. +#include <atomic> +#include <cstdlib> +#include <thread> +#include <vector> + +void asyncTask(gwp_asan::GuardedPoolAllocator *GPA, + std::atomic<bool> *StartingGun, unsigned NumIterations) { + while (!*StartingGun) { + // Wait for starting gun. + } + + // Get ourselves a new allocation. + for (unsigned i = 0; i < NumIterations; ++i) { + volatile char *Ptr = reinterpret_cast<volatile char *>( + GPA->allocate(GPA->maximumAllocationSize())); + // Do any other threads have access to this page? + EXPECT_EQ(*Ptr, 0); + + // Mark the page as from malloc. Wait to see if another thread also takes + // this page. + *Ptr = 'A'; + std::this_thread::sleep_for(std::chrono::nanoseconds(10000)); + + // Check we still own the page. + EXPECT_EQ(*Ptr, 'A'); + + // And now release it. + *Ptr = 0; + GPA->deallocate(const_cast<char *>(Ptr)); + } +} + +void runThreadContentionTest(unsigned NumThreads, unsigned NumIterations, + gwp_asan::GuardedPoolAllocator *GPA) { + + std::atomic<bool> StartingGun{false}; + std::vector<std::thread> Threads; + if (std::thread::hardware_concurrency() < NumThreads) { + NumThreads = std::thread::hardware_concurrency(); + } + + for (unsigned i = 0; i < NumThreads; ++i) { + Threads.emplace_back(asyncTask, GPA, &StartingGun, NumIterations); + } + + StartingGun = true; + + for (auto &T : Threads) + T.join(); +} + +TEST_F(CustomGuardedPoolAllocator, ThreadContention) { + unsigned NumThreads = 4; + unsigned NumIterations = 10000; + InitNumSlots(NumThreads); + runThreadContentionTest(NumThreads, NumIterations, &GPA); +} |

