1 // Check that ASan plays well with easy cases of makecontext/swapcontext.
2 
3 // RUN: %clangxx_asan -O0 %s -o %t && %run %t 2>&1 | FileCheck %s
4 // RUN: %clangxx_asan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
5 // RUN: %clangxx_asan -O2 %s -o %t && %run %t 2>&1 | FileCheck %s
6 // RUN: %clangxx_asan -O3 %s -o %t && %run %t 2>&1 | FileCheck %s
7 //
8 // This test is too sublte to try on non-x86 arch for now.
9 // Android and musl do not support swapcontext.
10 // REQUIRES: x86-target-arch && glibc-2.27
11 
12 #include <assert.h>
13 #include <memory.h>
14 #include <stdio.h>
15 #include <ucontext.h>
16 #include <unistd.h>
17 
18 ucontext_t orig_context;
19 ucontext_t child_context;
20 
21 const int kStackSize = 1 << 20;
22 
23 __attribute__((noinline))
Throw()24 void Throw() {
25   throw 1;
26 }
27 
28 __attribute__((noinline))
ThrowAndCatch()29 void ThrowAndCatch() {
30   try {
31     Throw();
32   } catch(int a) {
33     printf("ThrowAndCatch: %d\n", a);
34   }
35 }
36 
Child(int mode)37 void Child(int mode) {
38   assert(orig_context.uc_stack.ss_size == 0);
39   char x[32] = {0};  // Stack gets poisoned.
40   printf("Child: %p\n", x);
41   ThrowAndCatch();  // Simulate __asan_handle_no_return().
42   // (a) Do nothing, just return to parent function.
43   // (b) Jump into the original function. Stack remains poisoned unless we do
44   //     something.
45   if (mode == 1) {
46     if (swapcontext(&child_context, &orig_context) < 0) {
47       perror("swapcontext");
48       _exit(0);
49     }
50   }
51 }
52 
Run(int arg,int mode,char * child_stack)53 int Run(int arg, int mode, char *child_stack) {
54   printf("Child stack: %p\n", child_stack);
55   // Setup child context.
56   memset(&child_context, 0xff, sizeof(child_context));
57   getcontext(&child_context);
58   assert(child_context.uc_stack.ss_size == 0);
59   child_context.uc_stack.ss_sp = child_stack;
60   child_context.uc_stack.ss_size = kStackSize / 2;
61   if (mode == 0) {
62     child_context.uc_link = &orig_context;
63   }
64   makecontext(&child_context, (void (*)())Child, 1, mode);
65   memset(&orig_context, 0xff, sizeof(orig_context));
66   if (swapcontext(&orig_context, &child_context) < 0) {
67     perror("swapcontext");
68     return 0;
69   }
70   // Touch childs's stack to make sure it's unpoisoned.
71   for (int i = 0; i < kStackSize; i++) {
72     child_stack[i] = i;
73   }
74   return child_stack[arg];
75 }
76 
main(int argc,char ** argv)77 int main(int argc, char **argv) {
78   char stack[kStackSize + 1];
79   // CHECK: WARNING: ASan doesn't fully support makecontext/swapcontext
80   int ret = 0;
81   ret += Run(argc - 1, 0, stack);
82   printf("Test1 passed\n");
83   // CHECK: Test1 passed
84   ret += Run(argc - 1, 1, stack);
85   printf("Test2 passed\n");
86   // CHECK: Test2 passed
87   char *heap = new char[kStackSize + 1];
88   ret += Run(argc - 1, 0, heap);
89   printf("Test3 passed\n");
90   // CHECK: Test3 passed
91   ret += Run(argc - 1, 1, heap);
92   printf("Test4 passed\n");
93   // CHECK: Test4 passed
94 
95   delete [] heap;
96   return ret;
97 }
98