[asan] hopefully make the FakeStack async-signal safe, enable the related test

llvm-svn: 190592
This commit is contained in:
Kostya Serebryany 2013-09-12 08:34:50 +00:00
parent 9c0748a90c
commit 628cda7367
6 changed files with 61 additions and 11 deletions

View File

@ -74,6 +74,7 @@ ALWAYS_INLINE uptr OnMalloc(uptr class_id, uptr size, uptr real_stack) {
AsanThread *t = GetCurrentThread();
if (!t) return real_stack;
FakeStack *fs = t->fake_stack();
if (!fs) return real_stack;
FakeFrame *ff = fs->Allocate(fs->stack_size_log(), class_id, real_stack);
uptr ptr = reinterpret_cast<uptr>(ff);
PoisonShadow(ptr, size, 0);

View File

@ -56,9 +56,7 @@ struct FakeFrame {
// frames in round robin fasion to maximize the delay between a deallocation
// and the next allocation.
//
// FIXME: don't lazy init the FakeStack (not async-signal safe).
// FIXME: handle throw/longjmp/clone, i.e. garbage collect the unwinded frames.
// FIXME: use low bits of the pointer to store stack_size_log_ (performance).
class FakeStack {
static const uptr kMinStackFrameSizeLog = 6; // Min frame is 64B.
static const uptr kMaxStackFrameSizeLog = 16; // Max stack frame is 64K.

View File

@ -107,6 +107,27 @@ void AsanThread::Destroy() {
UnmapOrDie(this, size);
}
// We want to create the FakeStack lazyly on the first use, but not eralier
// than the stack size is known and the procedure has to be async-signal safe.
FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() {
uptr stack_size = this->stack_size();
if (stack_size == 0) // stack_size is not yet available, don't use FakeStack.
return 0;
uptr old_val = 0;
// fake_stack_ has 3 states:
// 0 -- not initialized
// 1 -- being initialized
// ptr -- initialized
// This CAS checks if the state was 0 and if so changes it to state 1,
// if that was successfull, it initilizes the pointer.
if (atomic_compare_exchange_strong(
reinterpret_cast<atomic_uintptr_t *>(&fake_stack_), &old_val, 1UL,
memory_order_relaxed))
return fake_stack_ =
FakeStack::Create(Log2(RoundUpToPowerOfTwo(stack_size)));
return 0;
}
void AsanThread::Init() {
SetThreadStackAndTls();
CHECK(AddrIsInMem(stack_bottom_));

View File

@ -82,8 +82,8 @@ class AsanThread {
}
FakeStack *fake_stack() {
if (!fake_stack_) // FIXME: lazy init is not async-signal safe.
fake_stack_ = FakeStack::Create(Log2(RoundUpToPowerOfTwo(stack_size())));
if (reinterpret_cast<uptr>(fake_stack_) <= 1)
return AsyncSignalSafeLazyInitFakeStack();
return fake_stack_;
}
@ -100,6 +100,8 @@ class AsanThread {
AsanThread() : unwinding(false) {}
void SetThreadStackAndTls();
void ClearShadowForThreadStackAndTLS();
FakeStack *AsyncSignalSafeLazyInitFakeStack();
AsanThreadContext *context_;
thread_callback_t start_routine_;
void *arg_;

View File

@ -1,7 +1,7 @@
// This test shows that the current implementation of use-after-return is
// not signal-safe.
// RUN: %clangxx_asan -O1 %s -o %t -lpthread && %t
// FAILS: %clangxx_asan -fsanitize=use-after-return -O1 %s -o %t -lpthread&& %t
// RUN: %clangxx_asan -fsanitize=use-after-return -O1 %s -o %t -lpthread && %t
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
@ -57,10 +57,14 @@ void *Thread(void *) {
int main(int argc, char **argv) {
EnableSigprof(SignalHandler);
const int kNumThread = 32;
pthread_t t[kNumThread];
for (int i = 0; i < kNumThread; i++)
pthread_create(&t[i], 0, Thread, 0);
for (int i = 0; i < kNumThread; i++)
pthread_join(t[i], 0);
for (int i = 0; i < 4; i++) {
fprintf(stderr, ".");
const int kNumThread = sizeof(void*) == 8 ? 16 : 8;
pthread_t t[kNumThread];
for (int i = 0; i < kNumThread; i++)
pthread_create(&t[i], 0, Thread, 0);
for (int i = 0; i < kNumThread; i++)
pthread_join(t[i], 0);
}
fprintf(stderr, "\n");
}

View File

@ -9,13 +9,22 @@
// Regression test for a CHECK failure with small stack size and large frame.
// RUN: %clangxx_asan -fsanitize=use-after-return -O3 %s -o %t -DkSize=10000 && \
// RUN: (ulimit -s 65; not %t) 2>&1 | FileCheck %s
//
// Test that we can find UAR in a thread other than main:
// RUN: %clangxx_asan -fsanitize=use-after-return -DUseThread -O2 %s -o %t && \
// RUN: not %t 2>&1 | FileCheck --check-prefix=THREAD %s
#include <stdio.h>
#include <pthread.h>
#ifndef kSize
# define kSize 1
#endif
#ifndef UseThread
# define UseThread 0
#endif
__attribute__((noinline))
char *Ident(char *x) {
fprintf(stderr, "1: %p\n", x);
@ -36,9 +45,24 @@ void Func2(char *x) {
// CHECK: #0{{.*}}Func2{{.*}}stack-use-after-return.cc:[[@LINE-2]]
// CHECK: is located in stack of thread T0 at offset
// CHECK: 'local' <== Memory access at offset 32 is inside this variable
// THREAD: WRITE of size 1 {{.*}} thread T{{[1-9]}}
// THREAD: #0{{.*}}Func2{{.*}}stack-use-after-return.cc:[[@LINE-6]]
// THREAD: is located in stack of thread T{{[1-9]}} at offset
// THREAD: 'local' <== Memory access at offset 32 is inside this variable
}
void *Thread(void *unused) {
Func2(Func1());
return NULL;
}
int main(int argc, char **argv) {
#if UseThread
pthread_t t;
pthread_create(&t, 0, Thread, 0);
pthread_join(t, 0);
#else
Func2(Func1());
#endif
return 0;
}