1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (C) 2023 ARM Limited.
4 *
5 * Tests for GCS mode locking. These tests rely on both having GCS
6 * unconfigured on entry and on the kselftest harness running each
7 * test in a fork()ed process which will have it's own mode.
8 */
9
10 #include <limits.h>
11
12 #include <sys/auxv.h>
13 #include <sys/prctl.h>
14
15 #include <asm/hwcap.h>
16
17 #include "kselftest_harness.h"
18
19 #include "gcs-util.h"
20
21 #define my_syscall2(num, arg1, arg2) \
22 ({ \
23 register long _num __asm__ ("x8") = (num); \
24 register long _arg1 __asm__ ("x0") = (long)(arg1); \
25 register long _arg2 __asm__ ("x1") = (long)(arg2); \
26 register long _arg3 __asm__ ("x2") = 0; \
27 register long _arg4 __asm__ ("x3") = 0; \
28 register long _arg5 __asm__ ("x4") = 0; \
29 \
30 __asm__ volatile ( \
31 "svc #0\n" \
32 : "=r"(_arg1) \
33 : "r"(_arg1), "r"(_arg2), \
34 "r"(_arg3), "r"(_arg4), \
35 "r"(_arg5), "r"(_num) \
36 : "memory", "cc" \
37 ); \
38 _arg1; \
39 })
40
41 /* No mode bits are rejected for locking */
TEST(lock_all_modes)42 TEST(lock_all_modes)
43 {
44 int ret;
45
46 ret = prctl(PR_LOCK_SHADOW_STACK_STATUS, ULONG_MAX, 0, 0, 0);
47 ASSERT_EQ(ret, 0);
48 }
49
FIXTURE(valid_modes)50 FIXTURE(valid_modes)
51 {
52 };
53
FIXTURE_VARIANT(valid_modes)54 FIXTURE_VARIANT(valid_modes)
55 {
56 unsigned long mode;
57 };
58
FIXTURE_VARIANT_ADD(valid_modes,enable)59 FIXTURE_VARIANT_ADD(valid_modes, enable)
60 {
61 .mode = PR_SHADOW_STACK_ENABLE,
62 };
63
FIXTURE_VARIANT_ADD(valid_modes,enable_write)64 FIXTURE_VARIANT_ADD(valid_modes, enable_write)
65 {
66 .mode = PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_WRITE,
67 };
68
FIXTURE_VARIANT_ADD(valid_modes,enable_push)69 FIXTURE_VARIANT_ADD(valid_modes, enable_push)
70 {
71 .mode = PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_PUSH,
72 };
73
FIXTURE_VARIANT_ADD(valid_modes,enable_write_push)74 FIXTURE_VARIANT_ADD(valid_modes, enable_write_push)
75 {
76 .mode = PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_WRITE |
77 PR_SHADOW_STACK_PUSH,
78 };
79
FIXTURE_SETUP(valid_modes)80 FIXTURE_SETUP(valid_modes)
81 {
82 }
83
FIXTURE_TEARDOWN(valid_modes)84 FIXTURE_TEARDOWN(valid_modes)
85 {
86 }
87
88 /* We can set the mode at all */
TEST_F(valid_modes,set)89 TEST_F(valid_modes, set)
90 {
91 int ret;
92
93 ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
94 variant->mode);
95 ASSERT_EQ(ret, 0);
96
97 _exit(0);
98 }
99
100 /* Enabling, locking then disabling is rejected */
TEST_F(valid_modes,enable_lock_disable)101 TEST_F(valid_modes, enable_lock_disable)
102 {
103 unsigned long mode;
104 int ret;
105
106 ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
107 variant->mode);
108 ASSERT_EQ(ret, 0);
109
110 ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
111 ASSERT_EQ(ret, 0);
112 ASSERT_EQ(mode, variant->mode);
113
114 ret = prctl(PR_LOCK_SHADOW_STACK_STATUS, variant->mode, 0, 0, 0);
115 ASSERT_EQ(ret, 0);
116
117 ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS, 0);
118 ASSERT_EQ(ret, -EBUSY);
119
120 _exit(0);
121 }
122
123 /* Locking then enabling is rejected */
TEST_F(valid_modes,lock_enable)124 TEST_F(valid_modes, lock_enable)
125 {
126 unsigned long mode;
127 int ret;
128
129 ret = prctl(PR_LOCK_SHADOW_STACK_STATUS, variant->mode, 0, 0, 0);
130 ASSERT_EQ(ret, 0);
131
132 ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
133 variant->mode);
134 ASSERT_EQ(ret, -EBUSY);
135
136 ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
137 ASSERT_EQ(ret, 0);
138 ASSERT_EQ(mode, 0);
139
140 _exit(0);
141 }
142
143 /* Locking then changing other modes is fine */
TEST_F(valid_modes,lock_enable_disable_others)144 TEST_F(valid_modes, lock_enable_disable_others)
145 {
146 unsigned long mode;
147 int ret;
148
149 ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
150 variant->mode);
151 ASSERT_EQ(ret, 0);
152
153 ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
154 ASSERT_EQ(ret, 0);
155 ASSERT_EQ(mode, variant->mode);
156
157 ret = prctl(PR_LOCK_SHADOW_STACK_STATUS, variant->mode, 0, 0, 0);
158 ASSERT_EQ(ret, 0);
159
160 ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
161 PR_SHADOW_STACK_ALL_MODES);
162 ASSERT_EQ(ret, 0);
163
164 ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
165 ASSERT_EQ(ret, 0);
166 ASSERT_EQ(mode, PR_SHADOW_STACK_ALL_MODES);
167
168
169 ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
170 variant->mode);
171 ASSERT_EQ(ret, 0);
172
173 ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
174 ASSERT_EQ(ret, 0);
175 ASSERT_EQ(mode, variant->mode);
176
177 _exit(0);
178 }
179
main(int argc,char ** argv)180 int main(int argc, char **argv)
181 {
182 unsigned long mode;
183 int ret;
184
185 if (!(getauxval(AT_HWCAP) & HWCAP_GCS))
186 ksft_exit_skip("SKIP GCS not supported\n");
187
188 ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
189 if (ret) {
190 ksft_print_msg("Failed to read GCS state: %d\n", ret);
191 return EXIT_FAILURE;
192 }
193
194 if (mode & PR_SHADOW_STACK_ENABLE) {
195 ksft_print_msg("GCS was enabled, test unsupported\n");
196 return KSFT_SKIP;
197 }
198
199 return test_harness_run(argc, argv);
200 }
201