diff options
author | Robert Nagy <robert@cvs.openbsd.org> | 2024-01-26 11:27:40 +0000 |
---|---|---|
committer | Robert Nagy <robert@cvs.openbsd.org> | 2024-01-26 11:27:40 +0000 |
commit | ab5fedb801b84474ed4ac3b8ea9300110083617f (patch) | |
tree | b3b15643c8486c85143204edf061589c3a27d74a /gnu/llvm/compiler-rt/lib/gwp_asan/tests | |
parent | ac86ae3fbb62d1b532f4d9950de889d680c751df (diff) |
import llvm compiler-rt 16.0.6
Diffstat (limited to 'gnu/llvm/compiler-rt/lib/gwp_asan/tests')
8 files changed, 374 insertions, 94 deletions
diff --git a/gnu/llvm/compiler-rt/lib/gwp_asan/tests/CMakeLists.txt b/gnu/llvm/compiler-rt/lib/gwp_asan/tests/CMakeLists.txt index abc02a49637..046ca7ce679 100644 --- a/gnu/llvm/compiler-rt/lib/gwp_asan/tests/CMakeLists.txt +++ b/gnu/llvm/compiler-rt/lib/gwp_asan/tests/CMakeLists.txt @@ -3,6 +3,8 @@ include(CompilerRTCompile) set(GWP_ASAN_UNITTEST_CFLAGS ${COMPILER_RT_UNITTEST_CFLAGS} ${COMPILER_RT_GTEST_CFLAGS} + ${SANITIZER_TEST_CXX_CFLAGS} + -std=c++17 -I${COMPILER_RT_SOURCE_DIR}/lib/ -O2 -g @@ -24,7 +26,8 @@ set(GWP_ASAN_UNITTESTS harness.cpp enable_disable.cpp late_init.cpp - options.cpp) + options.cpp + recoverable.cpp) set(GWP_ASAN_UNIT_TEST_HEADERS ${GWP_ASAN_HEADERS} @@ -33,7 +36,10 @@ set(GWP_ASAN_UNIT_TEST_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} -ldl) +set(GWP_ASAN_UNITTEST_LINK_FLAGS + ${COMPILER_RT_UNITTEST_LINK_FLAGS} -ldl + ${COMPILER_RT_UNWINDER_LINK_LIBS} + ${SANITIZER_TEST_CXX_LIBRARIES}) list(APPEND GWP_ASAN_UNITTEST_LINK_FLAGS --driver-mode=g++) if(NOT WIN32) list(APPEND GWP_ASAN_UNITTEST_LINK_FLAGS -pthread) @@ -66,7 +72,7 @@ if(COMPILER_RT_DEFAULT_TARGET_ARCH IN_LIST GWP_ASAN_SUPPORTED_ARCH) 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} + DEPS llvm_gtest ${GWP_ASAN_UNIT_TEST_HEADERS} CFLAGS ${GWP_ASAN_UNITTEST_CFLAGS} LINK_FLAGS ${GWP_ASAN_UNITTEST_LINK_FLAGS}) set_target_properties(GwpAsanUnitTests PROPERTIES diff --git a/gnu/llvm/compiler-rt/lib/gwp_asan/tests/alignment.cpp b/gnu/llvm/compiler-rt/lib/gwp_asan/tests/alignment.cpp index 5f24a9a1bd8..9f150467c79 100644 --- a/gnu/llvm/compiler-rt/lib/gwp_asan/tests/alignment.cpp +++ b/gnu/llvm/compiler-rt/lib/gwp_asan/tests/alignment.cpp @@ -34,81 +34,81 @@ public: // numerics of the testing. TEST(AlignmentTest, LeftAlignedAllocs) { // Alignment < Page Size. - EXPECT_EQ(0x4000, AlignmentTestGPA::alignUp( - /* Ptr */ 0x4000, /* Alignment */ 0x1)); + EXPECT_EQ(0x4000u, AlignmentTestGPA::alignUp( + /* Ptr */ 0x4000, /* Alignment */ 0x1)); // Alignment == Page Size. - EXPECT_EQ(0x4000, AlignmentTestGPA::alignUp( - /* Ptr */ 0x4000, /* Alignment */ 0x1000)); + EXPECT_EQ(0x4000u, AlignmentTestGPA::alignUp( + /* Ptr */ 0x4000, /* Alignment */ 0x1000)); // Alignment > Page Size. - EXPECT_EQ(0x4000, AlignmentTestGPA::alignUp( - /* Ptr */ 0x4000, /* Alignment */ 0x4000)); + EXPECT_EQ(0x4000u, AlignmentTestGPA::alignUp( + /* Ptr */ 0x4000, /* Alignment */ 0x4000)); } TEST(AlignmentTest, SingleByteAllocs) { // Alignment < Page Size. - EXPECT_EQ(0x1, + EXPECT_EQ(0x1u, AlignmentTestGPA::getRequiredBackingSize( /* Size */ 0x1, /* Alignment */ 0x1, /* PageSize */ 0x1000)); - EXPECT_EQ(0x7fff, AlignmentTestGPA::alignDown( - /* Ptr */ 0x8000 - 0x1, /* Alignment */ 0x1)); + EXPECT_EQ(0x7fffu, AlignmentTestGPA::alignDown( + /* Ptr */ 0x8000 - 0x1, /* Alignment */ 0x1)); // Alignment == Page Size. - EXPECT_EQ(0x1, + EXPECT_EQ(0x1u, AlignmentTestGPA::getRequiredBackingSize( /* Size */ 0x1, /* Alignment */ 0x1000, /* PageSize */ 0x1000)); - EXPECT_EQ(0x7000, AlignmentTestGPA::alignDown( - /* Ptr */ 0x8000 - 0x1, /* Alignment */ 0x1000)); + EXPECT_EQ(0x7000u, AlignmentTestGPA::alignDown( + /* Ptr */ 0x8000 - 0x1, /* Alignment */ 0x1000)); // Alignment > Page Size. - EXPECT_EQ(0x3001, + EXPECT_EQ(0x3001u, AlignmentTestGPA::getRequiredBackingSize( /* Size */ 0x1, /* Alignment */ 0x4000, /* PageSize */ 0x1000)); - EXPECT_EQ(0x4000, AlignmentTestGPA::alignDown( - /* Ptr */ 0x8000 - 0x1, /* Alignment */ 0x4000)); + EXPECT_EQ(0x4000u, AlignmentTestGPA::alignDown( + /* Ptr */ 0x8000 - 0x1, /* Alignment */ 0x4000)); } TEST(AlignmentTest, PageSizedAllocs) { // Alignment < Page Size. - EXPECT_EQ(0x1000, + EXPECT_EQ(0x1000u, AlignmentTestGPA::getRequiredBackingSize( /* Size */ 0x1000, /* Alignment */ 0x1, /* PageSize */ 0x1000)); - EXPECT_EQ(0x7000, AlignmentTestGPA::alignDown( - /* Ptr */ 0x8000 - 0x1000, /* Alignment */ 0x1)); + EXPECT_EQ(0x7000u, AlignmentTestGPA::alignDown( + /* Ptr */ 0x8000 - 0x1000, /* Alignment */ 0x1)); // Alignment == Page Size. - EXPECT_EQ(0x1000, AlignmentTestGPA::getRequiredBackingSize( - /* Size */ 0x1000, /* Alignment */ 0x1000, - /* PageSize */ 0x1000)); - EXPECT_EQ(0x7000, AlignmentTestGPA::alignDown( - /* Ptr */ 0x8000 - 0x1000, /* Alignment */ 0x1000)); + EXPECT_EQ(0x1000u, AlignmentTestGPA::getRequiredBackingSize( + /* Size */ 0x1000, /* Alignment */ 0x1000, + /* PageSize */ 0x1000)); + EXPECT_EQ(0x7000u, AlignmentTestGPA::alignDown( + /* Ptr */ 0x8000 - 0x1000, /* Alignment */ 0x1000)); // Alignment > Page Size. - EXPECT_EQ(0x4000, AlignmentTestGPA::getRequiredBackingSize( - /* Size */ 0x1000, /* Alignment */ 0x4000, - /* PageSize */ 0x1000)); - EXPECT_EQ(0x4000, AlignmentTestGPA::alignDown( - /* Ptr */ 0x8000 - 0x1000, /* Alignment */ 0x4000)); + EXPECT_EQ(0x4000u, AlignmentTestGPA::getRequiredBackingSize( + /* Size */ 0x1000, /* Alignment */ 0x4000, + /* PageSize */ 0x1000)); + EXPECT_EQ(0x4000u, AlignmentTestGPA::alignDown( + /* Ptr */ 0x8000 - 0x1000, /* Alignment */ 0x4000)); } TEST(AlignmentTest, MoreThanPageAllocs) { // Alignment < Page Size. - EXPECT_EQ(0x2fff, + EXPECT_EQ(0x2fffu, AlignmentTestGPA::getRequiredBackingSize( /* Size */ 0x2fff, /* Alignment */ 0x1, /* PageSize */ 0x1000)); - EXPECT_EQ(0x5001, AlignmentTestGPA::alignDown( - /* Ptr */ 0x8000 - 0x2fff, /* Alignment */ 0x1)); + EXPECT_EQ(0x5001u, AlignmentTestGPA::alignDown( + /* Ptr */ 0x8000 - 0x2fff, /* Alignment */ 0x1)); // Alignment == Page Size. - EXPECT_EQ(0x2fff, AlignmentTestGPA::getRequiredBackingSize( - /* Size */ 0x2fff, /* Alignment */ 0x1000, - /* PageSize */ 0x1000)); - EXPECT_EQ(0x5000, AlignmentTestGPA::alignDown( - /* Ptr */ 0x8000 - 0x2fff, /* Alignment */ 0x1000)); + EXPECT_EQ(0x2fffu, AlignmentTestGPA::getRequiredBackingSize( + /* Size */ 0x2fff, /* Alignment */ 0x1000, + /* PageSize */ 0x1000)); + EXPECT_EQ(0x5000u, AlignmentTestGPA::alignDown( + /* Ptr */ 0x8000 - 0x2fff, /* Alignment */ 0x1000)); // Alignment > Page Size. - EXPECT_EQ(0x5fff, AlignmentTestGPA::getRequiredBackingSize( - /* Size */ 0x2fff, /* Alignment */ 0x4000, - /* PageSize */ 0x1000)); - EXPECT_EQ(0x4000, AlignmentTestGPA::alignDown( - /* Ptr */ 0x8000 - 0x2fff, /* Alignment */ 0x4000)); + EXPECT_EQ(0x5fffu, AlignmentTestGPA::getRequiredBackingSize( + /* Size */ 0x2fff, /* Alignment */ 0x4000, + /* PageSize */ 0x1000)); + EXPECT_EQ(0x4000u, AlignmentTestGPA::alignDown( + /* Ptr */ 0x8000 - 0x2fff, /* Alignment */ 0x4000)); } diff --git a/gnu/llvm/compiler-rt/lib/gwp_asan/tests/backtrace.cpp b/gnu/llvm/compiler-rt/lib/gwp_asan/tests/backtrace.cpp index a4eb8eb9b21..e8789943962 100644 --- a/gnu/llvm/compiler-rt/lib/gwp_asan/tests/backtrace.cpp +++ b/gnu/llvm/compiler-rt/lib/gwp_asan/tests/backtrace.cpp @@ -6,46 +6,38 @@ // //===----------------------------------------------------------------------===// +#include <regex> #include <string> #include "gwp_asan/common.h" #include "gwp_asan/crash_handler.h" #include "gwp_asan/tests/harness.h" -// Optnone to ensure that the calls to these functions are not optimized away, -// as we're looking for them in the backtraces. -__attribute((optnone)) void * -AllocateMemory(gwp_asan::GuardedPoolAllocator &GPA) { - return GPA.allocate(1); -} -__attribute((optnone)) void -DeallocateMemory(gwp_asan::GuardedPoolAllocator &GPA, void *Ptr) { - GPA.deallocate(Ptr); -} -__attribute((optnone)) void -DeallocateMemory2(gwp_asan::GuardedPoolAllocator &GPA, void *Ptr) { - GPA.deallocate(Ptr); -} -__attribute__((optnone)) void TouchMemory(void *Ptr) { - *(reinterpret_cast<volatile char *>(Ptr)) = 7; -} - -TEST_F(BacktraceGuardedPoolAllocatorDeathTest, DoubleFree) { +TEST_P(BacktraceGuardedPoolAllocatorDeathTest, DoubleFree) { void *Ptr = AllocateMemory(GPA); DeallocateMemory(GPA, Ptr); - std::string DeathRegex = "Double Free.*"; - DeathRegex.append("DeallocateMemory2.*"); - - DeathRegex.append("was deallocated.*"); - DeathRegex.append("DeallocateMemory.*"); - - DeathRegex.append("was allocated.*"); - DeathRegex.append("AllocateMemory.*"); - ASSERT_DEATH(DeallocateMemory2(GPA, Ptr), DeathRegex); + std::string DeathRegex = "Double Free.*DeallocateMemory2.*"; + DeathRegex.append("was deallocated.*DeallocateMemory[^2].*"); + DeathRegex.append("was allocated.*AllocateMemory"); + if (!Recoverable) { + ASSERT_DEATH(DeallocateMemory2(GPA, Ptr), DeathRegex); + return; + } + + // For recoverable, assert that DeallocateMemory2() doesn't crash. + DeallocateMemory2(GPA, Ptr); + // Fuchsia's zxtest doesn't have an EXPECT_THAT(testing::MatchesRegex(), ...), + // so check the regex manually. + EXPECT_TRUE(std::regex_search( + GetOutputBuffer(), + std::basic_regex(DeathRegex, std::regex_constants::extended))) + << "Regex \"" << DeathRegex + << "\" was not found in input:\n============\n" + << GetOutputBuffer() << "\n============"; } -TEST_F(BacktraceGuardedPoolAllocatorDeathTest, UseAfterFree) { +TEST_P(BacktraceGuardedPoolAllocatorDeathTest, UseAfterFree) { #if defined(__linux__) && __ARM_ARCH == 7 // Incomplete backtrace on Armv7 Linux GTEST_SKIP(); @@ -54,17 +46,32 @@ TEST_F(BacktraceGuardedPoolAllocatorDeathTest, UseAfterFree) { void *Ptr = AllocateMemory(GPA); DeallocateMemory(GPA, Ptr); - std::string DeathRegex = "Use After Free.*"; - DeathRegex.append("TouchMemory.*"); - - DeathRegex.append("was deallocated.*"); - DeathRegex.append("DeallocateMemory.*"); - - DeathRegex.append("was allocated.*"); - DeathRegex.append("AllocateMemory.*"); - ASSERT_DEATH(TouchMemory(Ptr), DeathRegex); + std::string DeathRegex = "Use After Free.*TouchMemory.*"; + DeathRegex.append("was deallocated.*DeallocateMemory[^2].*"); + DeathRegex.append("was allocated.*AllocateMemory"); + + if (!Recoverable) { + ASSERT_DEATH(TouchMemory(Ptr), DeathRegex); + return; + } + + // For recoverable, assert that TouchMemory() doesn't crash. + TouchMemory(Ptr); + // Fuchsia's zxtest doesn't have an EXPECT_THAT(testing::MatchesRegex(), ...), + // so check the regex manually. + EXPECT_TRUE(std::regex_search( + GetOutputBuffer(), + std::basic_regex(DeathRegex, std::regex_constants::extended))) + << "Regex \"" << DeathRegex + << "\" was not found in input:\n============\n" + << GetOutputBuffer() << "\n============"; + ; } +INSTANTIATE_TEST_SUITE_P(RecoverableSignalDeathTest, + BacktraceGuardedPoolAllocatorDeathTest, + /* Recoverable */ testing::Bool()); + TEST(Backtrace, Short) { gwp_asan::AllocationMetadata Meta; Meta.AllocationTrace.RecordBacktrace( diff --git a/gnu/llvm/compiler-rt/lib/gwp_asan/tests/crash_handler_api.cpp b/gnu/llvm/compiler-rt/lib/gwp_asan/tests/crash_handler_api.cpp index 4cdb5694842..598b7b87892 100644 --- a/gnu/llvm/compiler-rt/lib/gwp_asan/tests/crash_handler_api.cpp +++ b/gnu/llvm/compiler-rt/lib/gwp_asan/tests/crash_handler_api.cpp @@ -40,7 +40,8 @@ protected: void setupState() { State.GuardedPagePool = 0x2000; - State.GuardedPagePoolEnd = 0xb000; + State.GuardedPagePoolEnd = 0xc000; + InternalFaultAddr = State.GuardedPagePoolEnd - 0x10; State.MaxSimultaneousAllocations = 4; // 0x3000, 0x5000, 0x7000, 0x9000. State.PageSize = 0x1000; } @@ -100,6 +101,7 @@ protected: static uintptr_t BacktraceConstants[kNumBacktraceConstants]; AllocatorState State = {}; AllocationMetadata Metadata[4] = {}; + uintptr_t InternalFaultAddr; }; uintptr_t CrashHandlerAPITest::BacktraceConstants[kNumBacktraceConstants] = { @@ -125,7 +127,7 @@ TEST_F(CrashHandlerAPITest, PointerNotAllocated) { EXPECT_TRUE(__gwp_asan_error_is_mine(&State, FailureAddress)); EXPECT_EQ(Error::UNKNOWN, __gwp_asan_diagnose_error(&State, Metadata, FailureAddress)); - EXPECT_EQ(0u, __gwp_asan_get_internal_crash_address(&State)); + EXPECT_EQ(0u, __gwp_asan_get_internal_crash_address(&State, FailureAddress)); EXPECT_EQ(nullptr, __gwp_asan_get_metadata(&State, Metadata, FailureAddress)); } @@ -140,7 +142,8 @@ TEST_F(CrashHandlerAPITest, DoubleFree) { EXPECT_TRUE(__gwp_asan_error_is_mine(&State)); EXPECT_EQ(Error::DOUBLE_FREE, __gwp_asan_diagnose_error(&State, Metadata, 0x0)); - EXPECT_EQ(FailureAddress, __gwp_asan_get_internal_crash_address(&State)); + EXPECT_EQ(FailureAddress, + __gwp_asan_get_internal_crash_address(&State, InternalFaultAddr)); checkMetadata(Index, FailureAddress); } @@ -155,7 +158,8 @@ TEST_F(CrashHandlerAPITest, InvalidFree) { EXPECT_TRUE(__gwp_asan_error_is_mine(&State)); EXPECT_EQ(Error::INVALID_FREE, __gwp_asan_diagnose_error(&State, Metadata, 0x0)); - EXPECT_EQ(FailureAddress, __gwp_asan_get_internal_crash_address(&State)); + EXPECT_EQ(FailureAddress, + __gwp_asan_get_internal_crash_address(&State, InternalFaultAddr)); checkMetadata(Index, FailureAddress); } @@ -168,7 +172,8 @@ TEST_F(CrashHandlerAPITest, InvalidFreeNoMetadata) { EXPECT_TRUE(__gwp_asan_error_is_mine(&State)); EXPECT_EQ(Error::INVALID_FREE, __gwp_asan_diagnose_error(&State, Metadata, 0x0)); - EXPECT_EQ(FailureAddress, __gwp_asan_get_internal_crash_address(&State)); + EXPECT_EQ(FailureAddress, + __gwp_asan_get_internal_crash_address(&State, InternalFaultAddr)); EXPECT_EQ(nullptr, __gwp_asan_get_metadata(&State, Metadata, FailureAddress)); } @@ -180,7 +185,7 @@ TEST_F(CrashHandlerAPITest, UseAfterFree) { EXPECT_TRUE(__gwp_asan_error_is_mine(&State, FailureAddress)); EXPECT_EQ(Error::USE_AFTER_FREE, __gwp_asan_diagnose_error(&State, Metadata, FailureAddress)); - EXPECT_EQ(0u, __gwp_asan_get_internal_crash_address(&State)); + EXPECT_EQ(0u, __gwp_asan_get_internal_crash_address(&State, FailureAddress)); checkMetadata(Index, FailureAddress); } @@ -192,7 +197,7 @@ TEST_F(CrashHandlerAPITest, BufferOverflow) { EXPECT_TRUE(__gwp_asan_error_is_mine(&State, FailureAddress)); EXPECT_EQ(Error::BUFFER_OVERFLOW, __gwp_asan_diagnose_error(&State, Metadata, FailureAddress)); - EXPECT_EQ(0u, __gwp_asan_get_internal_crash_address(&State)); + EXPECT_EQ(0u, __gwp_asan_get_internal_crash_address(&State, FailureAddress)); checkMetadata(Index, FailureAddress); } @@ -204,6 +209,6 @@ TEST_F(CrashHandlerAPITest, BufferUnderflow) { EXPECT_TRUE(__gwp_asan_error_is_mine(&State, FailureAddress)); EXPECT_EQ(Error::BUFFER_UNDERFLOW, __gwp_asan_diagnose_error(&State, Metadata, FailureAddress)); - EXPECT_EQ(0u, __gwp_asan_get_internal_crash_address(&State)); + EXPECT_EQ(0u, __gwp_asan_get_internal_crash_address(&State, FailureAddress)); checkMetadata(Index, FailureAddress); } diff --git a/gnu/llvm/compiler-rt/lib/gwp_asan/tests/harness.cpp b/gnu/llvm/compiler-rt/lib/gwp_asan/tests/harness.cpp index e668c73057f..ccad80ebdaa 100644 --- a/gnu/llvm/compiler-rt/lib/gwp_asan/tests/harness.cpp +++ b/gnu/llvm/compiler-rt/lib/gwp_asan/tests/harness.cpp @@ -16,3 +16,21 @@ bool OnlyOnce() { } } // namespace test } // namespace gwp_asan + +// Optnone to ensure that the calls to these functions are not optimized away, +// as we're looking for them in the backtraces. +__attribute__((optnone)) char * +AllocateMemory(gwp_asan::GuardedPoolAllocator &GPA) { + return static_cast<char *>(GPA.allocate(1)); +} +__attribute__((optnone)) void +DeallocateMemory(gwp_asan::GuardedPoolAllocator &GPA, void *Ptr) { + GPA.deallocate(Ptr); +} +__attribute__((optnone)) void +DeallocateMemory2(gwp_asan::GuardedPoolAllocator &GPA, void *Ptr) { + GPA.deallocate(Ptr); +} +__attribute__((optnone)) void TouchMemory(void *Ptr) { + *(reinterpret_cast<volatile char *>(Ptr)) = 7; +} diff --git a/gnu/llvm/compiler-rt/lib/gwp_asan/tests/harness.h b/gnu/llvm/compiler-rt/lib/gwp_asan/tests/harness.h index ed91e642de7..c8f643dbab9 100644 --- a/gnu/llvm/compiler-rt/lib/gwp_asan/tests/harness.h +++ b/gnu/llvm/compiler-rt/lib/gwp_asan/tests/harness.h @@ -14,9 +14,11 @@ #if defined(__Fuchsia__) #include <zxtest/zxtest.h> using Test = ::zxtest::Test; +template <typename T> using TestWithParam = ::zxtest::TestWithParam<T>; #else #include "gtest/gtest.h" using Test = ::testing::Test; +template <typename T> using TestWithParam = ::testing::TestWithParam<T>; #endif #include "gwp_asan/guarded_pool_allocator.h" @@ -39,6 +41,11 @@ bool OnlyOnce(); }; // namespace test }; // namespace gwp_asan +char *AllocateMemory(gwp_asan::GuardedPoolAllocator &GPA); +void DeallocateMemory(gwp_asan::GuardedPoolAllocator &GPA, void *Ptr); +void DeallocateMemory2(gwp_asan::GuardedPoolAllocator &GPA, void *Ptr); +void TouchMemory(void *Ptr); + class DefaultGuardedPoolAllocator : public Test { public: void SetUp() override { @@ -81,7 +88,8 @@ protected: MaxSimultaneousAllocations; }; -class BacktraceGuardedPoolAllocator : public Test { +class BacktraceGuardedPoolAllocator + : public TestWithParam</* Recoverable */ bool> { public: void SetUp() override { gwp_asan::options::Options Opts; @@ -91,10 +99,19 @@ public: Opts.InstallForkHandlers = gwp_asan::test::OnlyOnce(); GPA.init(Opts); + // In recoverable mode, capture GWP-ASan logs to an internal buffer so that + // we can search it in unit tests. For non-recoverable tests, the default + // buffer is fine, as any tests should be EXPECT_DEATH()'d. + Recoverable = GetParam(); + gwp_asan::Printf_t PrintfFunction = PrintfToBuffer; + GetOutputBuffer().clear(); + if (!Recoverable) + PrintfFunction = gwp_asan::test::getPrintfFunction(); + gwp_asan::segv_handler::installSignalHandlers( - &GPA, gwp_asan::test::getPrintfFunction(), - gwp_asan::backtrace::getPrintBacktraceFunction(), - gwp_asan::backtrace::getSegvBacktraceFunction()); + &GPA, PrintfFunction, gwp_asan::backtrace::getPrintBacktraceFunction(), + gwp_asan::backtrace::getSegvBacktraceFunction(), + /* Recoverable */ Recoverable); } void TearDown() override { @@ -103,7 +120,23 @@ public: } protected: + static std::string &GetOutputBuffer() { + static std::string Buffer; + return Buffer; + } + + __attribute__((format(printf, 1, 2))) static void + PrintfToBuffer(const char *Format, ...) { + va_list AP; + va_start(AP, Format); + char Buffer[8192]; + vsnprintf(Buffer, sizeof(Buffer), Format, AP); + GetOutputBuffer() += Buffer; + va_end(AP); + } + gwp_asan::GuardedPoolAllocator GPA; + bool Recoverable; }; // https://github.com/google/googletest/blob/master/docs/advanced.md#death-tests-and-threads diff --git a/gnu/llvm/compiler-rt/lib/gwp_asan/tests/iterate.cpp b/gnu/llvm/compiler-rt/lib/gwp_asan/tests/iterate.cpp index 2b8635d5b36..49953f33abf 100644 --- a/gnu/llvm/compiler-rt/lib/gwp_asan/tests/iterate.cpp +++ b/gnu/llvm/compiler-rt/lib/gwp_asan/tests/iterate.cpp @@ -8,6 +8,7 @@ #include "gwp_asan/tests/harness.h" +#include <algorithm> #include <set> #include <vector> diff --git a/gnu/llvm/compiler-rt/lib/gwp_asan/tests/recoverable.cpp b/gnu/llvm/compiler-rt/lib/gwp_asan/tests/recoverable.cpp new file mode 100644 index 00000000000..a4c5c3f5961 --- /dev/null +++ b/gnu/llvm/compiler-rt/lib/gwp_asan/tests/recoverable.cpp @@ -0,0 +1,210 @@ +//===-- recoverable.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 <atomic> +#include <mutex> +#include <regex> +#include <string> +#include <thread> +#include <vector> + +#include "gwp_asan/common.h" +#include "gwp_asan/crash_handler.h" +#include "gwp_asan/tests/harness.h" + +void CheckOnlyOneGwpAsanCrash(const std::string &OutputBuffer) { + const char *kGwpAsanErrorString = "GWP-ASan detected a memory error"; + size_t FirstIndex = OutputBuffer.find(kGwpAsanErrorString); + ASSERT_NE(FirstIndex, std::string::npos) << "Didn't detect a GWP-ASan crash"; + ASSERT_EQ(OutputBuffer.find(kGwpAsanErrorString, FirstIndex + 1), + std::string::npos) + << "Detected more than one GWP-ASan crash:\n" + << OutputBuffer; +} + +TEST_P(BacktraceGuardedPoolAllocator, MultipleDoubleFreeOnlyOneOutput) { + SCOPED_TRACE(""); + void *Ptr = AllocateMemory(GPA); + DeallocateMemory(GPA, Ptr); + // First time should generate a crash report. + DeallocateMemory(GPA, Ptr); + CheckOnlyOneGwpAsanCrash(GetOutputBuffer()); + ASSERT_NE(std::string::npos, GetOutputBuffer().find("Double Free")); + + // Ensure the crash is only reported once. + GetOutputBuffer().clear(); + for (size_t i = 0; i < 100; ++i) { + DeallocateMemory(GPA, Ptr); + ASSERT_TRUE(GetOutputBuffer().empty()); + } +} + +TEST_P(BacktraceGuardedPoolAllocator, MultipleInvalidFreeOnlyOneOutput) { + SCOPED_TRACE(""); + char *Ptr = static_cast<char *>(AllocateMemory(GPA)); + // First time should generate a crash report. + DeallocateMemory(GPA, Ptr + 1); + CheckOnlyOneGwpAsanCrash(GetOutputBuffer()); + ASSERT_NE(std::string::npos, GetOutputBuffer().find("Invalid (Wild) Free")); + + // Ensure the crash is only reported once. + GetOutputBuffer().clear(); + for (size_t i = 0; i < 100; ++i) { + DeallocateMemory(GPA, Ptr + 1); + ASSERT_TRUE(GetOutputBuffer().empty()); + } +} + +TEST_P(BacktraceGuardedPoolAllocator, MultipleUseAfterFreeOnlyOneOutput) { + SCOPED_TRACE(""); + void *Ptr = AllocateMemory(GPA); + DeallocateMemory(GPA, Ptr); + // First time should generate a crash report. + TouchMemory(Ptr); + ASSERT_NE(std::string::npos, GetOutputBuffer().find("Use After Free")); + + // Ensure the crash is only reported once. + GetOutputBuffer().clear(); + for (size_t i = 0; i < 100; ++i) { + TouchMemory(Ptr); + ASSERT_TRUE(GetOutputBuffer().empty()); + } +} + +TEST_P(BacktraceGuardedPoolAllocator, MultipleBufferOverflowOnlyOneOutput) { + SCOPED_TRACE(""); + char *Ptr = static_cast<char *>(AllocateMemory(GPA)); + // First time should generate a crash report. + TouchMemory(Ptr - 16); + TouchMemory(Ptr + 16); + CheckOnlyOneGwpAsanCrash(GetOutputBuffer()); + if (GetOutputBuffer().find("Buffer Overflow") == std::string::npos && + GetOutputBuffer().find("Buffer Underflow") == std::string::npos) + FAIL() << "Failed to detect buffer underflow/overflow:\n" + << GetOutputBuffer(); + + // Ensure the crash is only reported once. + GetOutputBuffer().clear(); + for (size_t i = 0; i < 100; ++i) { + TouchMemory(Ptr - 16); + TouchMemory(Ptr + 16); + ASSERT_TRUE(GetOutputBuffer().empty()) << GetOutputBuffer(); + } +} + +TEST_P(BacktraceGuardedPoolAllocator, OneDoubleFreeOneUseAfterFree) { + SCOPED_TRACE(""); + void *Ptr = AllocateMemory(GPA); + DeallocateMemory(GPA, Ptr); + // First time should generate a crash report. + DeallocateMemory(GPA, Ptr); + CheckOnlyOneGwpAsanCrash(GetOutputBuffer()); + ASSERT_NE(std::string::npos, GetOutputBuffer().find("Double Free")); + + // Ensure the crash is only reported once. + GetOutputBuffer().clear(); + for (size_t i = 0; i < 100; ++i) { + DeallocateMemory(GPA, Ptr); + ASSERT_TRUE(GetOutputBuffer().empty()); + } +} + +// We use double-free to detect that each slot can generate as single error. +// Use-after-free would also be acceptable, but buffer-overflow wouldn't be, as +// the random left/right alignment means that one right-overflow can disable +// page protections, and a subsequent left-overflow of a slot that's on the +// right hand side may not trap. +TEST_P(BacktraceGuardedPoolAllocator, OneErrorReportPerSlot) { + SCOPED_TRACE(""); + std::vector<void *> Ptrs; + for (size_t i = 0; i < GPA.getAllocatorState()->MaxSimultaneousAllocations; + ++i) { + void *Ptr = AllocateMemory(GPA); + ASSERT_NE(Ptr, nullptr); + Ptrs.push_back(Ptr); + DeallocateMemory(GPA, Ptr); + DeallocateMemory(GPA, Ptr); + CheckOnlyOneGwpAsanCrash(GetOutputBuffer()); + ASSERT_NE(std::string::npos, GetOutputBuffer().find("Double Free")); + // Ensure the crash from this slot is only reported once. + GetOutputBuffer().clear(); + DeallocateMemory(GPA, Ptr); + ASSERT_TRUE(GetOutputBuffer().empty()); + // Reset the buffer, as we're gonna move to the next allocation. + GetOutputBuffer().clear(); + } + + // All slots should have been used. No further errors should occur. + for (size_t i = 0; i < 100; ++i) + ASSERT_EQ(AllocateMemory(GPA), nullptr); + for (void *Ptr : Ptrs) { + DeallocateMemory(GPA, Ptr); + TouchMemory(Ptr); + } + ASSERT_TRUE(GetOutputBuffer().empty()); +} + +void singleAllocThrashTask(gwp_asan::GuardedPoolAllocator *GPA, + std::atomic<bool> *StartingGun, + unsigned NumIterations, unsigned Job, char *Ptr) { + while (!*StartingGun) { + // Wait for starting gun. + } + + for (unsigned i = 0; i < NumIterations; ++i) { + switch (Job) { + case 0: + DeallocateMemory(*GPA, Ptr); + break; + case 1: + DeallocateMemory(*GPA, Ptr + 1); + break; + case 2: + TouchMemory(Ptr); + break; + case 3: + TouchMemory(Ptr - 16); + TouchMemory(Ptr + 16); + break; + default: + __builtin_trap(); + } + } +} + +void runInterThreadThrashingSingleAlloc(unsigned NumIterations, + gwp_asan::GuardedPoolAllocator *GPA) { + std::atomic<bool> StartingGun{false}; + std::vector<std::thread> Threads; + constexpr unsigned kNumThreads = 4; + if (std::thread::hardware_concurrency() < kNumThreads) { + GTEST_SKIP() << "Not enough threads to run this test"; + } + + char *Ptr = static_cast<char *>(AllocateMemory(*GPA)); + + for (unsigned i = 0; i < kNumThreads; ++i) { + Threads.emplace_back(singleAllocThrashTask, GPA, &StartingGun, + NumIterations, i, Ptr); + } + + StartingGun = true; + + for (auto &T : Threads) + T.join(); +} + +TEST_P(BacktraceGuardedPoolAllocator, InterThreadThrashingSingleAlloc) { + SCOPED_TRACE(""); + constexpr unsigned kNumIterations = 100000; + runInterThreadThrashingSingleAlloc(kNumIterations, &GPA); + CheckOnlyOneGwpAsanCrash(GetOutputBuffer()); +} + +INSTANTIATE_TEST_SUITE_P(RecoverableTests, BacktraceGuardedPoolAllocator, + /* Recoverable */ testing::Values(true)); |