void GuardedPoolAllocator::AllocationMetadata::RecordDeallocation() {
IsDeallocated = true;
- // TODO(hctim): Implement stack trace collection.
+ // TODO(hctim): Implement stack trace collection. Ensure that the unwinder is
+ // not called if we have our recursive flag called, otherwise non-reentrant
+ // unwinders may deadlock.
DeallocationTrace.ThreadID = getThreadID();
}
installSignalHandlers();
}
+namespace {
+class ScopedBoolean {
+public:
+ ScopedBoolean(bool &B) : Bool(B) { Bool = true; }
+ ~ScopedBoolean() { Bool = false; }
+
+private:
+ bool &Bool;
+};
+} // anonymous namespace
+
void *GuardedPoolAllocator::allocate(size_t Size) {
+ // GuardedPagePoolEnd == 0 when GWP-ASan is disabled. If we are disabled, fall
+ // back to the supporting allocator.
+ if (GuardedPagePoolEnd == 0)
+ return nullptr;
+
+ // Protect against recursivity.
+ if (ThreadLocals.RecursiveGuard)
+ return nullptr;
+ ScopedBoolean SB(ThreadLocals.RecursiveGuard);
+
if (Size == 0 || Size > maximumAllocationSize())
return nullptr;
options::Printf_t Printf;
};
-void GuardedPoolAllocator::reportErrorInternal(uintptr_t AccessPtr,
- Error E) {
+void GuardedPoolAllocator::reportErrorInternal(uintptr_t AccessPtr, Error E) {
if (!pointerIsMine(reinterpret_cast<void *>(AccessPtr))) {
return;
}
// This does not guarantee that there are no races, because another thread can
// take the locks during the time that the signal handler is being called.
PoolMutex.tryLock();
+ ThreadLocals.RecursiveGuard = true;
Printf("*** GWP-ASan detected a memory error ***\n");
ScopedEndOfReportDecorator Decorator(Printf);
// TODO(hctim): Implement dumping here of allocation/deallocation traces.
}
-TLS_INITIAL_EXEC uint64_t GuardedPoolAllocator::NextSampleCounter = 0;
+TLS_INITIAL_EXEC
+GuardedPoolAllocator::ThreadLocalPackedVariables
+ GuardedPoolAllocator::ThreadLocals;
} // namespace gwp_asan
ALWAYS_INLINE bool shouldSample() {
// NextSampleCounter == 0 means we "should regenerate the counter".
// == 1 means we "should sample this allocation".
- if (UNLIKELY(NextSampleCounter == 0)) {
- // GuardedPagePoolEnd == 0 if GWP-ASan is disabled.
- if (UNLIKELY(GuardedPagePoolEnd == 0))
- return false;
- NextSampleCounter = (getRandomUnsigned32() % AdjustedSampleRate) + 1;
- }
-
- return UNLIKELY(--NextSampleCounter == 0);
+ if (UNLIKELY(ThreadLocals.NextSampleCounter == 0))
+ ThreadLocals.NextSampleCounter =
+ (getRandomUnsigned32() % AdjustedSampleRate) + 1;
+
+ return UNLIKELY(--ThreadLocals.NextSampleCounter == 0);
}
// Returns whether the provided pointer is a current sampled allocation that
// GWP-ASan is disabled, we wish to never spend wasted cycles recalculating
// the sample rate.
uint32_t AdjustedSampleRate = UINT32_MAX;
- // Thread-local decrementing counter that indicates that a given allocation
- // should be sampled when it reaches zero.
- static TLS_INITIAL_EXEC uint64_t NextSampleCounter;
+
+ // Pack the thread local variables into a struct to ensure that they're in
+ // the same cache line for performance reasons. These are the most touched
+ // variables in GWP-ASan.
+ struct alignas(8) ThreadLocalPackedVariables {
+ constexpr ThreadLocalPackedVariables() {}
+ // Thread-local decrementing counter that indicates that a given allocation
+ // should be sampled when it reaches zero.
+ uint32_t NextSampleCounter = 0;
+ // 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;
+ };
+ static TLS_INITIAL_EXEC ThreadLocalPackedVariables ThreadLocals;
};
} // namespace gwp_asan