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 20ucontext_t orig_context; 21ucontext_t child_context; 22ucontext_t next_child_context; 23 24char *next_child_stack; 25 26const int kStackSize = 1 << 20; 27 28void *main_thread_stack; 29size_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 45void 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 62void 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 97int 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 126void handler(int sig) { CallNoReturn(); } 127 128void 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 136int 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