summaryrefslogtreecommitdiff
path: root/lib/gwp_asan
diff options
context:
space:
mode:
authorMitch Phillips <mitchphillips@outlook.com>2019-06-25 22:29:05 +0000
committerMitch Phillips <mitchphillips@outlook.com>2019-06-25 22:29:05 +0000
commitc3fed978574b8fe3d3e69bd88b34943115789604 (patch)
treea6a33a7c2463e6be60e1225b2000652bf706580b /lib/gwp_asan
parentd4faf4e0586d8ec150032c4d8497ddd36c7b0ee6 (diff)
downloadcompiler-rt-c3fed978574b8fe3d3e69bd88b34943115789604.tar.gz
[GWP-ASan] Guard against recursive allocs. Pack TLS for perf.
Summary: Add a recursivity guard for GPA::allocate(). This means that any recursive allocations will fall back to the supporting allocator. In future patches, we will introduce stack trace collection support. The unwinder will be provided by the supporting allocator, and we can't guarantee they don't call malloc() (e.g. backtrace() on posix may call dlopen(), which may call malloc(). Furthermore, this patch packs the new TLS recursivity guard into a thread local struct, so that TLS variables should be hopefully not fall across cache lines. Reviewers: vlad.tsyrklevich, morehouse, eugenis Reviewed By: eugenis Subscribers: kubamracek, #sanitizers, llvm-commits, eugenis Tags: #sanitizers, #llvm Differential Revision: https://reviews.llvm.org/D63736 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@364356 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/gwp_asan')
-rw-r--r--lib/gwp_asan/guarded_pool_allocator.cpp33
-rw-r--r--lib/gwp_asan/guarded_pool_allocator.h33
2 files changed, 51 insertions, 15 deletions
diff --git a/lib/gwp_asan/guarded_pool_allocator.cpp b/lib/gwp_asan/guarded_pool_allocator.cpp
index 93743cd51..68ec00ddd 100644
--- a/lib/gwp_asan/guarded_pool_allocator.cpp
+++ b/lib/gwp_asan/guarded_pool_allocator.cpp
@@ -49,7 +49,9 @@ void GuardedPoolAllocator::AllocationMetadata::RecordAllocation(
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();
}
@@ -124,7 +126,28 @@ void GuardedPoolAllocator::init(const options::Options &Opts) {
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;
@@ -385,8 +408,7 @@ struct ScopedEndOfReportDecorator {
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;
}
@@ -395,6 +417,7 @@ void GuardedPoolAllocator::reportErrorInternal(uintptr_t AccessPtr,
// 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);
@@ -429,5 +452,7 @@ void GuardedPoolAllocator::reportErrorInternal(uintptr_t AccessPtr,
// 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
diff --git a/lib/gwp_asan/guarded_pool_allocator.h b/lib/gwp_asan/guarded_pool_allocator.h
index 50d9baf4f..9b77c007b 100644
--- a/lib/gwp_asan/guarded_pool_allocator.h
+++ b/lib/gwp_asan/guarded_pool_allocator.h
@@ -96,14 +96,11 @@ public:
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
@@ -245,9 +242,23 @@ private:
// 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