[compiler-rt] r273260 - [asan] add primitives that allow coroutine implementations

Dmitry Vyukov via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 21 13:39:58 PDT 2016


Philippe prepared r273288 to fix it.

On Tue, Jun 21, 2016 at 8:28 PM, Mike Aizatsky <aizatsky at google.com> wrote:
> Dmitry,
>
> I think this breaks sanitizer-windows build:
>
> http://lab.llvm.org:8011/builders/sanitizer-windows/builds/24305/steps/run%20tests/logs/stdio
>
> First build with the problem:
> http://lab.llvm.org:8011/builders/sanitizer-windows/builds/24286
>
>
> On Tue, Jun 21, 2016 at 5:36 AM Dmitry Vyukov via llvm-commits
> <llvm-commits at lists.llvm.org> wrote:
>>
>> Author: dvyukov
>> Date: Tue Jun 21 07:29:18 2016
>> New Revision: 273260
>>
>> URL: http://llvm.org/viewvc/llvm-project?rev=273260&view=rev
>> Log:
>> [asan] add primitives that allow coroutine implementations
>>
>> This patch adds the __sanitizer_start_switch_fiber and
>> __sanitizer_finish_switch_fiber methods inspired from what can be found
>> here
>>
>> https://github.com/facebook/folly/commit/2ea64dd24946cbc9f3f4ac3f6c6b98a486c56e73
>> .
>>
>> These methods are needed when the compiled software needs to implement
>> coroutines, fibers or the like. Without a way to annotate them, when the
>> program
>> jumps to a stack that is not the thread stack, __asan_handle_no_return
>> shows a
>> warning about that, and the fake stack mechanism may free fake frames that
>> are
>> still in use.
>>
>> Author: blastrock (Philippe Daouadi)
>> Reviewed in http://reviews.llvm.org/D20913
>>
>>
>> Added:
>>     compiler-rt/trunk/test/asan/TestCases/Linux/swapcontext_annotation.cc
>> Modified:
>>     compiler-rt/trunk/include/sanitizer/common_interface_defs.h
>>     compiler-rt/trunk/lib/asan/asan_thread.cc
>>     compiler-rt/trunk/lib/asan/asan_thread.h
>>
>> Modified: compiler-rt/trunk/include/sanitizer/common_interface_defs.h
>> URL:
>> http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/include/sanitizer/common_interface_defs.h?rev=273260&r1=273259&r2=273260&view=diff
>>
>> ==============================================================================
>> --- compiler-rt/trunk/include/sanitizer/common_interface_defs.h (original)
>> +++ compiler-rt/trunk/include/sanitizer/common_interface_defs.h Tue Jun 21
>> 07:29:18 2016
>> @@ -139,6 +139,26 @@ extern "C" {
>>    // `top_percent` should be between 1 and 100.
>>    // Experimental feature currently available only with asan on
>> Linux/x86_64.
>>    void __sanitizer_print_memory_profile(size_t top_percent);
>> +
>> +  // Fiber annotation interface.
>> +  // Before switching to a different stack, one must call
>> +  // __sanitizer_start_switch_fiber with a pointer to the bottom of the
>> +  // destination stack and its size. When code starts running on the new
>> stack,
>> +  // it must call __sanitizer_finish_switch_fiber to finalize the switch.
>> +  // The start_switch function takes a void** to store the current fake
>> stack if
>> +  // there is one (it is needed when detect_stack_use_after_return is
>> enabled).
>> +  // When restoring a stack, this pointer must be given to the
>> finish_switch
>> +  // function. In most cases, this void* can be stored on the stack just
>> before
>> +  // switching.  When leaving a fiber definitely, null must be passed as
>> first
>> +  // argument to the start_switch function so that the fake stack is
>> destroyed.
>> +  // If you do not want support for stack use-after-return detection, you
>> can
>> +  // always pass null to these two functions.
>> +  // Note that the fake stack mechanism is disabled during fiber switch,
>> so if a
>> +  // signal callback runs during the switch, it will not benefit from the
>> stack
>> +  // use-after-return detection.
>> +  void __sanitizer_start_switch_fiber(void **fake_stack_save,
>> +                                      const void *bottom, size_t size);
>> +  void __sanitizer_finish_switch_fiber(void *fake_stack_save);
>>  #ifdef __cplusplus
>>  }  // extern "C"
>>  #endif
>>
>> Modified: compiler-rt/trunk/lib/asan/asan_thread.cc
>> URL:
>> http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/asan/asan_thread.cc?rev=273260&r1=273259&r2=273260&view=diff
>>
>> ==============================================================================
>> --- compiler-rt/trunk/lib/asan/asan_thread.cc (original)
>> +++ compiler-rt/trunk/lib/asan/asan_thread.cc Tue Jun 21 07:29:18 2016
>> @@ -120,6 +120,71 @@ void AsanThread::Destroy() {
>>    DTLS_Destroy();
>>  }
>>
>> +void AsanThread::StartSwitchFiber(FakeStack **fake_stack_save, uptr
>> bottom,
>> +                                  uptr size) {
>> +  if (atomic_load(&stack_switching_, memory_order_relaxed)) {
>> +    Report("ERROR: starting fiber switch while in fiber switch\n");
>> +    Die();
>> +  }
>> +
>> +  next_stack_bottom_ = bottom;
>> +  next_stack_top_ = bottom + size;
>> +  atomic_store(&stack_switching_, 1, memory_order_release);
>> +
>> +  FakeStack *current_fake_stack = fake_stack_;
>> +  if (fake_stack_save)
>> +    *fake_stack_save = fake_stack_;
>> +  fake_stack_ = nullptr;
>> +  SetTLSFakeStack(nullptr);
>> +  // if fake_stack_save is null, the fiber will die, delete the fakestack
>> +  if (!fake_stack_save && current_fake_stack)
>> +    current_fake_stack->Destroy(this->tid());
>> +}
>> +
>> +void AsanThread::FinishSwitchFiber(FakeStack *fake_stack_save) {
>> +  if (!atomic_load(&stack_switching_, memory_order_relaxed)) {
>> +    Report("ERROR: finishing a fiber switch that has not started\n");
>> +    Die();
>> +  }
>> +
>> +  if (fake_stack_save) {
>> +    SetTLSFakeStack(fake_stack_save);
>> +    fake_stack_ = fake_stack_save;
>> +  }
>> +
>> +  stack_bottom_ = next_stack_bottom_;
>> +  stack_top_ = next_stack_top_;
>> +  atomic_store(&stack_switching_, 0, memory_order_release);
>> +  next_stack_top_ = 0;
>> +  next_stack_bottom_ = 0;
>> +}
>> +
>> +inline AsanThread::StackBounds AsanThread::GetStackBounds() const {
>> +  if (!atomic_load(&stack_switching_, memory_order_acquire))
>> +    return StackBounds{stack_bottom_, stack_top_};  // NOLINT
>> +  char local;
>> +  const uptr cur_stack = (uptr)&local;
>> +  // Note: need to check next stack first, because FinishSwitchFiber
>> +  // may be in process of overwriting stack_top_/bottom_. But in such
>> case
>> +  // we are already on the next stack.
>> +  if (cur_stack >= next_stack_bottom_ && cur_stack < next_stack_top_)
>> +    return StackBounds{next_stack_bottom_, next_stack_top_};  // NOLINT
>> +  return StackBounds{stack_bottom_, stack_top_};              // NOLINT
>> +}
>> +
>> +uptr AsanThread::stack_top() {
>> +  return GetStackBounds().top;
>> +}
>> +
>> +uptr AsanThread::stack_bottom() {
>> +  return GetStackBounds().bottom;
>> +}
>> +
>> +uptr AsanThread::stack_size() {
>> +  const auto bounds = GetStackBounds();
>> +  return bounds.top - bounds.bottom;
>> +}
>> +
>>  // 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() {
>> @@ -150,6 +215,8 @@ FakeStack *AsanThread::AsyncSignalSafeLa
>>  }
>>
>>  void AsanThread::Init() {
>> +  next_stack_top_ = next_stack_bottom_ = 0;
>> +  atomic_store(&stack_switching_, false, memory_order_release);
>>    fake_stack_ = nullptr;  // Will be initialized lazily if needed.
>>    CHECK_EQ(this->stack_size(), 0U);
>>    SetThreadStackAndTls();
>> @@ -195,9 +262,10 @@ thread_return_t AsanThread::ThreadStart(
>>
>>  void AsanThread::SetThreadStackAndTls() {
>>    uptr tls_size = 0;
>> -  GetThreadStackAndTls(tid() == 0, &stack_bottom_, &stack_size_,
>> &tls_begin_,
>> -                       &tls_size);
>> -  stack_top_ = stack_bottom_ + stack_size_;
>> +  uptr stack_size = 0;
>> +  GetThreadStackAndTls(tid() == 0, const_cast<uptr *>(&stack_bottom_),
>> +                       const_cast<uptr *>(&stack_size), &tls_begin_,
>> &tls_size);
>> +  stack_top_ = stack_bottom_ + stack_size;
>>    tls_end_ = tls_begin_ + tls_size;
>>    dtls_ = DTLS_Get();
>>
>> @@ -250,6 +318,11 @@ bool AsanThread::GetStackFrameAccessByAd
>>    return true;
>>  }
>>
>> +bool AsanThread::AddrIsInStack(uptr addr) {
>> +  const auto bounds = GetStackBounds();
>> +  return addr >= bounds.bottom && addr < bounds.top;
>> +}
>> +
>>  static bool ThreadStackContainsAddress(ThreadContextBase *tctx_base,
>>                                         void *addr) {
>>    AsanThreadContext *tctx = static_cast<AsanThreadContext*>(tctx_base);
>> @@ -357,3 +430,29 @@ void EnsureMainThreadIDIsCorrect() {
>>    __asan::EnsureMainThreadIDIsCorrect();
>>  }
>>  } // namespace __lsan
>> +
>> +// ---------------------- Interface ---------------- {{{1
>> +using namespace __asan;  // NOLINT
>> +
>> +extern "C" {
>> +SANITIZER_INTERFACE_ATTRIBUTE
>> +void __sanitizer_start_switch_fiber(void **fakestacksave, const void
>> *bottom,
>> +                                    uptr size) {
>> +  AsanThread *t = GetCurrentThread();
>> +  if (!t) {
>> +    VReport(1, "__asan_start_switch_fiber called from unknown thread\n");
>> +    return;
>> +  }
>> +  t->StartSwitchFiber((FakeStack**)fakestacksave, (uptr)bottom, size);
>> +}
>> +
>> +SANITIZER_INTERFACE_ATTRIBUTE
>> +void __sanitizer_finish_switch_fiber(void* fakestack) {
>> +  AsanThread *t = GetCurrentThread();
>> +  if (!t) {
>> +    VReport(1, "__asan_finish_switch_fiber called from unknown
>> thread\n");
>> +    return;
>> +  }
>> +  t->FinishSwitchFiber((FakeStack*)fakestack);
>> +}
>> +}
>>
>> Modified: compiler-rt/trunk/lib/asan/asan_thread.h
>> URL:
>> http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/asan/asan_thread.h?rev=273260&r1=273259&r2=273260&view=diff
>>
>> ==============================================================================
>> --- compiler-rt/trunk/lib/asan/asan_thread.h (original)
>> +++ compiler-rt/trunk/lib/asan/asan_thread.h Tue Jun 21 07:29:18 2016
>> @@ -66,9 +66,9 @@ class AsanThread {
>>    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_; }
>> +  uptr stack_top();
>> +  uptr stack_bottom();
>> +  uptr stack_size();
>>    uptr tls_begin() { return tls_begin_; }
>>    uptr tls_end() { return tls_end_; }
>>    DTLS *dtls() { return dtls_; }
>> @@ -83,9 +83,7 @@ class AsanThread {
>>    };
>>    bool GetStackFrameAccessByAddr(uptr addr, StackFrameAccess *access);
>>
>> -  bool AddrIsInStack(uptr addr) {
>> -    return addr >= stack_bottom_ && addr < stack_top_;
>> -  }
>> +  bool AddrIsInStack(uptr addr);
>>
>>    void DeleteFakeStack(int tid) {
>>      if (!fake_stack_) return;
>> @@ -95,13 +93,19 @@ class AsanThread {
>>      t->Destroy(tid);
>>    }
>>
>> +  void StartSwitchFiber(FakeStack **fake_stack_save, uptr bottom, uptr
>> size);
>> +  void FinishSwitchFiber(FakeStack *fake_stack_save);
>> +
>>    bool has_fake_stack() {
>> -    return (reinterpret_cast<uptr>(fake_stack_) > 1);
>> +    return !atomic_load(&stack_switching_, memory_order_relaxed) &&
>> +           (reinterpret_cast<uptr>(fake_stack_) > 1);
>>    }
>>
>>    FakeStack *fake_stack() {
>>      if (!__asan_option_detect_stack_use_after_return)
>>        return nullptr;
>> +    if (atomic_load(&stack_switching_, memory_order_relaxed))
>> +      return nullptr;
>>      if (!has_fake_stack())
>>        return AsyncSignalSafeLazyInitFakeStack();
>>      return fake_stack_;
>> @@ -127,14 +131,24 @@ class AsanThread {
>>    void ClearShadowForThreadStackAndTLS();
>>    FakeStack *AsyncSignalSafeLazyInitFakeStack();
>>
>> +  struct StackBounds {
>> +    uptr bottom;
>> +    uptr top;
>> +  };
>> +  StackBounds GetStackBounds() const;
>> +
>>    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_;
>> +  // these variables are used when the thread is about to switch stack
>> +  uptr next_stack_top_;
>> +  uptr next_stack_bottom_;
>> +  // true if switching is in progress
>> +  atomic_uint8_t stack_switching_;
>> +
>>    uptr tls_begin_;
>>    uptr tls_end_;
>>    DTLS *dtls_;
>>
>> Added:
>> compiler-rt/trunk/test/asan/TestCases/Linux/swapcontext_annotation.cc
>> URL:
>> http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/asan/TestCases/Linux/swapcontext_annotation.cc?rev=273260&view=auto
>>
>> ==============================================================================
>> --- compiler-rt/trunk/test/asan/TestCases/Linux/swapcontext_annotation.cc
>> (added)
>> +++ compiler-rt/trunk/test/asan/TestCases/Linux/swapcontext_annotation.cc
>> Tue Jun 21 07:29:18 2016
>> @@ -0,0 +1,178 @@
>> +// Check that ASan plays well with annotated makecontext/swapcontext.
>> +
>> +// RUN: %clangxx_asan -lpthread -O0 %s -o %t && %run %t 2>&1 | FileCheck
>> %s
>> +// RUN: %clangxx_asan -lpthread -O1 %s -o %t && %run %t 2>&1 | FileCheck
>> %s
>> +// RUN: %clangxx_asan -lpthread -O2 %s -o %t && %run %t 2>&1 | FileCheck
>> %s
>> +// RUN: %clangxx_asan -lpthread -O3 %s -o %t && %run %t 2>&1 | FileCheck
>> %s
>> +//
>> +// This test is too subtle to try on non-x86 arch for now.
>> +// REQUIRES: x86_64-supported-target,i386-supported-target
>> +
>> +#include <pthread.h>
>> +#include <setjmp.h>
>> +#include <stdio.h>
>> +#include <sys/time.h>
>> +#include <ucontext.h>
>> +#include <unistd.h>
>> +
>> +#include <sanitizer/common_interface_defs.h>
>> +
>> +ucontext_t orig_context;
>> +ucontext_t child_context;
>> +ucontext_t next_child_context;
>> +
>> +char *next_child_stack;
>> +
>> +const int kStackSize = 1 << 20;
>> +
>> +void *main_thread_stack;
>> +size_t main_thread_stacksize;
>> +
>> +__attribute__((noinline, noreturn)) void LongJump(jmp_buf env) {
>> +  longjmp(env, 1);
>> +  _exit(1);
>> +}
>> +
>> +// Simulate __asan_handle_no_return().
>> +__attribute__((noinline)) void CallNoReturn() {
>> +  jmp_buf env;
>> +  if (setjmp(env) != 0) return;
>> +
>> +  LongJump(env);
>> +  _exit(1);
>> +}
>> +
>> +void NextChild() {
>> +  CallNoReturn();
>> +  __sanitizer_finish_switch_fiber();
>> +
>> +  char x[32] = {0};  // Stack gets poisoned.
>> +  printf("NextChild: %p\n", x);
>> +
>> +  CallNoReturn();
>> +
>> +  __sanitizer_start_switch_fiber(main_thread_stack,
>> main_thread_stacksize);
>> +  CallNoReturn();
>> +  if (swapcontext(&next_child_context, &orig_context) < 0) {
>> +    perror("swapcontext");
>> +    _exit(1);
>> +  }
>> +}
>> +
>> +void Child(int mode) {
>> +  CallNoReturn();
>> +  __sanitizer_finish_switch_fiber();
>> +  char x[32] = {0};  // Stack gets poisoned.
>> +  printf("Child: %p\n", x);
>> +  CallNoReturn();
>> +  // (a) Do nothing, just return to parent function.
>> +  // (b) Jump into the original function. Stack remains poisoned unless
>> we do
>> +  //     something.
>> +  // (c) Jump to another function which will then jump back to the main
>> function
>> +  if (mode == 0) {
>> +    __sanitizer_start_switch_fiber(main_thread_stack,
>> main_thread_stacksize);
>> +    CallNoReturn();
>> +  } else if (mode == 1) {
>> +    __sanitizer_start_switch_fiber(main_thread_stack,
>> main_thread_stacksize);
>> +    CallNoReturn();
>> +    if (swapcontext(&child_context, &orig_context) < 0) {
>> +      perror("swapcontext");
>> +      _exit(1);
>> +    }
>> +  } else if (mode == 2) {
>> +    getcontext(&next_child_context);
>> +    next_child_context.uc_stack.ss_sp = next_child_stack;
>> +    next_child_context.uc_stack.ss_size = kStackSize / 2;
>> +    makecontext(&next_child_context, (void (*)())NextChild, 0);
>> +    __sanitizer_start_switch_fiber(next_child_context.uc_stack.ss_sp,
>> +                                   next_child_context.uc_stack.ss_size);
>> +    CallNoReturn();
>> +    if (swapcontext(&child_context, &next_child_context) < 0) {
>> +      perror("swapcontext");
>> +      _exit(1);
>> +    }
>> +  }
>> +}
>> +
>> +int Run(int arg, int mode, char *child_stack) {
>> +  printf("Child stack: %p\n", child_stack);
>> +  // Setup child context.
>> +  getcontext(&child_context);
>> +  child_context.uc_stack.ss_sp = child_stack;
>> +  child_context.uc_stack.ss_size = kStackSize / 2;
>> +  if (mode == 0) {
>> +    child_context.uc_link = &orig_context;
>> +  }
>> +  makecontext(&child_context, (void (*)())Child, 1, mode);
>> +  CallNoReturn();
>> +  __sanitizer_start_switch_fiber(child_context.uc_stack.ss_sp,
>> +                                 child_context.uc_stack.ss_size);
>> +  CallNoReturn();
>> +  if (swapcontext(&orig_context, &child_context) < 0) {
>> +    perror("swapcontext");
>> +    _exit(1);
>> +  }
>> +  CallNoReturn();
>> +  __sanitizer_finish_switch_fiber();
>> +  CallNoReturn();
>> +
>> +  // Touch childs's stack to make sure it's unpoisoned.
>> +  for (int i = 0; i < kStackSize; i++) {
>> +    child_stack[i] = i;
>> +  }
>> +  return child_stack[arg];
>> +}
>> +
>> +void handler(int sig) { CallNoReturn(); }
>> +
>> +void InitStackBounds() {
>> +  pthread_attr_t attr;
>> +  pthread_attr_init(&attr);
>> +  pthread_getattr_np(pthread_self(), &attr);
>> +  pthread_attr_getstack(&attr, &main_thread_stack,
>> &main_thread_stacksize);
>> +  pthread_attr_destroy(&attr);
>> +}
>> +
>> +int main(int argc, char **argv) {
>> +  InitStackBounds();
>> +
>> +  // set up a signal that will spam and trigger __asan_handle_no_return
>> at
>> +  // tricky moments
>> +  struct sigaction act = {};
>> +  act.sa_handler = &handler;
>> +  if (sigaction(SIGPROF, &act, 0)) {
>> +    perror("sigaction");
>> +    _exit(1);
>> +  }
>> +
>> +  itimerval t;
>> +  t.it_interval.tv_sec = 0;
>> +  t.it_interval.tv_usec = 10;
>> +  t.it_value = t.it_interval;
>> +  if (setitimer(ITIMER_PROF, &t, 0)) {
>> +    perror("setitimer");
>> +    _exit(1);
>> +  }
>> +
>> +  char *heap = new char[kStackSize + 1];
>> +  next_child_stack = new char[kStackSize + 1];
>> +  char stack[kStackSize + 1];
>> +  // CHECK: WARNING: ASan doesn't fully support makecontext/swapcontext
>> +  int ret = 0;
>> +  // CHECK-NOT: ASan is ignoring requested __asan_handle_no_return
>> +  for (unsigned int i = 0; i < 30; ++i) {
>> +    ret += Run(argc - 1, 0, stack);
>> +    ret += Run(argc - 1, 1, stack);
>> +    ret += Run(argc - 1, 2, stack);
>> +    ret += Run(argc - 1, 0, heap);
>> +    ret += Run(argc - 1, 1, heap);
>> +    ret += Run(argc - 1, 2, heap);
>> +  }
>> +  // CHECK: Test passed
>> +  printf("Test passed\n");
>> +
>> +  delete[] heap;
>> +  delete[] next_child_stack;
>> +
>> +  return ret;
>> +}
>>
>>
>> _______________________________________________
>> llvm-commits mailing list
>> llvm-commits at lists.llvm.org
>> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits
>
> --
> Mike
> Sent from phone


More information about the llvm-commits mailing list