Home | History | Annotate | Download | only in Linux
      1 // Check that ASan plays well with annotated makecontext/swapcontext.
      2 
      3 // RUN: %clangxx_asan -lpthread -O0 %s -o %t && %run %t 2>&1 | FileCheck %s
      4 // RUN: %clangxx_asan -lpthread -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
      5 // RUN: %clangxx_asan -lpthread -O2 %s -o %t && %run %t 2>&1 | FileCheck %s
      6 // RUN: %clangxx_asan -lpthread -O3 %s -o %t && %run %t 2>&1 | FileCheck %s
      7 //
      8 // This test is too subtle to try on non-x86 arch for now.
      9 // REQUIRES: x86_64-supported-target,i386-supported-target
     10 
     11 #include <pthread.h>
     12 #include <setjmp.h>
     13 #include <stdio.h>
     14 #include <sys/time.h>
     15 #include <ucontext.h>
     16 #include <unistd.h>
     17 
     18 #include <sanitizer/common_interface_defs.h>
     19 
     20 ucontext_t orig_context;
     21 ucontext_t child_context;
     22 ucontext_t next_child_context;
     23 
     24 char *next_child_stack;
     25 
     26 const int kStackSize = 1 << 20;
     27 
     28 void *main_thread_stack;
     29 size_t main_thread_stacksize;
     30 
     31 __attribute__((noinline, noreturn)) void LongJump(jmp_buf env) {
     32   longjmp(env, 1);
     33   _exit(1);
     34 }
     35 
     36 // Simulate __asan_handle_no_return().
     37 __attribute__((noinline)) void CallNoReturn() {
     38   jmp_buf env;
     39   if (setjmp(env) != 0) return;
     40 
     41   LongJump(env);
     42   _exit(1);
     43 }
     44 
     45 void NextChild() {
     46   CallNoReturn();
     47   __sanitizer_finish_switch_fiber();
     48 
     49   char x[32] = {0};  // Stack gets poisoned.
     50   printf("NextChild: %p\n", x);
     51 
     52   CallNoReturn();
     53 
     54   __sanitizer_start_switch_fiber(main_thread_stack, main_thread_stacksize);
     55   CallNoReturn();
     56   if (swapcontext(&next_child_context, &orig_context) < 0) {
     57     perror("swapcontext");
     58     _exit(1);
     59   }
     60 }
     61 
     62 void Child(int mode) {
     63   CallNoReturn();
     64   __sanitizer_finish_switch_fiber();
     65   char x[32] = {0};  // Stack gets poisoned.
     66   printf("Child: %p\n", x);
     67   CallNoReturn();
     68   // (a) Do nothing, just return to parent function.
     69   // (b) Jump into the original function. Stack remains poisoned unless we do
     70   //     something.
     71   // (c) Jump to another function which will then jump back to the main function
     72   if (mode == 0) {
     73     __sanitizer_start_switch_fiber(main_thread_stack, main_thread_stacksize);
     74     CallNoReturn();
     75   } else if (mode == 1) {
     76     __sanitizer_start_switch_fiber(main_thread_stack, main_thread_stacksize);
     77     CallNoReturn();
     78     if (swapcontext(&child_context, &orig_context) < 0) {
     79       perror("swapcontext");
     80       _exit(1);
     81     }
     82   } else if (mode == 2) {
     83     getcontext(&next_child_context);
     84     next_child_context.uc_stack.ss_sp = next_child_stack;
     85     next_child_context.uc_stack.ss_size = kStackSize / 2;
     86     makecontext(&next_child_context, (void (*)())NextChild, 0);
     87     __sanitizer_start_switch_fiber(next_child_context.uc_stack.ss_sp,
     88                                    next_child_context.uc_stack.ss_size);
     89     CallNoReturn();
     90     if (swapcontext(&child_context, &next_child_context) < 0) {
     91       perror("swapcontext");
     92       _exit(1);
     93     }
     94   }
     95 }
     96 
     97 int Run(int arg, int mode, char *child_stack) {
     98   printf("Child stack: %p\n", child_stack);
     99   // Setup child context.
    100   getcontext(&child_context);
    101   child_context.uc_stack.ss_sp = child_stack;
    102   child_context.uc_stack.ss_size = kStackSize / 2;
    103   if (mode == 0) {
    104     child_context.uc_link = &orig_context;
    105   }
    106   makecontext(&child_context, (void (*)())Child, 1, mode);
    107   CallNoReturn();
    108   __sanitizer_start_switch_fiber(child_context.uc_stack.ss_sp,
    109                                  child_context.uc_stack.ss_size);
    110   CallNoReturn();
    111   if (swapcontext(&orig_context, &child_context) < 0) {
    112     perror("swapcontext");
    113     _exit(1);
    114   }
    115   CallNoReturn();
    116   __sanitizer_finish_switch_fiber();
    117   CallNoReturn();
    118 
    119   // Touch childs's stack to make sure it's unpoisoned.
    120   for (int i = 0; i < kStackSize; i++) {
    121     child_stack[i] = i;
    122   }
    123   return child_stack[arg];
    124 }
    125 
    126 void handler(int sig) { CallNoReturn(); }
    127 
    128 void InitStackBounds() {
    129   pthread_attr_t attr;
    130   pthread_attr_init(&attr);
    131   pthread_getattr_np(pthread_self(), &attr);
    132   pthread_attr_getstack(&attr, &main_thread_stack, &main_thread_stacksize);
    133   pthread_attr_destroy(&attr);
    134 }
    135 
    136 int main(int argc, char **argv) {
    137   InitStackBounds();
    138 
    139   // set up a signal that will spam and trigger __asan_handle_no_return at
    140   // tricky moments
    141   struct sigaction act = {};
    142   act.sa_handler = &handler;
    143   if (sigaction(SIGPROF, &act, 0)) {
    144     perror("sigaction");
    145     _exit(1);
    146   }
    147 
    148   itimerval t;
    149   t.it_interval.tv_sec = 0;
    150   t.it_interval.tv_usec = 10;
    151   t.it_value = t.it_interval;
    152   if (setitimer(ITIMER_PROF, &t, 0)) {
    153     perror("setitimer");
    154     _exit(1);
    155   }
    156 
    157   char *heap = new char[kStackSize + 1];
    158   next_child_stack = new char[kStackSize + 1];
    159   char stack[kStackSize + 1];
    160   // CHECK: WARNING: ASan doesn't fully support makecontext/swapcontext
    161   int ret = 0;
    162   // CHECK-NOT: ASan is ignoring requested __asan_handle_no_return
    163   for (unsigned int i = 0; i < 30; ++i) {
    164     ret += Run(argc - 1, 0, stack);
    165     ret += Run(argc - 1, 1, stack);
    166     ret += Run(argc - 1, 2, stack);
    167     ret += Run(argc - 1, 0, heap);
    168     ret += Run(argc - 1, 1, heap);
    169     ret += Run(argc - 1, 2, heap);
    170   }
    171   // CHECK: Test passed
    172   printf("Test passed\n");
    173 
    174   delete[] heap;
    175   delete[] next_child_stack;
    176 
    177   return ret;
    178 }
    179