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) 51 return; 52 53 LongJump(env); 54 _exit(1); 55 } 56 57 void 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 77 void 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 do 86 // something. 87 // (c) Jump to another function which will then jump back to the main function 88 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 117 int 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 149 ucontext_t orig_huge_stack_context; 150 ucontext_t child_huge_stack_context; 151 152 // There used to be a limitation for stack unpoisoning (size <= 4Mb), check that it's gone. 153 const int kHugeStackSize = 1 << 23; 154 155 void 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 167 void 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 188 void 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 197 void handler(int sig) { CallNoReturn(); } 198 199 int main(int argc, char **argv) { 200 // CHECK: WARNING: ASan doesn't fully support makecontext/swapcontext 201 // CHECK-NOT: ASan is ignoring requested __asan_handle_no_return 202 RunHugeStack(); 203 204 // set up a signal that will spam and trigger __asan_handle_no_return at 205 // tricky moments 206 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_return 227 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]] 524288 231 ret += Run(argc - 1, 1, stack); 232 // LOOPCHECK: Child stack: [[CHILD_STACK:0x[0-9a-f]*]] 233 // LOOPCHECK: Main context from: [[CHILD_STACK]] 524288 234 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]] 524288 238 // LOOPCHECK: Main context from: [[NEXT_CHILD_STACK]] 524288 239 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 passed 246 printf("Test passed\n"); 247 248 delete[] heap; 249 delete[] next_child_stack; 250 251 return ret; 252 } 253