summaryrefslogtreecommitdiff
path: root/gnu/llvm/compiler-rt/lib/gwp_asan/tests
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/llvm/compiler-rt/lib/gwp_asan/tests')
-rw-r--r--gnu/llvm/compiler-rt/lib/gwp_asan/tests/CMakeLists.txt12
-rw-r--r--gnu/llvm/compiler-rt/lib/gwp_asan/tests/alignment.cpp82
-rw-r--r--gnu/llvm/compiler-rt/lib/gwp_asan/tests/backtrace.cpp83
-rw-r--r--gnu/llvm/compiler-rt/lib/gwp_asan/tests/crash_handler_api.cpp21
-rw-r--r--gnu/llvm/compiler-rt/lib/gwp_asan/tests/harness.cpp18
-rw-r--r--gnu/llvm/compiler-rt/lib/gwp_asan/tests/harness.h41
-rw-r--r--gnu/llvm/compiler-rt/lib/gwp_asan/tests/iterate.cpp1
-rw-r--r--gnu/llvm/compiler-rt/lib/gwp_asan/tests/recoverable.cpp210
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));