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