| // RUN: %clangxx_tsan -O0 %s -o %t && %run %t 2>&1 | FileCheck %s |
| // RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s |
| |
| #include "test.h" |
| #include <setjmp.h> |
| |
| __attribute__((noinline)) void throws_int() { |
| throw 42; |
| } |
| |
| __attribute__((noinline)) void callee_throws() { |
| try { |
| throws_int(); |
| } catch (int) { |
| fprintf(stderr, "callee_throws caught exception\n"); |
| } |
| } |
| |
| __attribute__((noinline)) void throws_catches_rethrows() { |
| try { |
| throws_int(); |
| } catch (int) { |
| fprintf(stderr, "throws_catches_rethrows caught exception\n"); |
| throw; |
| } |
| } |
| |
| __attribute__((noinline)) void callee_rethrows() { |
| try { |
| throws_catches_rethrows(); |
| } catch (int) { |
| fprintf(stderr, "callee_rethrows caught exception\n"); |
| } |
| } |
| |
| __attribute__((noinline)) void throws_and_catches() { |
| try { |
| throws_int(); |
| } catch (int) { |
| fprintf(stderr, "throws_and_catches caught exception\n"); |
| } |
| } |
| |
| __attribute__((noinline)) void nested_try() { |
| try { |
| try { |
| throws_int(); |
| } catch (double) { |
| fprintf(stderr, "nested_try inner block caught exception\n"); |
| } |
| } catch (int) { |
| fprintf(stderr, "nested_try outer block caught exception\n"); |
| } |
| } |
| |
| __attribute__((noinline)) void nested_try2() { |
| try { |
| try { |
| throws_int(); |
| } catch (int) { |
| fprintf(stderr, "nested_try inner block caught exception\n"); |
| } |
| } catch (double) { |
| fprintf(stderr, "nested_try outer block caught exception\n"); |
| } |
| } |
| |
| class ClassWithDestructor { |
| public: |
| ClassWithDestructor() { |
| fprintf(stderr, "ClassWithDestructor\n"); |
| } |
| ~ClassWithDestructor() { |
| fprintf(stderr, "~ClassWithDestructor\n"); |
| } |
| }; |
| |
| __attribute__((noinline)) void local_object_then_throw() { |
| ClassWithDestructor obj; |
| throws_int(); |
| } |
| |
| __attribute__((noinline)) void cpp_object_with_destructor() { |
| try { |
| local_object_then_throw(); |
| } catch (int) { |
| fprintf(stderr, "cpp_object_with_destructor caught exception\n"); |
| } |
| } |
| |
| __attribute__((noinline)) void recursive_call(long n) { |
| if (n > 0) { |
| recursive_call(n - 1); |
| } else { |
| throws_int(); |
| } |
| } |
| |
| __attribute__((noinline)) void multiframe_unwind() { |
| try { |
| recursive_call(5); |
| } catch (int) { |
| fprintf(stderr, "multiframe_unwind caught exception\n"); |
| } |
| } |
| |
| __attribute__((noinline)) void longjmp_unwind() { |
| jmp_buf env; |
| int i = setjmp(env); |
| if (i != 0) { |
| fprintf(stderr, "longjmp_unwind jumped\n"); |
| return; |
| } |
| |
| try { |
| longjmp(env, 42); |
| } catch (int) { |
| fprintf(stderr, "longjmp_unwind caught exception\n"); |
| } |
| } |
| |
| __attribute__((noinline)) void recursive_call_longjmp(jmp_buf env, long n) { |
| if (n > 0) { |
| recursive_call_longjmp(env, n - 1); |
| } else { |
| longjmp(env, 42); |
| } |
| } |
| |
| __attribute__((noinline)) void longjmp_unwind_multiple_frames() { |
| jmp_buf env; |
| int i = setjmp(env); |
| if (i != 0) { |
| fprintf(stderr, "longjmp_unwind_multiple_frames jumped\n"); |
| return; |
| } |
| |
| try { |
| recursive_call_longjmp(env, 5); |
| } catch (int) { |
| fprintf(stderr, "longjmp_unwind_multiple_frames caught exception\n"); |
| } |
| } |
| |
| #define CHECK_SHADOW_STACK(val) \ |
| fprintf(stderr, (val == __tsan_testonly_shadow_stack_current_size() \ |
| ? "OK.\n" \ |
| : "Shadow stack leak!\n")); |
| |
| int main(int argc, const char * argv[]) { |
| fprintf(stderr, "Hello, World!\n"); |
| unsigned long shadow_stack_size = __tsan_testonly_shadow_stack_current_size(); |
| |
| throws_and_catches(); |
| CHECK_SHADOW_STACK(shadow_stack_size); |
| |
| callee_throws(); |
| CHECK_SHADOW_STACK(shadow_stack_size); |
| |
| callee_rethrows(); |
| CHECK_SHADOW_STACK(shadow_stack_size); |
| |
| nested_try(); |
| CHECK_SHADOW_STACK(shadow_stack_size); |
| |
| nested_try2(); |
| CHECK_SHADOW_STACK(shadow_stack_size); |
| |
| cpp_object_with_destructor(); |
| CHECK_SHADOW_STACK(shadow_stack_size); |
| |
| multiframe_unwind(); |
| CHECK_SHADOW_STACK(shadow_stack_size); |
| |
| longjmp_unwind(); |
| CHECK_SHADOW_STACK(shadow_stack_size); |
| |
| longjmp_unwind_multiple_frames(); |
| CHECK_SHADOW_STACK(shadow_stack_size); |
| |
| return 0; |
| } |
| |
| // CHECK: Hello, World! |
| // CHECK-NOT: Shadow stack leak |