1 // Check that ASan plays well with annotated makecontext/swapcontext.
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
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
26 #include <sanitizer/common_interface_defs.h>
28 ucontext_t orig_context
;
29 ucontext_t child_context
;
30 ucontext_t next_child_context
;
32 char *next_child_stack
;
34 const int kStackSize
= 1 << 20;
36 const void *main_thread_stack
;
37 size_t main_thread_stacksize
;
39 const void *from_stack
;
40 size_t from_stacksize
;
42 __attribute__((noinline
, noreturn
)) void LongJump(jmp_buf env
) {
47 // Simulate __asan_handle_no_return().
48 __attribute__((noinline
)) void CallNoReturn() {
59 __sanitizer_finish_switch_fiber(nullptr, &from_stack
, &from_stacksize
);
61 printf("NextChild from: %p %zu\n", from_stack
, from_stacksize
);
63 char x
[32] = {0}; // Stack gets poisoned.
64 printf("NextChild: %p\n", x
);
68 __sanitizer_start_switch_fiber(nullptr, main_thread_stack
,
69 main_thread_stacksize
);
71 if (swapcontext(&next_child_context
, &orig_context
) < 0) {
72 perror("swapcontext");
77 void Child(int mode
) {
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
);
84 // (a) Do nothing, just return to parent function.
85 // (b) Jump into the original function. Stack remains poisoned unless we do
87 // (c) Jump to another function which will then jump back to the main function
89 __sanitizer_start_switch_fiber(nullptr, main_thread_stack
,
90 main_thread_stacksize
);
92 } else if (mode
== 1) {
93 __sanitizer_start_switch_fiber(nullptr, main_thread_stack
,
94 main_thread_stacksize
);
96 if (swapcontext(&child_context
, &orig_context
) < 0) {
97 perror("swapcontext");
100 } else if (mode
== 2) {
101 printf("NextChild stack: %p\n", next_child_stack
);
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
);
110 if (swapcontext(&child_context
, &next_child_context
) < 0) {
111 perror("swapcontext");
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;
124 child_context
.uc_link
= &orig_context
;
126 makecontext(&child_context
, (void (*)())Child
, 1, mode
);
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
);
132 if (swapcontext(&orig_context
, &child_context
) < 0) {
133 perror("swapcontext");
137 __sanitizer_finish_switch_fiber(fake_stack_save
, &from_stack
,
140 printf("Main context from: %p %zu\n", from_stack
, from_stacksize
);
142 // Touch childs's stack to make sure it's unpoisoned.
143 for (int i
= 0; i
< kStackSize
; i
++) {
146 return child_stack
[arg
];
149 ucontext_t orig_huge_stack_context
;
150 ucontext_t child_huge_stack_context
;
152 // There used to be a limitation for stack unpoisoning (size <= 4Mb), check that it's gone.
153 const int kHugeStackSize
= 1 << 23;
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");
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");
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
) {
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
);
197 void handler(int sig
) { CallNoReturn(); }
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
204 // set up a signal that will spam and trigger __asan_handle_no_return at
206 struct sigaction act
= {};
207 act
.sa_handler
= &handler
;
208 if (sigaction(SIGPROF
, &act
, 0)) {
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)) {
222 char *heap
= new char[kStackSize
+ 1];
223 next_child_stack
= new char[kStackSize
+ 1];
224 char stack
[kStackSize
+ 1];
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
);
245 // CHECK: Test passed
246 printf("Test passed\n");
249 delete[] next_child_stack
;