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_VARIANT_ADD(valid_modes,write)80 FIXTURE_VARIANT_ADD(valid_modes, write)
81 {
82 .mode = PR_SHADOW_STACK_WRITE,
83 };
84
FIXTURE_VARIANT_ADD(valid_modes,push)85 FIXTURE_VARIANT_ADD(valid_modes, push)
86 {
87 .mode = PR_SHADOW_STACK_PUSH,
88 };
89
FIXTURE_VARIANT_ADD(valid_modes,write_push)90 FIXTURE_VARIANT_ADD(valid_modes, write_push)
91 {
92 .mode = PR_SHADOW_STACK_WRITE | PR_SHADOW_STACK_PUSH,
93 };
94
FIXTURE_SETUP(valid_modes)95 FIXTURE_SETUP(valid_modes)
96 {
97 }
98
FIXTURE_TEARDOWN(valid_modes)99 FIXTURE_TEARDOWN(valid_modes)
100 {
101 }
102
103 /* We can set the mode at all */
TEST_F(valid_modes,set)104 TEST_F(valid_modes, set)
105 {
106 int ret;
107
108 ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
109 variant->mode);
110 ASSERT_EQ(ret, 0);
111
112 _exit(0);
113 }
114
115 /* Enabling, locking then disabling is rejected */
TEST_F(valid_modes,enable_lock_disable)116 TEST_F(valid_modes, enable_lock_disable)
117 {
118 unsigned long mode;
119 int ret;
120
121 ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
122 variant->mode);
123 ASSERT_EQ(ret, 0);
124
125 ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
126 ASSERT_EQ(ret, 0);
127 ASSERT_EQ(mode, variant->mode);
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, 0);
133 ASSERT_EQ(ret, -EPERM);
134
135 _exit(0);
136 }
137
138 /* Locking then enabling is rejected */
TEST_F(valid_modes,lock_enable)139 TEST_F(valid_modes, lock_enable)
140 {
141 unsigned long mode;
142 int ret;
143
144 ret = prctl(PR_LOCK_SHADOW_STACK_STATUS, variant->mode, 0, 0, 0);
145 ASSERT_EQ(ret, 0);
146
147 ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
148 variant->mode);
149 ASSERT_EQ(ret, -EPERM);
150
151 ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
152 ASSERT_EQ(ret, 0);
153 ASSERT_EQ(mode, 0);
154
155 _exit(0);
156 }
157
158 /* Locking then changing other modes is fine */
TEST_F(valid_modes,lock_enable_disable_others)159 TEST_F(valid_modes, lock_enable_disable_others)
160 {
161 unsigned long mode;
162 int ret;
163
164 ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
165 variant->mode);
166 ASSERT_EQ(ret, 0);
167
168 ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
169 ASSERT_EQ(ret, 0);
170 ASSERT_EQ(mode, variant->mode);
171
172 ret = prctl(PR_LOCK_SHADOW_STACK_STATUS, variant->mode, 0, 0, 0);
173 ASSERT_EQ(ret, 0);
174
175 ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
176 PR_SHADOW_STACK_ALL_MODES);
177 ASSERT_EQ(ret, 0);
178
179 ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
180 ASSERT_EQ(ret, 0);
181 ASSERT_EQ(mode, PR_SHADOW_STACK_ALL_MODES);
182
183 ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
184 variant->mode);
185 ASSERT_EQ(ret, 0);
186
187 ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
188 ASSERT_EQ(ret, 0);
189 ASSERT_EQ(mode, variant->mode);
190
191 _exit(0);
192 }
193
main(int argc,char ** argv)194 int main(int argc, char **argv)
195 {
196 unsigned long mode;
197 int ret;
198
199 if (!(getauxval(AT_HWCAP) & HWCAP_GCS))
200 ksft_exit_skip("SKIP GCS not supported\n");
201
202 ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
203 if (ret) {
204 ksft_print_msg("Failed to read GCS state: %d\n", ret);
205 return EXIT_FAILURE;
206 }
207
208 if (mode & PR_SHADOW_STACK_ENABLE) {
209 ksft_print_msg("GCS was enabled, test unsupported\n");
210 return KSFT_SKIP;
211 }
212
213 return test_harness_run(argc, argv);
214 }
215