diff --git a/compiler-rt/lib/gwp_asan/guarded_pool_allocator.h b/compiler-rt/lib/gwp_asan/guarded_pool_allocator.h --- a/compiler-rt/lib/gwp_asan/guarded_pool_allocator.h +++ b/compiler-rt/lib/gwp_asan/guarded_pool_allocator.h @@ -98,6 +98,7 @@ // pool using the provided options. See options.inc for runtime configuration // options. void init(const options::Options &Opts); + void uninitTestOnly(); // Return whether the allocation should be randomly chosen for sampling. GWP_ASAN_ALWAYS_INLINE bool shouldSample() { @@ -156,6 +157,7 @@ // mappings, call mapMemory() followed by markReadWrite() on the returned // pointer. void *mapMemory(size_t Size) const; + void unmapMemory(void *Addr, size_t Size) const; void markReadWrite(void *Ptr, size_t Size) const; void markInaccessible(void *Ptr, size_t Size) const; @@ -169,6 +171,7 @@ // signal(), we have to use platform-specific signal handlers to obtain the // address that caused the SIGSEGV exception. static void installSignalHandlers(); + static void uninstallSignalHandlers(); // Returns the index of the slot that this pointer resides in. If the pointer // is not owned by this pool, the result is undefined. diff --git a/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp b/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp --- a/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp +++ b/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp @@ -171,6 +171,24 @@ installSignalHandlers(); } +void GuardedPoolAllocator::uninitTestOnly() { + if (GuardedPagePool != UINTPTR_MAX) { + unmapMemory(reinterpret_cast(GuardedPagePool), + GuardedPagePoolEnd - GuardedPagePool); + GuardedPagePool = UINTPTR_MAX; + GuardedPagePoolEnd = 0; + } + if (Metadata) { + unmapMemory(Metadata, MaxSimultaneousAllocations * sizeof(*Metadata)); + Metadata = nullptr; + } + if (FreeSlots) { + unmapMemory(FreeSlots, MaxSimultaneousAllocations * sizeof(*FreeSlots)); + FreeSlots = nullptr; + } + uninstallSignalHandlers(); +} + void *GuardedPoolAllocator::allocate(size_t Size) { // GuardedPagePoolEnd == 0 when GWP-ASan is disabled. If we are disabled, fall // back to the supporting allocator. diff --git a/compiler-rt/lib/gwp_asan/platform_specific/guarded_pool_allocator_posix.cpp b/compiler-rt/lib/gwp_asan/platform_specific/guarded_pool_allocator_posix.cpp --- a/compiler-rt/lib/gwp_asan/platform_specific/guarded_pool_allocator_posix.cpp +++ b/compiler-rt/lib/gwp_asan/platform_specific/guarded_pool_allocator_posix.cpp @@ -9,6 +9,7 @@ #include "gwp_asan/guarded_pool_allocator.h" #include +#include #include #include #include @@ -30,6 +31,16 @@ return Ptr; } +void GuardedPoolAllocator::unmapMemory(void *Addr, size_t Size) const { + int Res = munmap(Addr, Size); + + if (Res != 0) { + Printf("Failed to unmap guarded pool allocator memory, errno: %d\n", errno); + Printf(" unmmap(%p, %zu, ...) failed.\n", Addr, Size); + exit(EXIT_FAILURE); + } +} + void GuardedPoolAllocator::markReadWrite(void *Ptr, size_t Size) const { if (mprotect(Ptr, Size, PROT_READ | PROT_WRITE) != 0) { Printf("Failed to set guarded pool allocator memory at as RW, errno: %d\n", @@ -85,6 +96,13 @@ sigaction(SIGSEGV, &Action, &PreviousHandler); } +void GuardedPoolAllocator::uninstallSignalHandlers() { + if (PreviousHandler.sa_sigaction) { + sigaction(SIGSEGV, &PreviousHandler, nullptr); + memset(&PreviousHandler, 0, sizeof(PreviousHandler)); + } +} + uint64_t GuardedPoolAllocator::getThreadID() { #ifdef SYS_gettid return syscall(SYS_gettid); diff --git a/compiler-rt/lib/scudo/standalone/combined.h b/compiler-rt/lib/scudo/standalone/combined.h --- a/compiler-rt/lib/scudo/standalone/combined.h +++ b/compiler-rt/lib/scudo/standalone/combined.h @@ -167,6 +167,9 @@ void unmapTestOnly() { TSDRegistry.unmapTestOnly(); Primary.unmapTestOnly(); +#ifdef GWP_ASAN_HOOKS + GuardedAlloc.uninitTestOnly(); +#endif // GWP_ASAN_HOOKS } TSDRegistryT *getTSDRegistry() { return &TSDRegistry; } 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 @@ -164,6 +164,17 @@ EXPECT_NE(Stats.find("Stats: Quarantine"), std::string::npos); } +// Test that multiple instantiations of the allocator have not messed up the +// process's signal handlers (GWP-ASan used to do this). +void testSEGV() { + const scudo::uptr Size = 4 * scudo::getPageSizeCached(); + scudo::MapPlatformData Data = {}; + void *P = scudo::map(nullptr, Size, "testSEGV", MAP_NOACCESS, &Data); + EXPECT_NE(P, nullptr); + EXPECT_DEATH(memset(P, 0xaa, Size), ""); + scudo::unmap(P, Size, UNMAP_ALL, &Data); +} + TEST(ScudoCombinedTest, BasicCombined) { UseQuarantine = false; testAllocator(); @@ -173,6 +184,7 @@ testAllocator(); UseQuarantine = true; testAllocator(); + testSEGV(); #endif } diff --git a/compiler-rt/test/scudo/standalone/CMakeLists.txt b/compiler-rt/test/scudo/standalone/CMakeLists.txt --- a/compiler-rt/test/scudo/standalone/CMakeLists.txt +++ b/compiler-rt/test/scudo/standalone/CMakeLists.txt @@ -4,6 +4,12 @@ ${CMAKE_CURRENT_BINARY_DIR}/unit/lit.site.cfg.py) list(APPEND SCUDO_STANDALONE_TEST_DEPS ScudoUnitTests) list(APPEND SCUDO_STANDALONE_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/unit) + + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/unit-with-gwp-asan/lit.site.cfg.py.in + ${CMAKE_CURRENT_BINARY_DIR}/unit-with-gwp-asan/lit.site.cfg.py) + list(APPEND SCUDO_STANDALONE_TEST_DEPS ScudoUnitTests) + list(APPEND SCUDO_STANDALONE_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/unit-with-gwp-asan) endif() add_lit_testsuite(check-scudo_standalone diff --git a/compiler-rt/test/scudo/standalone/unit-with-gwp-asan/lit.site.cfg.py.in b/compiler-rt/test/scudo/standalone/unit-with-gwp-asan/lit.site.cfg.py.in new file mode 100644 --- /dev/null +++ b/compiler-rt/test/scudo/standalone/unit-with-gwp-asan/lit.site.cfg.py.in @@ -0,0 +1,16 @@ +@LIT_SITE_CFG_IN_HEADER@ + +# Load common config for all compiler-rt unit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/unittests/lit.common.unit.configured") + +# Setup config name. +config.name = 'ScudoStandalone-Unit-WithGwpAsan' + +# Setup test source and exec root. +# For unit tests, we define it as build directory with unit tests. +config.test_exec_root = "@COMPILER_RT_BINARY_DIR@/lib/scudo/standalone/tests" +config.test_source_root = config.test_exec_root + +# Disable GWP-ASan for scudo internal tests. +if config.gwp_asan: + config.environment['SCUDO_OPTIONS'] = 'GWP_ASAN_Enabled=1'