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 does not support swapcontext.
16 // REQUIRES: x86-target-arch && !android
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 
LongJump(jmp_buf env)42 __attribute__((noinline, noreturn)) void LongJump(jmp_buf env) {
43   longjmp(env, 1);
44   _exit(1);
45 }
46 
47 // Simulate __asan_handle_no_return().
CallNoReturn()48 __attribute__((noinline)) void CallNoReturn() {
49   jmp_buf env;
50   if (setjmp(env) != 0) return;
51 
52   LongJump(env);
53   _exit(1);
54 }
55 
NextChild()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 
Child(int mode)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 
Run(int arg,int mode,char * child_stack)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 
handler(int sig)155 void handler(int sig) { CallNoReturn(); }
156 
main(int argc,char ** argv)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