1 // Check that ASan plays well with easy cases of makecontext/swapcontext.
2
3 // RUN: %clangxx_asan -O0 %s -o %t && %run %t
4 // RUN: %clangxx_asan -O3 %s -o %t && %run %t
5 // RUN: %clangxx_asan -fsanitize-address-use-after-return=never -O0 %s -o %t && %run %t
6 // RUN: %clangxx_asan -fsanitize-address-use-after-return=never -O3 %s -o %t && %run %t
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
Throw()23 __attribute__((noinline)) void Throw() { throw 1; }
24
ThrowAndCatch()25 __attribute__((noinline)) void ThrowAndCatch() {
26 try {
27 Throw();
28 } catch (int a) {
29 printf("ThrowAndCatch: %d\n", a);
30 }
31 }
32
Child(int mode,int a,int b,int c)33 void Child(int mode, int a, int b, int c) {
34 char x[32] = {0}; // Stack gets poisoned.
35 printf("Child: %d\n", x);
36 assert(a == 'a');
37 assert(b == 'b');
38 assert(c == 'c');
39 ThrowAndCatch(); // Simulate __asan_handle_no_return().
40 // (a) Do nothing, just return to parent function.
41 // (b) Jump into the original function. Stack remains poisoned unless we do
42 // something.
43 if (mode == 1) {
44 if (swapcontext(&child_context, &orig_context) < 0) {
45 perror("swapcontext");
46 _exit(0);
47 }
48 }
49 }
50
Run(int arg,int mode,char * child_stack)51 int Run(int arg, int mode, char *child_stack) {
52 printf("Child stack: %p\n", child_stack);
53 // Setup child context.
54 getcontext(&child_context);
55 child_context.uc_stack.ss_sp = child_stack;
56 child_context.uc_stack.ss_size = kStackSize / 2;
57 if (mode == 0) {
58 child_context.uc_link = &orig_context;
59 }
60 makecontext(&child_context, (void (*)())Child, 4, mode, 'a', 'b', 'c');
61 if (swapcontext(&orig_context, &child_context) < 0) {
62 perror("swapcontext");
63 return 0;
64 }
65 // Touch childs's stack to make sure it's unpoisoned.
66 for (int i = 0; i < kStackSize; i++) {
67 child_stack[i] = i;
68 }
69 return child_stack[arg];
70 }
71
72 ucontext_t poll_context;
73 ucontext_t poller_context;
74
Poll()75 void Poll() {
76 swapcontext(&poll_context, &poller_context);
77
78 {
79 char x = 0;
80 printf("POLL: %p\n", &x);
81 }
82
83 swapcontext(&poll_context, &poller_context);
84 }
85
DoRunPoll(char * poll_stack)86 void DoRunPoll(char *poll_stack) {
87 getcontext(&poll_context);
88 poll_context.uc_stack.ss_sp = poll_stack;
89 poll_context.uc_stack.ss_size = kStackSize / 2;
90 makecontext(&poll_context, Poll, 0);
91
92 getcontext(&poller_context);
93
94 swapcontext(&poller_context, &poll_context);
95 swapcontext(&poller_context, &poll_context);
96
97 // Touch poll's stack to make sure it's unpoisoned.
98 for (int i = 0; i < kStackSize; i++) {
99 poll_stack[i] = i;
100 }
101 }
102
RunPoll()103 void RunPoll() {
104 char *poll_stack = new char[kStackSize];
105
106 for (size_t i = 0; i < 2; ++i) {
107 DoRunPoll(poll_stack);
108 }
109
110 delete[] poll_stack;
111 }
112
main(int argc,char ** argv)113 int main(int argc, char **argv) {
114 char stack[kStackSize + 1];
115 int ret = 0;
116 ret += Run(argc - 1, 0, stack);
117 ret += Run(argc - 1, 1, stack);
118 char *heap = new char[kStackSize + 1];
119 ret += Run(argc - 1, 0, heap);
120 ret += Run(argc - 1, 1, heap);
121
122 RunPoll();
123 delete[] heap;
124 return ret;
125 }
126