diff --git a/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp b/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp --- a/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp +++ b/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp @@ -14,6 +14,7 @@ #include "combined.h" #include "mem_map.h" +#include #include #include #include @@ -78,13 +79,8 @@ } ~TestAllocator() { this->unmapTestOnly(); } - void *operator new(size_t size) { - void *p = nullptr; - EXPECT_EQ(0, posix_memalign(&p, alignof(TestAllocator), size)); - return p; - } - - void operator delete(void *ptr) { free(ptr); } + void *operator new(size_t size); + void operator delete(void *ptr); }; template struct ScudoCombinedTest : public Test { @@ -111,11 +107,25 @@ #define SCUDO_TYPED_TEST_ALL_TYPES(FIXTURE, NAME) \ SCUDO_TYPED_TEST_TYPE(FIXTURE, NAME, AndroidSvelteConfig) \ SCUDO_TYPED_TEST_TYPE(FIXTURE, NAME, FuchsiaConfig) +constexpr size_t kMaxSize = + std::max({sizeof(TestAllocator), + sizeof(TestAllocator)}); +constexpr size_t kMaxAlign = + std::max({alignof(TestAllocator), + alignof(TestAllocator)}); #else #define SCUDO_TYPED_TEST_ALL_TYPES(FIXTURE, NAME) \ SCUDO_TYPED_TEST_TYPE(FIXTURE, NAME, AndroidSvelteConfig) \ SCUDO_TYPED_TEST_TYPE(FIXTURE, NAME, DefaultConfig) \ SCUDO_TYPED_TEST_TYPE(FIXTURE, NAME, AndroidConfig) +constexpr size_t kMaxSize = + std::max({sizeof(TestAllocator), + sizeof(TestAllocator), + sizeof(TestAllocator)}); +constexpr size_t kMaxAlign = + std::max({alignof(TestAllocator), + alignof(TestAllocator), + alignof(TestAllocator)}); #endif #define SCUDO_TYPED_TEST_TYPE(FIXTURE, NAME, TYPE) \ @@ -130,6 +140,56 @@ SCUDO_TYPED_TEST_ALL_TYPES(FIXTURE, NAME) \ template void FIXTURE##NAME::Run() +#if SCUDO_RISCV64 +// The allocator is over 4MB large. Rather than creating an instance of this on +// the heap, keep it in a global storage to reduce fragmentation from having to +// mmap this at the start of every test. +// +// Note that this is a workaround for allowing the combined tests to run on +// RISCV with a 39-bit VMA under asan. This unique environment means we're more +// limited on the space we can mmap, especially if there's two allocators +// reserving a lot of contiguous space. Using storage mixed in with globals +// reduces overal fragments. +struct TestAllocatorStorage { + static void *get() NO_THREAD_SAFETY_ANALYSIS { + M.lock(); + return AllocatorStorage; + } + + static void release(void *ptr) NO_THREAD_SAFETY_ANALYSIS { + M.assertHeld(); + M.unlock(); + ASSERT_EQ(ptr, AllocatorStorage); + } + + static scudo::HybridMutex M; + static uint8_t AllocatorStorage[kMaxSize]; +}; +scudo::HybridMutex TestAllocatorStorage::M; +alignas(kMaxAlign) uint8_t TestAllocatorStorage::AllocatorStorage[kMaxSize]; +#else +struct TestAllocatorStorage { + static void *get() NO_THREAD_SAFETY_ANALYSIS { + void *p = nullptr; + EXPECT_EQ(0, posix_memalign(&p, kMaxAlign, kMaxSize)); + return p; + } + static void release(void *ptr) NO_THREAD_SAFETY_ANALYSIS { free(ptr); } +}; +#endif + +template +void *TestAllocator::operator new(size_t size) { + assert(size <= kMaxSize && + "Allocation size doesn't fit in the allocator storage"); + return TestAllocatorStorage::get(); +} + +template +void TestAllocator::operator delete(void *ptr) { + TestAllocatorStorage::release(ptr); +} + SCUDO_TYPED_TEST(ScudoCombinedTest, IsOwned) { auto *Allocator = this->Allocator.get(); static scudo::u8 StaticBuffer[scudo::Chunk::getHeaderSize() + 1];