File Index
Files and directories
Sign in
FILE

gcs-locking.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/arm64/gcs/gcs-locking.c C
  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 */
 42TEST(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
 50FIXTURE(valid_modes)
 51{
 52};
 53
 54FIXTURE_VARIANT(valid_modes)
 55{
 56	unsigned long mode;
 57};
 58
 59FIXTURE_VARIANT_ADD(valid_modes, enable)
 60{
 61	.mode = PR_SHADOW_STACK_ENABLE,
 62};
 63
 64FIXTURE_VARIANT_ADD(valid_modes, enable_write)
 65{
 66	.mode = PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_WRITE,
 67};
 68
 69FIXTURE_VARIANT_ADD(valid_modes, enable_push)
 70{
 71	.mode = PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_PUSH,
 72};
 73
 74FIXTURE_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
 80FIXTURE_SETUP(valid_modes)
 81{
 82}
 83
 84FIXTURE_TEARDOWN(valid_modes)
 85{
 86}
 87
 88/* We can set the mode at all */
 89TEST_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 */
101TEST_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 */
124TEST_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 */
144TEST_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	ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
169			  variant->mode);
170	ASSERT_EQ(ret, 0);
171
172	ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
173	ASSERT_EQ(ret, 0);
174	ASSERT_EQ(mode, variant->mode);
175
176	_exit(0);
177}
178
179int main(int argc, char **argv)
180{
181	unsigned long mode;
182	int ret;
183
184	if (!(getauxval(AT_HWCAP) & HWCAP_GCS))
185		ksft_exit_skip("SKIP GCS not supported\n");
186
187	ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
188	if (ret) {
189		ksft_print_msg("Failed to read GCS state: %d\n", ret);
190		return EXIT_FAILURE;
191	}
192
193	if (mode & PR_SHADOW_STACK_ENABLE) {
194		ksft_print_msg("GCS was enabled, test unsupported\n");
195		return KSFT_SKIP;
196	}
197
198	return test_harness_run(argc, argv);
199}
~/ Read-only file preview.