| 1 | // RUN: %clangxx_asan -O0 -pthread %s -o %t && %env_asan_opts=use_sigaltstack=0 not --crash %run %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 | assert(pthread_attr_destroy(&attr) == 0); |
| 93 | |
| 94 | pthread_join(th: tid, thread_return: nullptr); |
| 95 | |
| 96 | free(ptr: main_stack); |
| 97 | free(ptr: alt_stack); |
| 98 | |
| 99 | return 0; |
| 100 | } |
| 101 | |