[GWP-ASan] Move random-related code in the allocator
We need to have all thread specific data packed into a single `uintptr_t` for the upcoming Fuchsia support. We can move the `RandomState` into the `ThreadLocalPackedVariables`, reducing the size of `NextSampleCounter` to 31 bits (or we could reduce `RandomState` to 31 bits). We move `getRandomUnsigned32` into the platform agnostic part of the class, and `initPRNG` in the platform specific part. `ScopedBoolean` is replaced by actual assignments since non-const references to bitfields are prohibited. `random.{h,cpp}` are removed. Differential Revision: https://reviews.llvm.org/D89908
This commit is contained in:
parent
ce565861c7
commit
9903b0586c
|
@ -10,7 +10,6 @@ set(GWP_ASAN_SOURCES
|
|||
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 @@ set(GWP_ASAN_HEADERS
|
|||
mutex.h
|
||||
options.h
|
||||
options.inc
|
||||
random.h
|
||||
stack_trace_compressor.h
|
||||
utilities.h
|
||||
)
|
||||
|
|
|
@ -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 @@ namespace {
|
|||
// 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 @@ void GuardedPoolAllocator::init(const options::Options &Opts) {
|
|||
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.");
|
||||
|
||||
|
@ -155,13 +145,15 @@ static uintptr_t getPageAddr(uintptr_t Ptr, uintptr_t PageSize) {
|
|||
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;
|
||||
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 +233,7 @@ void GuardedPoolAllocator::deallocate(void *Ptr) {
|
|||
// 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 +278,15 @@ void GuardedPoolAllocator::freeSlot(size_t SlotIndex) {
|
|||
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;
|
||||
|
|
|
@ -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 <stddef.h>
|
||||
|
@ -195,17 +194,40 @@ private:
|
|||
// 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;
|
||||
// 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
|
||||
|
|
|
@ -29,7 +29,7 @@ GWP_ASAN_OPTION(int, MaxSimultaneousAllocations, 16,
|
|||
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
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef ANDROID
|
||||
|
@ -33,6 +34,11 @@ void MaybeSetMappingName(void *Mapping, size_t Size, const char *Name) {
|
|||
}
|
||||
|
||||
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);
|
||||
|
|
|
@ -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 <time.h>
|
||||
|
||||
// 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
|
|
@ -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 <stdint.h>
|
||||
|
||||
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_
|
Loading…
Reference in a new issue