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
18ucontext_t orig_context;
19ucontext_t child_context;
20
21const int kStackSize = 1 << 20;
22
23__attribute__((noinline)) void Throw() { throw 1; }
24
25__attribute__((noinline)) void ThrowAndCatch() {
26 try {
27 Throw();
28 } catch (int a) {
29 printf(format: "ThrowAndCatch: %d\n", a);
30 }
31}
32
33void Child(int mode, int a, int b, int c) {
34 char x[32] = {0}; // Stack gets poisoned.
35 printf(format: "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(oucp: &child_context, ucp: &orig_context) < 0) {
45 perror(s: "swapcontext");
46 _exit(status: 0);
47 }
48 }
49}
50
51int Run(int arg, int mode, char *child_stack) {
52 printf(format: "Child stack: %p\n", child_stack);
53 // Setup child context.
54 getcontext(ucp: &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(ucp: &child_context, func: (void (*)())Child, argc: 4, mode, 'a', 'b', 'c');
61 if (swapcontext(oucp: &orig_context, ucp: &child_context) < 0) {
62 perror(s: "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
72ucontext_t poll_context;
73ucontext_t poller_context;
74
75void Poll() {
76 swapcontext(oucp: &poll_context, ucp: &poller_context);
77
78 {
79 char x = 0;
80 printf(format: "POLL: %p\n", &x);
81 }
82
83 swapcontext(oucp: &poll_context, ucp: &poller_context);
84}
85
86void DoRunPoll(char *poll_stack) {
87 getcontext(ucp: &poll_context);
88 poll_context.uc_stack.ss_sp = poll_stack;
89 poll_context.uc_stack.ss_size = kStackSize / 2;
90 makecontext(ucp: &poll_context, func: Poll, argc: 0);
91
92 getcontext(ucp: &poller_context);
93
94 swapcontext(oucp: &poller_context, ucp: &poll_context);
95 swapcontext(oucp: &poller_context, ucp: &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
103void 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
113int main(int argc, char **argv) {
114 char stack[kStackSize + 1];
115 int ret = 0;
116 ret += Run(arg: argc - 1, mode: 0, child_stack: stack);
117 ret += Run(arg: argc - 1, mode: 1, child_stack: stack);
118 char *heap = new char[kStackSize + 1];
119 ret += Run(arg: argc - 1, mode: 0, child_stack: heap);
120 ret += Run(arg: argc - 1, mode: 1, child_stack: heap);
121
122 RunPoll();
123 delete[] heap;
124 return ret;
125}
126

source code of compiler-rt/test/asan/TestCases/Linux/swapcontext_test.cpp