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 %s 4 // RUN: %clangxx_asan -std=c++11 -lpthread -O1 %s -o %t && %run %t 2>&1 | FileCheck %s 5 // RUN: %clangxx_asan -std=c++11 -lpthread -O2 %s -o %t && %run %t 2>&1 | FileCheck %s 6 // RUN: %clangxx_asan -std=c++11 -lpthread -O3 %s -o %t && %run %t 2>&1 | FileCheck %s 7 // RUN: seq 60 | xargs -i -- grep LOOPCHECK %s > %t.checks 8 // RUN: %clangxx_asan -std=c++11 -lpthread -O0 %s -o %t && %run %t 2>&1 | FileCheck %t.checks --check-prefix LOOPCHECK 9 // RUN: %clangxx_asan -std=c++11 -lpthread -O1 %s -o %t && %run %t 2>&1 | FileCheck %t.checks --check-prefix LOOPCHECK 10 // RUN: %clangxx_asan -std=c++11 -lpthread -O2 %s -o %t && %run %t 2>&1 | FileCheck %t.checks --check-prefix LOOPCHECK 11 // RUN: %clangxx_asan -std=c++11 -lpthread -O3 %s -o %t && %run %t 2>&1 | FileCheck %t.checks --check-prefix LOOPCHECK 12 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.27 17 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 28 ucontext_t orig_context; 29 ucontext_t child_context; 30 ucontext_t next_child_context; 31 32 char *next_child_stack; 33 34 const int kStackSize = 1 << 20; 35 36 const void *main_thread_stack; 37 size_t main_thread_stacksize; 38 39 const void *from_stack; 40 size_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) return; 51 52 LongJump(env); 53 _exit(1); 54 } 55 56 void NextChild() { 57 CallNoReturn(); 58 __sanitizer_finish_switch_fiber(nullptr, &from_stack, &from_stacksize); 59 60 printf("NextChild from: %p %zu\n", from_stack, from_stacksize); 61 62 char x[32] = {0}; // Stack gets poisoned. 63 printf("NextChild: %p\n", x); 64 65 CallNoReturn(); 66 67 __sanitizer_start_switch_fiber(nullptr, 68 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 77 void Child(int mode) { 78 CallNoReturn(); 79 __sanitizer_finish_switch_fiber(nullptr, 80 &main_thread_stack, 81 &main_thread_stacksize); 82 char x[32] = {0}; // Stack gets poisoned. 83 printf("Child: %p\n", x); 84 CallNoReturn(); 85 // (a) Do nothing, just return to parent function. 86 // (b) Jump into the original function. Stack remains poisoned unless we do 87 // something. 88 // (c) Jump to another function which will then jump back to the main function 89 if (mode == 0) { 90 __sanitizer_start_switch_fiber(nullptr, 91 main_thread_stack, 92 main_thread_stacksize); 93 CallNoReturn(); 94 } else if (mode == 1) { 95 __sanitizer_start_switch_fiber(nullptr, 96 main_thread_stack, 97 main_thread_stacksize); 98 CallNoReturn(); 99 if (swapcontext(&child_context, &orig_context) < 0) { 100 perror("swapcontext"); 101 _exit(1); 102 } 103 } else if (mode == 2) { 104 printf("NextChild stack: %p\n", next_child_stack); 105 106 getcontext(&next_child_context); 107 next_child_context.uc_stack.ss_sp = next_child_stack; 108 next_child_context.uc_stack.ss_size = kStackSize / 2; 109 makecontext(&next_child_context, (void (*)())NextChild, 0); 110 __sanitizer_start_switch_fiber(nullptr, 111 next_child_context.uc_stack.ss_sp, 112 next_child_context.uc_stack.ss_size); 113 CallNoReturn(); 114 if (swapcontext(&child_context, &next_child_context) < 0) { 115 perror("swapcontext"); 116 _exit(1); 117 } 118 } 119 } 120 121 int Run(int arg, int mode, char *child_stack) { 122 printf("Child stack: %p\n", child_stack); 123 // Setup child context. 124 getcontext(&child_context); 125 child_context.uc_stack.ss_sp = child_stack; 126 child_context.uc_stack.ss_size = kStackSize / 2; 127 if (mode == 0) { 128 child_context.uc_link = &orig_context; 129 } 130 makecontext(&child_context, (void (*)())Child, 1, mode); 131 CallNoReturn(); 132 void* fake_stack_save; 133 __sanitizer_start_switch_fiber(&fake_stack_save, 134 child_context.uc_stack.ss_sp, 135 child_context.uc_stack.ss_size); 136 CallNoReturn(); 137 if (swapcontext(&orig_context, &child_context) < 0) { 138 perror("swapcontext"); 139 _exit(1); 140 } 141 CallNoReturn(); 142 __sanitizer_finish_switch_fiber(fake_stack_save, 143 &from_stack, 144 &from_stacksize); 145 CallNoReturn(); 146 printf("Main context from: %p %zu\n", from_stack, from_stacksize); 147 148 // Touch childs's stack to make sure it's unpoisoned. 149 for (int i = 0; i < kStackSize; i++) { 150 child_stack[i] = i; 151 } 152 return child_stack[arg]; 153 } 154 155 void handler(int sig) { CallNoReturn(); } 156 157 int main(int argc, char **argv) { 158 // set up a signal that will spam and trigger __asan_handle_no_return at 159 // tricky moments 160 struct sigaction act = {}; 161 act.sa_handler = &handler; 162 if (sigaction(SIGPROF, &act, 0)) { 163 perror("sigaction"); 164 _exit(1); 165 } 166 167 itimerval t; 168 t.it_interval.tv_sec = 0; 169 t.it_interval.tv_usec = 10; 170 t.it_value = t.it_interval; 171 if (setitimer(ITIMER_PROF, &t, 0)) { 172 perror("setitimer"); 173 _exit(1); 174 } 175 176 char *heap = new char[kStackSize + 1]; 177 next_child_stack = new char[kStackSize + 1]; 178 char stack[kStackSize + 1]; 179 // CHECK: WARNING: ASan doesn't fully support makecontext/swapcontext 180 int ret = 0; 181 // CHECK-NOT: ASan is ignoring requested __asan_handle_no_return 182 for (unsigned int i = 0; i < 30; ++i) { 183 ret += Run(argc - 1, 0, stack); 184 // LOOPCHECK: Child stack: [[CHILD_STACK:0x[0-9a-f]*]] 185 // LOOPCHECK: Main context from: [[CHILD_STACK]] 524288 186 ret += Run(argc - 1, 1, stack); 187 // LOOPCHECK: Child stack: [[CHILD_STACK:0x[0-9a-f]*]] 188 // LOOPCHECK: Main context from: [[CHILD_STACK]] 524288 189 ret += Run(argc - 1, 2, stack); 190 // LOOPCHECK: Child stack: [[CHILD_STACK:0x[0-9a-f]*]] 191 // LOOPCHECK: NextChild stack: [[NEXT_CHILD_STACK:0x[0-9a-f]*]] 192 // LOOPCHECK: NextChild from: [[CHILD_STACK]] 524288 193 // LOOPCHECK: Main context from: [[NEXT_CHILD_STACK]] 524288 194 ret += Run(argc - 1, 0, heap); 195 ret += Run(argc - 1, 1, heap); 196 ret += Run(argc - 1, 2, heap); 197 printf("Iteration %d passed\n", i); 198 } 199 200 // CHECK: Test passed 201 printf("Test passed\n"); 202 203 delete[] heap; 204 delete[] next_child_stack; 205 206 return ret; 207 } 208