brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.2 KiB · 5753f13 Raw
253 lines · cpp
1// Check that ASan plays well with annotated makecontext/swapcontext.2 3// RUN: %clangxx_asan -std=c++11 -lpthread -O0 %s -o %t && %run %t 2>&1 | FileCheck %s4// RUN: %clangxx_asan -std=c++11 -lpthread -O1 %s -o %t && %run %t 2>&1 | FileCheck %s5// RUN: %clangxx_asan -std=c++11 -lpthread -O2 %s -o %t && %run %t 2>&1 | FileCheck %s6// RUN: %clangxx_asan -std=c++11 -lpthread -O3 %s -o %t && %run %t 2>&1 | FileCheck %s7// RUN: seq 60 | xargs -i -- grep LOOPCHECK %s > %t.checks8// RUN: %clangxx_asan -std=c++11 -lpthread -O0 %s -o %t && %run %t 2>&1 | FileCheck %t.checks --check-prefix LOOPCHECK9// RUN: %clangxx_asan -std=c++11 -lpthread -O1 %s -o %t && %run %t 2>&1 | FileCheck %t.checks --check-prefix LOOPCHECK10// RUN: %clangxx_asan -std=c++11 -lpthread -O2 %s -o %t && %run %t 2>&1 | FileCheck %t.checks --check-prefix LOOPCHECK11// RUN: %clangxx_asan -std=c++11 -lpthread -O3 %s -o %t && %run %t 2>&1 | FileCheck %t.checks --check-prefix LOOPCHECK12 13//14// This test is too subtle to try on non-x86 arch for now.15// Android and musl do not support swapcontext.16// REQUIRES: x86-target-arch && glibc-2.2717 18#include <pthread.h>19#include <setjmp.h>20#include <signal.h>21#include <stdio.h>22#include <sys/time.h>23#include <ucontext.h>24#include <unistd.h>25 26#include <sanitizer/common_interface_defs.h>27 28ucontext_t orig_context;29ucontext_t child_context;30ucontext_t next_child_context;31 32char *next_child_stack;33 34const int kStackSize = 1 << 20;35 36const void *main_thread_stack;37size_t main_thread_stacksize;38 39const void *from_stack;40size_t from_stacksize;41 42__attribute__((noinline, noreturn)) void LongJump(jmp_buf env) {43  longjmp(env, 1);44  _exit(1);45}46 47// Simulate __asan_handle_no_return().48__attribute__((noinline)) void CallNoReturn() {49  jmp_buf env;50  if (setjmp(env) != 0)51    return;52 53  LongJump(env);54  _exit(1);55}56 57void NextChild() {58  CallNoReturn();59  __sanitizer_finish_switch_fiber(nullptr, &from_stack, &from_stacksize);60 61  printf("NextChild from: %p %zu\n", from_stack, from_stacksize);62 63  char x[32] = {0}; // Stack gets poisoned.64  printf("NextChild: %p\n", x);65 66  CallNoReturn();67 68  __sanitizer_start_switch_fiber(nullptr, main_thread_stack,69                                 main_thread_stacksize);70  CallNoReturn();71  if (swapcontext(&next_child_context, &orig_context) < 0) {72    perror("swapcontext");73    _exit(1);74  }75}76 77void Child(int mode) {78  CallNoReturn();79  __sanitizer_finish_switch_fiber(nullptr, &main_thread_stack,80                                  &main_thread_stacksize);81  char x[32] = {0}; // Stack gets poisoned.82  printf("Child: %p\n", x);83  CallNoReturn();84  // (a) Do nothing, just return to parent function.85  // (b) Jump into the original function. Stack remains poisoned unless we do86  //     something.87  // (c) Jump to another function which will then jump back to the main function88  if (mode == 0) {89    __sanitizer_start_switch_fiber(nullptr, main_thread_stack,90                                   main_thread_stacksize);91    CallNoReturn();92  } else if (mode == 1) {93    __sanitizer_start_switch_fiber(nullptr, main_thread_stack,94                                   main_thread_stacksize);95    CallNoReturn();96    if (swapcontext(&child_context, &orig_context) < 0) {97      perror("swapcontext");98      _exit(1);99    }100  } else if (mode == 2) {101    printf("NextChild stack: %p\n", next_child_stack);102 103    getcontext(&next_child_context);104    next_child_context.uc_stack.ss_sp = next_child_stack;105    next_child_context.uc_stack.ss_size = kStackSize / 2;106    makecontext(&next_child_context, (void (*)())NextChild, 0);107    __sanitizer_start_switch_fiber(nullptr, next_child_context.uc_stack.ss_sp,108                                   next_child_context.uc_stack.ss_size);109    CallNoReturn();110    if (swapcontext(&child_context, &next_child_context) < 0) {111      perror("swapcontext");112      _exit(1);113    }114  }115}116 117int Run(int arg, int mode, char *child_stack) {118  printf("Child stack: %p\n", child_stack);119  // Setup child context.120  getcontext(&child_context);121  child_context.uc_stack.ss_sp = child_stack;122  child_context.uc_stack.ss_size = kStackSize / 2;123  if (mode == 0) {124    child_context.uc_link = &orig_context;125  }126  makecontext(&child_context, (void (*)())Child, 1, mode);127  CallNoReturn();128  void *fake_stack_save;129  __sanitizer_start_switch_fiber(&fake_stack_save, child_context.uc_stack.ss_sp,130                                 child_context.uc_stack.ss_size);131  CallNoReturn();132  if (swapcontext(&orig_context, &child_context) < 0) {133    perror("swapcontext");134    _exit(1);135  }136  CallNoReturn();137  __sanitizer_finish_switch_fiber(fake_stack_save, &from_stack,138                                  &from_stacksize);139  CallNoReturn();140  printf("Main context from: %p %zu\n", from_stack, from_stacksize);141 142  // Touch childs's stack to make sure it's unpoisoned.143  for (int i = 0; i < kStackSize; i++) {144    child_stack[i] = i;145  }146  return child_stack[arg];147}148 149ucontext_t orig_huge_stack_context;150ucontext_t child_huge_stack_context;151 152// There used to be a limitation for stack unpoisoning (size <= 4Mb), check that it's gone.153const int kHugeStackSize = 1 << 23;154 155void ChildHugeStack() {156  __sanitizer_finish_switch_fiber(nullptr, &main_thread_stack,157                                  &main_thread_stacksize);158  char x[32] = {0}; // Stack gets poisoned.159  __sanitizer_start_switch_fiber(nullptr, main_thread_stack,160                                 main_thread_stacksize);161  if (swapcontext(&child_huge_stack_context, &orig_huge_stack_context) < 0) {162    perror("swapcontext");163    _exit(1);164  }165}166 167void DoRunHugeStack(char *child_stack) {168  getcontext(&child_huge_stack_context);169  child_huge_stack_context.uc_stack.ss_sp = child_stack;170  child_huge_stack_context.uc_stack.ss_size = kHugeStackSize;171  makecontext(&child_huge_stack_context, (void (*)())ChildHugeStack, 0);172  void *fake_stack_save;173  __sanitizer_start_switch_fiber(&fake_stack_save,174                                 child_huge_stack_context.uc_stack.ss_sp,175                                 child_huge_stack_context.uc_stack.ss_size);176  if (swapcontext(&orig_huge_stack_context, &child_huge_stack_context) < 0) {177    perror("swapcontext");178    _exit(1);179  }180  __sanitizer_finish_switch_fiber(181      fake_stack_save, (const void **)&child_huge_stack_context.uc_stack.ss_sp,182      &child_huge_stack_context.uc_stack.ss_size);183  for (int i = 0; i < kHugeStackSize; ++i) {184    child_stack[i] = i;185  }186}187 188void RunHugeStack() {189  const int run_offset = 1 << 14;190  char *heap = new char[kHugeStackSize + run_offset + 1];191  DoRunHugeStack(heap);192  DoRunHugeStack(heap + run_offset);193  DoRunHugeStack(heap);194  delete[] heap;195}196 197void handler(int sig) { CallNoReturn(); }198 199int main(int argc, char **argv) {200  // CHECK: WARNING: ASan doesn't fully support makecontext/swapcontext201  // CHECK-NOT: ASan is ignoring requested __asan_handle_no_return202  RunHugeStack();203 204  // set up a signal that will spam and trigger __asan_handle_no_return at205  // tricky moments206  struct sigaction act = {};207  act.sa_handler = &handler;208  if (sigaction(SIGPROF, &act, 0)) {209    perror("sigaction");210    _exit(1);211  }212 213  itimerval t;214  t.it_interval.tv_sec = 0;215  t.it_interval.tv_usec = 10;216  t.it_value = t.it_interval;217  if (setitimer(ITIMER_PROF, &t, 0)) {218    perror("setitimer");219    _exit(1);220  }221 222  char *heap = new char[kStackSize + 1];223  next_child_stack = new char[kStackSize + 1];224  char stack[kStackSize + 1];225  int ret = 0;226  // CHECK-NOT: ASan is ignoring requested __asan_handle_no_return227  for (unsigned int i = 0; i < 30; ++i) {228    ret += Run(argc - 1, 0, stack);229    // LOOPCHECK: Child stack: [[CHILD_STACK:0x[0-9a-f]*]]230    // LOOPCHECK: Main context from: [[CHILD_STACK]] 524288231    ret += Run(argc - 1, 1, stack);232    // LOOPCHECK: Child stack: [[CHILD_STACK:0x[0-9a-f]*]]233    // LOOPCHECK: Main context from: [[CHILD_STACK]] 524288234    ret += Run(argc - 1, 2, stack);235    // LOOPCHECK: Child stack: [[CHILD_STACK:0x[0-9a-f]*]]236    // LOOPCHECK: NextChild stack: [[NEXT_CHILD_STACK:0x[0-9a-f]*]]237    // LOOPCHECK: NextChild from: [[CHILD_STACK]] 524288238    // LOOPCHECK: Main context from: [[NEXT_CHILD_STACK]] 524288239    ret += Run(argc - 1, 0, heap);240    ret += Run(argc - 1, 1, heap);241    ret += Run(argc - 1, 2, heap);242    printf("Iteration %d passed\n", i);243  }244 245  // CHECK: Test passed246  printf("Test passed\n");247 248  delete[] heap;249  delete[] next_child_stack;250 251  return ret;252}253