• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
LongJump(jmp_buf env)31 __attribute__((noinline, noreturn)) void LongJump(jmp_buf env) {
32   longjmp(env, 1);
33   _exit(1);
34 }
35 
36 // Simulate __asan_handle_no_return().
CallNoReturn()37 __attribute__((noinline)) void CallNoReturn() {
38   jmp_buf env;
39   if (setjmp(env) != 0) return;
40 
41   LongJump(env);
42   _exit(1);
43 }
44 
NextChild()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 
Child(int mode)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 
Run(int arg,int mode,char * child_stack)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 
handler(int sig)126 void handler(int sig) { CallNoReturn(); }
127 
InitStackBounds()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 
main(int argc,char ** argv)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