1 // RUN: %clangxx_tsan -O0 %s -o %t && %run %t 2>&1 | FileCheck %s
2 // RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
3 
4 #include "test.h"
5 #include <setjmp.h>
6 
throws_int()7 __attribute__((noinline)) void throws_int() {
8   throw 42;
9 }
10 
callee_throws()11 __attribute__((noinline)) void callee_throws() {
12   try {
13     throws_int();
14   } catch (int) {  // NOLINT
15     fprintf(stderr, "callee_throws caught exception\n");
16   }
17 }
18 
throws_catches_rethrows()19 __attribute__((noinline)) void throws_catches_rethrows() {
20   try {
21     throws_int();
22   } catch (int) {  // NOLINT
23     fprintf(stderr, "throws_catches_rethrows caught exception\n");
24     throw;
25   }
26 }
27 
callee_rethrows()28 __attribute__((noinline)) void callee_rethrows() {
29   try {
30     throws_catches_rethrows();
31   } catch (int) {  // NOLINT
32     fprintf(stderr, "callee_rethrows caught exception\n");
33   }
34 }
35 
throws_and_catches()36 __attribute__((noinline)) void throws_and_catches() {
37   try {
38     throws_int();
39   } catch (int) {  // NOLINT
40     fprintf(stderr, "throws_and_catches caught exception\n");
41   }
42 }
43 
nested_try()44 __attribute__((noinline)) void nested_try() {
45   try {
46     try {
47       throws_int();
48     } catch (double) {  // NOLINT
49       fprintf(stderr, "nested_try inner block caught exception\n");
50     }
51   } catch (int) {  // NOLINT
52     fprintf(stderr, "nested_try outer block caught exception\n");
53   }
54 }
55 
nested_try2()56 __attribute__((noinline)) void nested_try2() {
57   try {
58     try {
59       throws_int();
60     } catch (int) {  // NOLINT
61       fprintf(stderr, "nested_try inner block caught exception\n");
62     }
63   } catch (double) {  // NOLINT
64     fprintf(stderr, "nested_try outer block caught exception\n");
65   }
66 }
67 
68 class ClassWithDestructor {
69  public:
ClassWithDestructor()70   ClassWithDestructor() {
71     fprintf(stderr, "ClassWithDestructor\n");
72   }
~ClassWithDestructor()73   ~ClassWithDestructor() {
74     fprintf(stderr, "~ClassWithDestructor\n");
75   }
76 };
77 
local_object_then_throw()78 __attribute__((noinline)) void local_object_then_throw() {
79   ClassWithDestructor obj;
80   throws_int();
81 }
82 
cpp_object_with_destructor()83 __attribute__((noinline)) void cpp_object_with_destructor() {
84   try {
85     local_object_then_throw();
86   } catch (int) {  // NOLINT
87     fprintf(stderr, "cpp_object_with_destructor caught exception\n");
88   }
89 }
90 
recursive_call(long n)91 __attribute__((noinline)) void recursive_call(long n) {
92   if (n > 0) {
93     recursive_call(n - 1);
94   } else {
95     throws_int();
96   }
97 }
98 
multiframe_unwind()99 __attribute__((noinline)) void multiframe_unwind() {
100   try {
101     recursive_call(5);
102   } catch (int) {  // NOLINT
103     fprintf(stderr, "multiframe_unwind caught exception\n");
104   }
105 }
106 
longjmp_unwind()107 __attribute__((noinline)) void longjmp_unwind() {
108   jmp_buf env;
109   int i = setjmp(env);
110   if (i != 0) {
111     fprintf(stderr, "longjmp_unwind jumped\n");
112     return;
113   }
114 
115   try {
116     longjmp(env, 42);
117   } catch (int) {  // NOLINT
118     fprintf(stderr, "longjmp_unwind caught exception\n");
119   }
120 }
121 
recursive_call_longjmp(jmp_buf env,long n)122 __attribute__((noinline)) void recursive_call_longjmp(jmp_buf env, long n) {
123   if (n > 0) {
124     recursive_call_longjmp(env, n - 1);
125   } else {
126     longjmp(env, 42);
127   }
128 }
129 
longjmp_unwind_multiple_frames()130 __attribute__((noinline)) void longjmp_unwind_multiple_frames() {
131   jmp_buf env;
132   int i = setjmp(env);
133   if (i != 0) {
134     fprintf(stderr, "longjmp_unwind_multiple_frames jumped\n");
135     return;
136   }
137 
138   try {
139     recursive_call_longjmp(env, 5);
140   } catch (int) {  // NOLINT
141     fprintf(stderr, "longjmp_unwind_multiple_frames caught exception\n");
142   }
143 }
144 
145 #define CHECK_SHADOW_STACK(val)                                                \
146   fprintf(stderr, (val == __tsan_testonly_shadow_stack_current_size()          \
147                        ? "OK.\n"                                               \
148                        : "Shadow stack leak!\n"));
149 
main(int argc,const char * argv[])150 int main(int argc, const char * argv[]) {
151   fprintf(stderr, "Hello, World!\n");
152   unsigned long shadow_stack_size = __tsan_testonly_shadow_stack_current_size();
153 
154   throws_and_catches();
155   CHECK_SHADOW_STACK(shadow_stack_size);
156 
157   callee_throws();
158   CHECK_SHADOW_STACK(shadow_stack_size);
159 
160   callee_rethrows();
161   CHECK_SHADOW_STACK(shadow_stack_size);
162 
163   nested_try();
164   CHECK_SHADOW_STACK(shadow_stack_size);
165 
166   nested_try2();
167   CHECK_SHADOW_STACK(shadow_stack_size);
168 
169   cpp_object_with_destructor();
170   CHECK_SHADOW_STACK(shadow_stack_size);
171 
172   multiframe_unwind();
173   CHECK_SHADOW_STACK(shadow_stack_size);
174 
175   longjmp_unwind();
176   CHECK_SHADOW_STACK(shadow_stack_size);
177 
178   longjmp_unwind_multiple_frames();
179   CHECK_SHADOW_STACK(shadow_stack_size);
180 
181   return 0;
182 }
183 
184 // CHECK: Hello, World!
185 // CHECK-NOT: Shadow stack leak
186