1 // -*- C++ -*-
2 //===----------------------------------------------------------------------===//
3 //
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //
8 //===----------------------------------------------------------------------===//
9 
10 // Ensure that libunwind doesn't crash on invalid info; the Linux aarch64
11 // sigreturn frame check would previously attempt to access invalid memory in
12 // this scenario.
13 // REQUIRES: linux && (target={{aarch64-.+}} || target={{x86_64-.+}})
14 
15 // GCC doesn't support __attribute__((naked)) on AArch64.
16 // UNSUPPORTED: gcc
17 
18 // Inline assembly is incompatible with MSAN.
19 // UNSUPPORTED: msan
20 
21 #undef NDEBUG
22 #include <assert.h>
23 #include <libunwind.h>
24 #include <stdio.h>
25 
26 __attribute__((naked)) void bad_unwind_info() {
27 #if defined(__aarch64__)
28   __asm__("// not using 0 because unwinder was already resilient to that\n"
29           "mov     x8, #4\n"
30           "stp     x30, x8, [sp, #-16]!\n"
31           ".cfi_def_cfa_offset 16\n"
32           "// purposely use incorrect offset for x30\n"
33           ".cfi_offset x30, -8\n"
34           "bl      stepper\n"
35           "ldr     x30, [sp], #16\n"
36           ".cfi_def_cfa_offset 0\n"
37           ".cfi_restore x30\n"
38           "ret\n");
39 #elif defined(__x86_64__)
40   __asm__("pushq   %rbx\n"
41           ".cfi_def_cfa_offset 16\n"
42           "movq    8(%rsp), %rbx\n"
43           "# purposely corrupt return value on stack\n"
44           "movq    $4, 8(%rsp)\n"
45           "callq   stepper\n"
46           "movq    %rbx, 8(%rsp)\n"
47           "popq    %rbx\n"
48           ".cfi_def_cfa_offset 8\n"
49           "ret\n");
50 #else
51 #error This test is only supported on aarch64 or x86-64
52 #endif
53 }
54 
55 extern "C" void stepper() {
56   unw_cursor_t cursor;
57   unw_context_t uc;
58   unw_getcontext(&uc);
59   unw_init_local(&cursor, &uc);
60   // stepping to bad_unwind_info should succeed
61   assert(unw_step(&cursor) > 0);
62   // stepping past bad_unwind_info should fail but not crash
63   assert(unw_step(&cursor) <= 0);
64 }
65 
66 int main() { bad_unwind_info(); }
67