diff options
author | Kostya Serebryany <kcc@google.com> | 2013-09-12 13:25:29 +0000 |
---|---|---|
committer | Kostya Serebryany <kcc@google.com> | 2013-09-12 13:25:29 +0000 |
commit | 627ea6391e7ed1be37cf284e39e10de9862df4c1 (patch) | |
tree | 355a89fa16c5ea899fed7d770565d53f88a5a76f /compiler-rt | |
parent | db6144e3e33ceef876be04805cf3333289830ce0 (diff) | |
download | bcm5719-llvm-627ea6391e7ed1be37cf284e39e10de9862df4c1.tar.gz bcm5719-llvm-627ea6391e7ed1be37cf284e39e10de9862df4c1.zip |
[asan] add a test for use-after-return and exceptions and fix it. Not 100% sure this is a complete fix, will keep looking for harder cases.
llvm-svn: 190603
Diffstat (limited to 'compiler-rt')
-rw-r--r-- | compiler-rt/lib/asan/asan_fake_stack.cc | 32 | ||||
-rw-r--r-- | compiler-rt/lib/asan/asan_fake_stack.h | 8 | ||||
-rw-r--r-- | compiler-rt/lib/asan/asan_rtl.cc | 2 | ||||
-rw-r--r-- | compiler-rt/lib/asan/lit_tests/TestCases/uar_and_exceptions.cc | 27 |
4 files changed, 68 insertions, 1 deletions
diff --git a/compiler-rt/lib/asan/asan_fake_stack.cc b/compiler-rt/lib/asan/asan_fake_stack.cc index 69044f00c00..078f9042eee 100644 --- a/compiler-rt/lib/asan/asan_fake_stack.cc +++ b/compiler-rt/lib/asan/asan_fake_stack.cc @@ -25,6 +25,8 @@ void FakeStack::PoisonAll(u8 magic) { FakeFrame *FakeStack::Allocate(uptr stack_size_log, uptr class_id, uptr real_stack) { CHECK_LT(class_id, kNumberOfSizeClasses); + if (needs_gc_) + GC(real_stack); uptr &hint_position = hint_position_[class_id]; const int num_iter = NumberOfFrames(stack_size_log, class_id); u8 *flags = GetFlags(stack_size_log, class_id); @@ -38,6 +40,7 @@ FakeFrame *FakeStack::Allocate(uptr stack_size_log, uptr class_id, GetFrame(stack_size_log, class_id, pos)); res->real_stack = real_stack; res->class_id = class_id; + allocated_from_size_class_mask_ |= 1UL << class_id; return res; } } @@ -70,6 +73,35 @@ uptr FakeStack::AddrIsInFakeStack(uptr ptr) { return base + pos * BytesInSizeClass(class_id); } +void FakeStack::HandleNoReturn() { + needs_gc_ = true; +} + +// When throw, longjmp or some such happens we don't call OnFree() and +// as the result may leak one or more fake frames, but the good news is that +// we are notified about all such events by HandleNoReturn(). +// If we recently had such no-return event we need to collect garbage frames. +// We do it based on their 'real_stack' values -- everything that is lower +// than the current real_stack is garbage. +NOINLINE void FakeStack::GC(uptr real_stack) { + uptr collected = 0; + for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) { + if (!(allocated_from_size_class_mask_ & (1UL << class_id))) continue; + u8 *flags = GetFlags(stack_size_log(), class_id); + for (uptr i = 0, n = NumberOfFrames(stack_size_log(), class_id); i < n; + i++) { + if (flags[i] == 0) continue; // not allocated. + FakeFrame *ff = reinterpret_cast<FakeFrame *>( + GetFrame(stack_size_log(), class_id, i)); + if (ff->real_stack < real_stack) { + flags[i] = 0; + collected++; + } + } + } + needs_gc_ = false; +} + ALWAYS_INLINE uptr OnMalloc(uptr class_id, uptr size, uptr real_stack) { AsanThread *t = GetCurrentThread(); if (!t) return real_stack; diff --git a/compiler-rt/lib/asan/asan_fake_stack.h b/compiler-rt/lib/asan/asan_fake_stack.h index 493e7e521d8..f5f49064de6 100644 --- a/compiler-rt/lib/asan/asan_fake_stack.h +++ b/compiler-rt/lib/asan/asan_fake_stack.h @@ -153,15 +153,21 @@ class FakeStack { uptr stack_size_log() const { return stack_size_log_; } + void HandleNoReturn(); + void GC(uptr real_stack); + private: FakeStack() { } - static const uptr kFlagsOffset = 4096; // There is were flags begin. + static const uptr kFlagsOffset = 4096; // This is were the flags begin. // Must match the number of uses of DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID COMPILER_CHECK(kNumberOfSizeClasses == 11); static const uptr kMaxStackMallocSize = 1 << kMaxStackFrameSizeLog; uptr hint_position_[kNumberOfSizeClasses]; uptr stack_size_log_; + // a bit is set if something was allocated from the corresponding size class. + uptr allocated_from_size_class_mask_; + bool needs_gc_; }; } // namespace __asan diff --git a/compiler-rt/lib/asan/asan_rtl.cc b/compiler-rt/lib/asan/asan_rtl.cc index 80bd5918013..a77f9de6ba4 100644 --- a/compiler-rt/lib/asan/asan_rtl.cc +++ b/compiler-rt/lib/asan/asan_rtl.cc @@ -427,6 +427,8 @@ void NOINLINE __asan_handle_no_return() { return; } PoisonShadow(bottom, top - bottom, 0); + if (curr_thread->has_fake_stack()) + curr_thread->fake_stack()->HandleNoReturn(); } void NOINLINE __asan_set_death_callback(void (*callback)(void)) { diff --git a/compiler-rt/lib/asan/lit_tests/TestCases/uar_and_exceptions.cc b/compiler-rt/lib/asan/lit_tests/TestCases/uar_and_exceptions.cc new file mode 100644 index 00000000000..f1639f08443 --- /dev/null +++ b/compiler-rt/lib/asan/lit_tests/TestCases/uar_and_exceptions.cc @@ -0,0 +1,27 @@ +// Test that use-after-return works with exceptions. +// RUN: %clangxx_asan -fsanitize=use-after-return -O0 %s -o %t && %t + +#include <stdio.h> + +volatile char *g; + +void Func(int depth) { + char frame[100]; + g = &frame[0]; + if (depth) + Func(depth - 1); + else + throw 1; +} + +int main(int argc, char **argv) { + for (int i = 0; i < 4000; i++) { + try { + Func(argc * 100); + } catch(...) { + } + if ((i % 1000) == 0) + fprintf(stderr, "done [%d]\n", i); + } + return 0; +} |