1 | // RUN: %clangxx_asan -O0 -pthread %s -o %t && %env_asan_opts=use_sigaltstack=0 %run not --crash %t 2>&1 | FileCheck %s |
2 | |
3 | // Check that fake stack does not discard frames on the main stack, when GC is |
4 | // triggered from high alt stack. |
5 | |
6 | // This test does not work on iOS simulator |
7 | // (https://github.com/llvm/llvm-project/issues/64942). |
8 | // UNSUPPORTED: iossim |
9 | |
10 | #include <algorithm> |
11 | #include <assert.h> |
12 | #include <csignal> |
13 | #include <cstdint> |
14 | #include <pthread.h> |
15 | #include <signal.h> |
16 | #include <stdio.h> |
17 | #include <stdlib.h> |
18 | #include <unistd.h> |
19 | |
20 | const size_t kStackSize = 0x100000; |
21 | |
22 | int *on_thread; |
23 | int *p; |
24 | |
25 | template <size_t N> void Fn() { |
26 | int t[N]; |
27 | p = t; |
28 | if constexpr (N > 1) |
29 | Fn<N - 1>(); |
30 | } |
31 | |
32 | static void Handler(int signo) { |
33 | fprintf(stderr, format: "Handler Frame:%p\n" , __builtin_frame_address(0)); |
34 | |
35 | // Trigger GC and create a lot of frame to reuse "Thread" frame if it was |
36 | // discarded. |
37 | for (int i = 0; i < 1000; ++i) |
38 | Fn<100>(); |
39 | // If we discarder and reused "Thread" frame, the next line will crash with |
40 | // false report. |
41 | *on_thread = 10; |
42 | fprintf(stderr, format: "SUCCESS\n" ); |
43 | // CHECK: SUCCESS |
44 | } |
45 | |
46 | void *Thread(void *arg) { |
47 | fprintf(stderr, format: "Thread Frame:%p\n" , __builtin_frame_address(0)); |
48 | stack_t stack = {}; |
49 | stack.ss_sp = arg; |
50 | stack.ss_flags = 0; |
51 | stack.ss_size = kStackSize; |
52 | assert(sigaltstack(&stack, nullptr) == 0); |
53 | |
54 | struct sigaction sa = {}; |
55 | sa.sa_handler = Handler; |
56 | sa.sa_flags = SA_ONSTACK; |
57 | sigaction(SIGABRT, act: &sa, oact: nullptr); |
58 | |
59 | // Store pointer to the local var, so we can access this frame from the signal |
60 | // handler when the frame is still alive. |
61 | int n; |
62 | on_thread = &n; |
63 | |
64 | // Abort should schedule FakeStack GC and call handler on alt stack. |
65 | abort(); |
66 | } |
67 | |
68 | int main(void) { |
69 | // Allocate main and alt stack for future thread. |
70 | void *main_stack; |
71 | void *alt_stack; |
72 | size_t const kPageSize = sysconf(_SC_PAGESIZE); |
73 | assert(posix_memalign(&main_stack, kPageSize, kStackSize) == 0); |
74 | assert(posix_memalign(&alt_stack, kPageSize, kStackSize) == 0); |
75 | |
76 | // Pick the lower stack as the main stack, as we want to trigger GC in |
77 | // FakeStack from alt stack in a such way that main stack is allocated below. |
78 | if ((uintptr_t)main_stack > (uintptr_t)alt_stack) |
79 | std::swap(a&: alt_stack, b&: main_stack); |
80 | |
81 | fprintf(stderr, format: "main_stack: %p-%p\n" , main_stack, |
82 | (char *)main_stack + kStackSize); |
83 | fprintf(stderr, format: "alt_stack: %p-%p\n" , alt_stack, |
84 | (char *)alt_stack + kStackSize); |
85 | |
86 | pthread_attr_t attr; |
87 | assert(pthread_attr_init(&attr) == 0); |
88 | assert(pthread_attr_setstack(&attr, main_stack, kStackSize) == 0); |
89 | |
90 | pthread_t tid; |
91 | assert(pthread_create(&tid, &attr, Thread, alt_stack) == 0); |
92 | |
93 | pthread_join(th: tid, thread_return: nullptr); |
94 | |
95 | free(ptr: main_stack); |
96 | free(ptr: alt_stack); |
97 | |
98 | return 0; |
99 | } |
100 | |