[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
This commit is contained in:
Kostya Serebryany 2013-09-12 13:25:29 +00:00
parent db6144e3e3
commit 627ea6391e
4 changed files with 68 additions and 1 deletions

View File

@ -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;

View File

@ -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

View File

@ -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)) {

View File

@ -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;
}