[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:
parent
db6144e3e3
commit
627ea6391e
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
Loading…
Reference in New Issue