diff --git a/compiler-rt/lib/gwp_asan/CMakeLists.txt b/compiler-rt/lib/gwp_asan/CMakeLists.txt --- a/compiler-rt/lib/gwp_asan/CMakeLists.txt +++ b/compiler-rt/lib/gwp_asan/CMakeLists.txt @@ -10,7 +10,6 @@ platform_specific/mutex_posix.cpp platform_specific/utilities_posix.cpp guarded_pool_allocator.cpp - random.cpp stack_trace_compressor.cpp utilities.cpp ) @@ -23,7 +22,6 @@ mutex.h options.h options.inc - random.h stack_trace_compressor.h utilities.h ) 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 @@ -13,7 +13,6 @@ #include "gwp_asan/definitions.h" #include "gwp_asan/mutex.h" #include "gwp_asan/options.h" -#include "gwp_asan/random.h" #include "gwp_asan/stack_trace_compressor.h" #include @@ -80,7 +79,8 @@ // UINT32_MAX. if (GWP_ASAN_UNLIKELY(ThreadLocals.NextSampleCounter == 0)) ThreadLocals.NextSampleCounter = - (getRandomUnsigned32() % (AdjustedSampleRatePlusOne - 1)) + 1; + ((getRandomUnsigned32() % (AdjustedSampleRatePlusOne - 1)) + 1) & + ThreadLocalPackedVariables::NextSampleCounterMask; return GWP_ASAN_UNLIKELY(--ThreadLocals.NextSampleCounter == 0); } @@ -195,17 +195,42 @@ // the same cache line for performance reasons. These are the most touched // variables in GWP-ASan. struct alignas(8) ThreadLocalPackedVariables { - constexpr ThreadLocalPackedVariables() {} + constexpr ThreadLocalPackedVariables() + : RandomState(0xff82eb50), NextSampleCounter(0), RecursiveGuard(false) { + } + // Initialised to a magic constant so that an uninitialised GWP-ASan won't + // regenerate its sample counter for as long as possible. The xorshift32() + // algorithm used below results in getRandomUnsigned32(0xff82eb50) == + // 0xfffffea4. + uint32_t RandomState; // Thread-local decrementing counter that indicates that a given allocation // should be sampled when it reaches zero. - uint32_t NextSampleCounter = 0; + uint32_t NextSampleCounter : 31; + // The mask is needed to silence conversion errors. + static const uint32_t NextSampleCounterMask = (1U << 31) - 1; // Guard against recursivity. Unwinders often contain complex behaviour that // may not be safe for the allocator (i.e. the unwinder calls dlopen(), // which calls malloc()). When recursive behaviour is detected, we will // automatically fall back to the supporting allocator to supply the // allocation. - bool RecursiveGuard = false; + bool RecursiveGuard : 1; }; + static_assert(sizeof(ThreadLocalPackedVariables) == sizeof(uint64_t), + "thread local data does not fit in a uint64_t"); + + class ScopedRecursiveGuard { + public: + ScopedRecursiveGuard() { ThreadLocals.RecursiveGuard = true; } + ~ScopedRecursiveGuard() { ThreadLocals.RecursiveGuard = false; } + }; + + // Initialise the PRNG, platform-specific. + void initPRNG(); + + // xorshift (32-bit output), extremely fast PRNG that uses arithmetic + // operations only. Seeded using platform-specific mechanisms by initPRNG(). + uint32_t getRandomUnsigned32(); + static GWP_ASAN_TLS_INITIAL_EXEC ThreadLocalPackedVariables ThreadLocals; }; } // namespace gwp_asan 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 @@ -10,7 +10,6 @@ #include "gwp_asan/optional/segv_handler.h" #include "gwp_asan/options.h" -#include "gwp_asan/random.h" #include "gwp_asan/utilities.h" // RHEL creates the PRIu64 format macro (for printing uint64_t's) only when this @@ -38,15 +37,6 @@ // referenced by users outside this translation unit, in order to avoid // init-order-fiasco. GuardedPoolAllocator *SingletonPtr = nullptr; - -class ScopedBoolean { -public: - ScopedBoolean(bool &B) : Bool(B) { Bool = true; } - ~ScopedBoolean() { Bool = false; } - -private: - bool &Bool; -}; } // anonymous namespace // Gets the singleton implementation of this class. Thread-compatible until @@ -64,7 +54,7 @@ return; Check(Opts.SampleRate >= 0, "GWP-ASan Error: SampleRate is < 0."); - Check(Opts.SampleRate <= INT32_MAX, "GWP-ASan Error: SampleRate is > 2^31."); + Check(Opts.SampleRate < (1 << 30), "GWP-ASan Error: SampleRate is >= 2^30."); Check(Opts.MaxSimultaneousAllocations >= 0, "GWP-ASan Error: MaxSimultaneousAllocations is < 0."); @@ -102,7 +92,8 @@ initPRNG(); ThreadLocals.NextSampleCounter = - (getRandomUnsigned32() % (AdjustedSampleRatePlusOne - 1)) + 1; + ((getRandomUnsigned32() % (AdjustedSampleRatePlusOne - 1)) + 1) & + ThreadLocalPackedVariables::NextSampleCounterMask; State.GuardedPagePool = reinterpret_cast(GuardedPoolMemory); State.GuardedPagePoolEnd = @@ -155,13 +146,17 @@ void *GuardedPoolAllocator::allocate(size_t Size) { // GuardedPagePoolEnd == 0 when GWP-ASan is disabled. If we are disabled, fall // back to the supporting allocator. - if (State.GuardedPagePoolEnd == 0) + if (State.GuardedPagePoolEnd == 0) { + ThreadLocals.NextSampleCounter = + (AdjustedSampleRatePlusOne - 1) & + ThreadLocalPackedVariables::NextSampleCounterMask; return nullptr; + } // Protect against recursivity. if (ThreadLocals.RecursiveGuard) return nullptr; - ScopedBoolean SB(ThreadLocals.RecursiveGuard); + ScopedRecursiveGuard SRG; if (Size == 0 || Size > State.maximumAllocationSize()) return nullptr; @@ -241,7 +236,7 @@ // Ensure that the unwinder is not called if the recursive flag is set, // otherwise non-reentrant unwinders may deadlock. if (!ThreadLocals.RecursiveGuard) { - ScopedBoolean B(ThreadLocals.RecursiveGuard); + ScopedRecursiveGuard SRG; Meta->DeallocationTrace.RecordBacktrace(Backtrace); } } @@ -286,6 +281,15 @@ FreeSlots[FreeSlotsLength++] = SlotIndex; } +uint32_t GuardedPoolAllocator::getRandomUnsigned32() { + uint32_t RandomState = ThreadLocals.RandomState; + RandomState ^= RandomState << 13; + RandomState ^= RandomState >> 17; + RandomState ^= RandomState << 5; + ThreadLocals.RandomState = RandomState; + return RandomState; +} + GWP_ASAN_TLS_INITIAL_EXEC GuardedPoolAllocator::ThreadLocalPackedVariables GuardedPoolAllocator::ThreadLocals; diff --git a/compiler-rt/lib/gwp_asan/options.inc b/compiler-rt/lib/gwp_asan/options.inc --- a/compiler-rt/lib/gwp_asan/options.inc +++ b/compiler-rt/lib/gwp_asan/options.inc @@ -29,7 +29,7 @@ GWP_ASAN_OPTION(int, SampleRate, 5000, "The probability (1 / SampleRate) that an allocation is " "selected for GWP-ASan sampling. Default is 5000. Sample rates " - "up to (2^31 - 1) are supported.") + "up to (2^30 - 1) are supported.") // Developer note - This option is not actually processed by GWP-ASan itself. It // is included here so that a user can specify whether they want signal handlers 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 @@ -16,6 +16,7 @@ #include #include #include +#include #include #ifdef ANDROID @@ -33,6 +34,11 @@ } namespace gwp_asan { + +void GuardedPoolAllocator::initPRNG() { + ThreadLocals.RandomState = time(nullptr) + getThreadID(); +} + void *GuardedPoolAllocator::mapMemory(size_t Size, const char *Name) const { void *Ptr = mmap(nullptr, Size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); diff --git a/compiler-rt/lib/gwp_asan/random.h b/compiler-rt/lib/gwp_asan/random.h deleted file mode 100644 --- a/compiler-rt/lib/gwp_asan/random.h +++ /dev/null @@ -1,23 +0,0 @@ -//===-- random.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_RANDOM_H_ -#define GWP_ASAN_RANDOM_H_ - -#include - -namespace gwp_asan { -// Initialise the PRNG, using time and thread ID as the seed. -void initPRNG(); - -// xorshift (32-bit output), extremely fast PRNG that uses arithmetic operations -// only. Seeded using walltime. -uint32_t getRandomUnsigned32(); -} // namespace gwp_asan - -#endif // GWP_ASAN_RANDOM_H_ diff --git a/compiler-rt/lib/gwp_asan/random.cpp b/compiler-rt/lib/gwp_asan/random.cpp deleted file mode 100644 --- a/compiler-rt/lib/gwp_asan/random.cpp +++ /dev/null @@ -1,29 +0,0 @@ -//===-- random.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 "gwp_asan/random.h" -#include "gwp_asan/common.h" - -#include - -// Initialised to a magic constant so that an uninitialised GWP-ASan won't -// regenerate its sample counter for as long as possible. The xorshift32() -// algorithm used below results in getRandomUnsigned32(0xff82eb50) == -// 0xfffffea4. -GWP_ASAN_TLS_INITIAL_EXEC uint32_t RandomState = 0xff82eb50; - -namespace gwp_asan { -void initPRNG() { RandomState = time(nullptr) + getThreadID(); } - -uint32_t getRandomUnsigned32() { - RandomState ^= RandomState << 13; - RandomState ^= RandomState >> 17; - RandomState ^= RandomState << 5; - return RandomState; -} -} // namespace gwp_asan