Index: lib/asan/asan_interceptors.cc =================================================================== --- lib/asan/asan_interceptors.cc +++ lib/asan/asan_interceptors.cc @@ -346,7 +346,18 @@ uptr stack, ssize; ReadContextStack(ucp, &stack, &ssize); ClearShadowMemoryForContextStack(stack, ssize); + AsanThread *curr_thread = GetCurrentThread(); + uptr old_stack, old_ssize; + if (curr_thread) { + old_stack = curr_thread->stack_bottom(); + old_ssize = curr_thread->stack_size(); + WriteContextStack(oucp, old_stack, old_ssize); + curr_thread->SetUserStack(stack, ssize); + } int res = REAL(swapcontext)(oucp, ucp); + if (curr_thread) { + curr_thread->SetUserStack(old_stack, old_ssize); + } // swapcontext technically does not return, but program may swap context to // "oucp" later, that would look as if swapcontext() returned 0. // We need to clear shadow for ucp once again, as it may be in arbitrary @@ -354,6 +365,35 @@ ClearShadowMemoryForContextStack(stack, ssize); return res; } + +INTERCEPTOR(int, setcontext, struct ucontext_t *ucp) { + uptr stack, ssize; + ReadContextStack(ucp, &stack, &ssize); + ClearShadowMemoryForContextStack(stack, ssize); + + AsanThread *curr_thread = GetCurrentThread(); + uptr old_stack, old_ssize; + if (curr_thread) { + old_stack = curr_thread->stack_bottom(); + old_ssize = curr_thread->stack_size(); + curr_thread->SetUserStack(stack, ssize); + } + int res = REAL(setcontext)(ucp); + if (curr_thread) { + curr_thread->SetUserStack(old_stack, old_ssize); + } + return res; +} + +INTERCEPTOR(int, getcontext, struct ucontext_t *ucp) { + int res = REAL(getcontext)(ucp); + AsanThread *curr_thread = GetCurrentThread(); + if (!res && curr_thread) { + WriteContextStack(ucp, curr_thread->stack_bottom(), + curr_thread->stack_size()); + } + return res; +} #endif // ASAN_INTERCEPT_SWAPCONTEXT INTERCEPTOR(void, longjmp, void *env, int val) { @@ -796,6 +836,8 @@ #endif #if ASAN_INTERCEPT_SWAPCONTEXT ASAN_INTERCEPT_FUNC(swapcontext); + ASAN_INTERCEPT_FUNC(setcontext); + ASAN_INTERCEPT_FUNC(getcontext); #endif #if ASAN_INTERCEPT__LONGJMP ASAN_INTERCEPT_FUNC(_longjmp); Index: lib/asan/asan_internal.h =================================================================== --- lib/asan/asan_internal.h +++ lib/asan/asan_internal.h @@ -76,6 +76,7 @@ void AsanOnDeadlySignal(int, void *siginfo, void *context); void ReadContextStack(void *context, uptr *stack, uptr *ssize); +void WriteContextStack(void *context, uptr stack, uptr ssize); void StopInitOrderChecking(); // Wrapper for TLS/TSD. Index: lib/asan/asan_linux.cc =================================================================== --- lib/asan/asan_linux.cc +++ lib/asan/asan_linux.cc @@ -156,10 +156,20 @@ *stack = (uptr)ucp->uc_stack.ss_sp; *ssize = ucp->uc_stack.ss_size; } + +void WriteContextStack(void *context, uptr stack, uptr ssize) { + ucontext_t *ucp = (ucontext_t*)context; + ucp->uc_stack.ss_sp = (void*)stack; + ucp->uc_stack.ss_size = ssize; +} #else void ReadContextStack(void *context, uptr *stack, uptr *ssize) { UNIMPLEMENTED(); } + +void WriteContextStack(void *context, uptr stack, uptr ssize) { + UNIMPLEMENTED(); +} #endif void *AsanDlSymNext(const char *sym) { Index: lib/asan/asan_mac.cc =================================================================== --- lib/asan/asan_mac.cc +++ lib/asan/asan_mac.cc @@ -70,6 +70,10 @@ UNIMPLEMENTED(); } +void WriteContextStack(void *context, uptr stack, uptr ssize) { + UNIMPLEMENTED(); +} + // Support for the following functions from libdispatch on Mac OS: // dispatch_async_f() // dispatch_async() Index: lib/asan/asan_thread.h =================================================================== --- lib/asan/asan_thread.h +++ lib/asan/asan_thread.h @@ -66,9 +66,15 @@ thread_return_t ThreadStart(uptr os_id, atomic_uintptr_t *signal_thread_is_registered); - uptr stack_top() { return stack_top_; } - uptr stack_bottom() { return stack_bottom_; } - uptr stack_size() { return stack_size_; } + struct StackDescriptor { + uptr stack_top; + uptr stack_bottom; + uptr stack_size; + }; + + uptr stack_top() { return CurrentStack()->stack_top; } + uptr stack_bottom() { return CurrentStack()->stack_bottom; } + uptr stack_size() { return CurrentStack()->stack_size; } uptr tls_begin() { return tls_begin_; } uptr tls_end() { return tls_end_; } DTLS *dtls() { return dtls_; } @@ -76,6 +82,17 @@ AsanThreadContext *context() { return context_; } void set_context(AsanThreadContext *context) { context_ = context; } + void SetUserStack(uptr base, uptr size) { + temp_stack_->stack_bottom = base; + temp_stack_->stack_top = base + size; + temp_stack_->stack_size = size; + + StackDescriptor* oprev = previous_stack_; + previous_stack_ = next_stack_; + next_stack_ = temp_stack_; + temp_stack_ = oprev; + } + struct StackFrameAccess { uptr offset; uptr frame_pc; @@ -83,8 +100,21 @@ }; bool GetStackFrameAccessByAddr(uptr addr, StackFrameAccess *access); + bool AddrIsInStack(StackDescriptor *stack, uptr addr) { + return addr >= stack->stack_bottom && addr < stack->stack_top; + } + + StackDescriptor *CurrentStack() { + int local; + if (AddrIsInStack(previous_stack_, (uptr)&local)) { + return previous_stack_; + } else { + return next_stack_; + } + } + bool AddrIsInStack(uptr addr) { - return addr >= stack_bottom_ && addr < stack_top_; + return AddrIsInStack(CurrentStack(), addr); } void DeleteFakeStack(int tid) { @@ -130,11 +160,17 @@ AsanThreadContext *context_; thread_callback_t start_routine_; void *arg_; - uptr stack_top_; - uptr stack_bottom_; - // stack_size_ == stack_top_ - stack_bottom_; - // It needs to be set in a async-signal-safe manner. - uptr stack_size_; + + // We have three stack descriptors for async-safe stack change. New stack + // information is written to temp_stack_. Then previous_stack_ is made to + // point to the same descriptor that next_stack_ does. Finally, temp_stack_ + // is assigned to next_stack_. The result is that at any time either + // previous_stack_ or next_stack_ contain the correct stack information. + StackDescriptor stacks_[3]; + StackDescriptor* temp_stack_; + StackDescriptor* next_stack_; + StackDescriptor* previous_stack_; + uptr tls_begin_; uptr tls_end_; DTLS *dtls_; Index: lib/asan/asan_thread.cc =================================================================== --- lib/asan/asan_thread.cc +++ lib/asan/asan_thread.cc @@ -150,17 +150,20 @@ } void AsanThread::Init() { + temp_stack_ = &stacks_[0]; + next_stack_ = &stacks_[1]; + previous_stack_ = &stacks_[2]; fake_stack_ = nullptr; // Will be initialized lazily if needed. CHECK_EQ(this->stack_size(), 0U); SetThreadStackAndTls(); CHECK_GT(this->stack_size(), 0U); - CHECK(AddrIsInMem(stack_bottom_)); - CHECK(AddrIsInMem(stack_top_ - 1)); + CHECK(AddrIsInMem(stack_bottom())); + CHECK(AddrIsInMem(stack_top() - 1)); ClearShadowForThreadStackAndTLS(); int local = 0; VReport(1, "T%d: stack [%p,%p) size 0x%zx; local=%p\n", tid(), - (void *)stack_bottom_, (void *)stack_top_, stack_top_ - stack_bottom_, - &local); + (void *)stack_bottom(), (void *)stack_top(), + stack_top() - stack_bottom(), &local); } thread_return_t AsanThread::ThreadStart( @@ -195,9 +198,11 @@ void AsanThread::SetThreadStackAndTls() { uptr tls_size = 0; - GetThreadStackAndTls(tid() == 0, &stack_bottom_, &stack_size_, &tls_begin_, - &tls_size); - stack_top_ = stack_bottom_ + stack_size_; + GetThreadStackAndTls(tid() == 0, &next_stack_->stack_bottom, + &next_stack_->stack_size, &tls_begin_, &tls_size); + next_stack_->stack_top = next_stack_->stack_bottom + next_stack_->stack_size; + previous_stack_->stack_top = previous_stack_->stack_bottom = 0; + previous_stack_->stack_size = 0; tls_end_ = tls_begin_ + tls_size; dtls_ = DTLS_Get(); @@ -206,7 +211,7 @@ } void AsanThread::ClearShadowForThreadStackAndTLS() { - PoisonShadow(stack_bottom_, stack_top_ - stack_bottom_, 0); + PoisonShadow(stack_bottom(), stack_top() - stack_bottom(), 0); if (tls_begin_ != tls_end_) PoisonShadow(tls_begin_, tls_end_ - tls_begin_, 0); } Index: lib/asan/asan_win.cc =================================================================== --- lib/asan/asan_win.cc +++ lib/asan/asan_win.cc @@ -190,6 +190,10 @@ UNIMPLEMENTED(); } +void WriteContextStack(void *context, uptr stack, uptr ssize) { + UNIMPLEMENTED(); +} + void AsanOnDeadlySignal(int, void *siginfo, void *context) { UNIMPLEMENTED(); } Index: test/asan/TestCases/Linux/swapcontext_test.cc =================================================================== --- test/asan/TestCases/Linux/swapcontext_test.cc +++ test/asan/TestCases/Linux/swapcontext_test.cc @@ -17,16 +17,31 @@ const int kStackSize = 1 << 20; +volatile int force_write; + __attribute__((noinline)) void Throw() { + char buf[1024]; + for (int i = 0; i < 1024; i++) { + buf[i] = force_write; + } throw 1; } __attribute__((noinline)) +void Func() { + int buf[4 * 1024]; + for (int i = 0; i < 4 * 1024; i++) { + buf[i] = force_write; + } +} + +__attribute__((noinline)) void ThrowAndCatch() { try { Throw(); } catch(int a) { + Func(); printf("ThrowAndCatch: %d\n", a); } } Index: test/lsan/TestCases/swapcontext.cc =================================================================== --- test/lsan/TestCases/swapcontext.cc +++ test/lsan/TestCases/swapcontext.cc @@ -1,8 +1,4 @@ -// We can't unwind stack if we're running coroutines on heap-allocated -// memory. Make sure we don't report these leaks. - // RUN: %clangxx_lsan %s -o %t -// RUN: %run %t 2>&1 // RUN: not %run %t foo 2>&1 | FileCheck %s #include