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
20const size_t kStackSize = 0x100000;
21
22int *on_thread;
23int *p;
24
25template <size_t N> void Fn() {
26 int t[N];
27 p = t;
28 if constexpr (N > 1)
29 Fn<N - 1>();
30}
31
32static 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
46void *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
68int 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

source code of compiler-rt/test/asan/TestCases/Posix/fake_stack_gc.cpp