1#include <asm/hwcap.h>
2#include <stdbool.h>
3#include <sys/auxv.h>
4#include <sys/prctl.h>
5
6#ifndef HWCAP_GCS
7#define HWCAP_GCS (1UL << 32)
8#endif
9
10#define PR_GET_SHADOW_STACK_STATUS 74
11#define PR_SET_SHADOW_STACK_STATUS 75
12#define PR_LOCK_SHADOW_STACK_STATUS 76
13
14#define PR_SHADOW_STACK_ENABLE (1UL << 0)
15#define PR_SHADOW_STACK_WRITE (1UL << 1)
16#define PR_SHADOW_STACK_PUSH (1UL << 2)
17
18#define PRCTL_SYSCALL_NO 167
19
20// Once we enable GCS, we cannot return from the function that made the syscall
21// to enable it. This is because the control stack is empty, there is no valid
22// address for us to return to. So for the initial enable we must use inline asm
23// instead of the libc's prctl wrapper function.
24#define my_prctl(option, arg2, arg3, arg4, arg5) \
25 ({ \
26 register unsigned long x0 __asm__("x0") = option; \
27 register unsigned long x1 __asm__("x1") = arg2; \
28 register unsigned long x2 __asm__("x2") = arg3; \
29 register unsigned long x3 __asm__("x3") = arg4; \
30 register unsigned long x4 __asm__("x4") = arg5; \
31 register unsigned long x8 __asm__("x8") = PRCTL_SYSCALL_NO; \
32 __asm__ __volatile__("svc #0\n" \
33 : "=r"(x0) \
34 : "r"(x0), "r"(x1), "r"(x2), "r"(x3), "r"(x4), \
35 "r"(x8) \
36 : "cc", "memory"); \
37 })
38
39unsigned long get_gcs_status() {
40 unsigned long mode = 0;
41 prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
42 return mode;
43}
44
45extern void _start();
46bool change_gcs_config(bool enable) {
47 // The test unlocks and disables all features (excluding the main enable bit)
48 // before calling this expression. Enable them again.
49 unsigned long new_status =
50 enable | PR_SHADOW_STACK_PUSH | PR_SHADOW_STACK_WRITE;
51
52 if (enable) {
53 // We would not be able to return from prctl().
54 my_prctl(PR_SET_SHADOW_STACK_STATUS, new_status, 0, 0, 0);
55
56 // This is a stack, so we must push in reverse order to the pops we want to
57 // have later. So push the return of __lldb_expr (_start), then the return
58 // address of this function (__lldb_expr).
59 __asm__ __volatile__("sys #3, C7, C7, #0, %0\n" // gcspushm _start
60 "sys #3, C7, C7, #0, x30\n" // gcspushm x30
61 :
62 : "r"(_start));
63 } else {
64 if (prctl(PR_SET_SHADOW_STACK_STATUS, new_status, 0, 0, 0) != 0)
65 return false;
66 }
67
68 // Turn back on all locks.
69 if (prctl(PR_LOCK_SHADOW_STACK_STATUS, ~(0UL), 0, 0, 0) != 0)
70 return false;
71
72 return true;
73}
74
75void gcs_signal() {
76 // If we enabled GCS manually, then we could just return from main to generate
77 // a signal. However, if the C library enabled it, then we'd just exit
78 // normally. Assume the latter, and try to return to some bogus address to
79 // generate the signal.
80 __asm__ __volatile__(
81 // Corrupt the link register. This could be many numbers but 16 is a
82 // nicely aligned value that is unlikely to result in a fault because the
83 // PC is misaligned, which would hide the GCS fault.
84 "add x30, x30, #10\n"
85 "ret\n");
86}
87
88// These functions are used to observe gcspr_el0 changing as we enter them, and
89// the fault we cause by changing its value. Also used to check expression
90// eval can handle function calls.
91int test_func2() { return 99; }
92
93int test_func() { return test_func2(); }
94
95int main() {
96 if (!(getauxval(AT_HWCAP) & HWCAP_GCS))
97 return 1;
98
99 unsigned long mode = get_gcs_status();
100 if ((mode & 1) == 0) {
101 // If GCS wasn't already enabled by the C library, enable it.
102 my_prctl(PR_SET_SHADOW_STACK_STATUS, PR_SHADOW_STACK_ENABLE, 0, 0, 0);
103 // From this point on, we cannot return from main without faulting because
104 // the return address from main, and every function before that, is not on
105 // the guarded control stack.
106 }
107
108 // By now we should have one memory region where the GCS is stored.
109
110 // For register read/write tests.
111 volatile int i = test_func();
112
113 // If this was a register test, we would have disabled GCS during the
114 // test_func call. We cannot re-enable it from ptrace so skip this part in
115 // this case.
116 mode = get_gcs_status();
117 if ((mode & 1) == 1)
118 gcs_signal(); // Set break point at this line.
119
120 return 0;
121}
122

source code of lldb/test/API/linux/aarch64/gcs/main.c