summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/asan/asan_fake_stack.cc1
-rw-r--r--lib/asan/asan_fake_stack.h2
-rw-r--r--lib/asan/asan_thread.cc21
-rw-r--r--lib/asan/asan_thread.h6
-rw-r--r--lib/asan/lit_tests/TestCases/Linux/uar_signals.cc18
-rw-r--r--lib/asan/lit_tests/TestCases/stack-use-after-return.cc24
6 files changed, 61 insertions, 11 deletions
diff --git a/lib/asan/asan_fake_stack.cc b/lib/asan/asan_fake_stack.cc
index 624e53bda..69044f00c 100644
--- a/lib/asan/asan_fake_stack.cc
+++ b/lib/asan/asan_fake_stack.cc
@@ -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);
diff --git a/lib/asan/asan_fake_stack.h b/lib/asan/asan_fake_stack.h
index 7daf37388..b6dc23bb9 100644
--- a/lib/asan/asan_fake_stack.h
+++ b/lib/asan/asan_fake_stack.h
@@ -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.
diff --git a/lib/asan/asan_thread.cc b/lib/asan/asan_thread.cc
index 64d07ca0d..5bea433fb 100644
--- a/lib/asan/asan_thread.cc
+++ b/lib/asan/asan_thread.cc
@@ -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_));
diff --git a/lib/asan/asan_thread.h b/lib/asan/asan_thread.h
index a17a7b4dd..55c399661 100644
--- a/lib/asan/asan_thread.h
+++ b/lib/asan/asan_thread.h
@@ -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_;
diff --git a/lib/asan/lit_tests/TestCases/Linux/uar_signals.cc b/lib/asan/lit_tests/TestCases/Linux/uar_signals.cc
index 59a281487..70aab4fef 100644
--- a/lib/asan/lit_tests/TestCases/Linux/uar_signals.cc
+++ b/lib/asan/lit_tests/TestCases/Linux/uar_signals.cc
@@ -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");
}
diff --git a/lib/asan/lit_tests/TestCases/stack-use-after-return.cc b/lib/asan/lit_tests/TestCases/stack-use-after-return.cc
index 2d321ba6f..79dd72c6a 100644
--- a/lib/asan/lit_tests/TestCases/stack-use-after-return.cc
+++ b/lib/asan/lit_tests/TestCases/stack-use-after-return.cc
@@ -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;
}