xref: /linux/tools/testing/selftests/rseq/syscall_errors_test.c (revision 4f9786035f9e519db41375818e1d0b5f20da2f10)
1*3c27b408SMichael Jeanson // SPDX-License-Identifier: MIT
2*3c27b408SMichael Jeanson // SPDX-FileCopyrightText: 2024 Michael Jeanson <mjeanson@efficios.com>
3*3c27b408SMichael Jeanson 
4*3c27b408SMichael Jeanson #ifndef _GNU_SOURCE
5*3c27b408SMichael Jeanson #define _GNU_SOURCE
6*3c27b408SMichael Jeanson #endif
7*3c27b408SMichael Jeanson 
8*3c27b408SMichael Jeanson #include <assert.h>
9*3c27b408SMichael Jeanson #include <stdint.h>
10*3c27b408SMichael Jeanson #include <syscall.h>
11*3c27b408SMichael Jeanson #include <string.h>
12*3c27b408SMichael Jeanson #include <unistd.h>
13*3c27b408SMichael Jeanson 
14*3c27b408SMichael Jeanson #include "rseq.h"
15*3c27b408SMichael Jeanson 
16*3c27b408SMichael Jeanson static int sys_rseq(void *rseq_abi, uint32_t rseq_len,
17*3c27b408SMichael Jeanson 		    int flags, uint32_t sig)
18*3c27b408SMichael Jeanson {
19*3c27b408SMichael Jeanson 	return syscall(__NR_rseq, rseq_abi, rseq_len, flags, sig);
20*3c27b408SMichael Jeanson }
21*3c27b408SMichael Jeanson 
22*3c27b408SMichael Jeanson /*
23*3c27b408SMichael Jeanson  * Check the value of errno on some expected failures of the rseq syscall.
24*3c27b408SMichael Jeanson  */
25*3c27b408SMichael Jeanson 
26*3c27b408SMichael Jeanson int main(void)
27*3c27b408SMichael Jeanson {
28*3c27b408SMichael Jeanson 	struct rseq_abi *global_rseq = rseq_get_abi();
29*3c27b408SMichael Jeanson 	int ret;
30*3c27b408SMichael Jeanson 	int errno_copy;
31*3c27b408SMichael Jeanson 
32*3c27b408SMichael Jeanson 	if (!rseq_available()) {
33*3c27b408SMichael Jeanson 		fprintf(stderr, "rseq syscall unavailable");
34*3c27b408SMichael Jeanson 		goto error;
35*3c27b408SMichael Jeanson 	}
36*3c27b408SMichael Jeanson 
37*3c27b408SMichael Jeanson 	/* The current thread is NOT registered. */
38*3c27b408SMichael Jeanson 
39*3c27b408SMichael Jeanson 	/* EINVAL */
40*3c27b408SMichael Jeanson 	errno = 0;
41*3c27b408SMichael Jeanson 	ret = sys_rseq(global_rseq, 32, -1, RSEQ_SIG);
42*3c27b408SMichael Jeanson 	errno_copy = errno;
43*3c27b408SMichael Jeanson 	fprintf(stderr, "Registration with invalid flag fails with errno set to EINVAL (ret = %d, errno = %s)\n", ret, strerrorname_np(errno_copy));
44*3c27b408SMichael Jeanson 	if (ret == 0 || errno_copy != EINVAL)
45*3c27b408SMichael Jeanson 		goto error;
46*3c27b408SMichael Jeanson 
47*3c27b408SMichael Jeanson 	errno = 0;
48*3c27b408SMichael Jeanson 	ret = sys_rseq((char *) global_rseq + 1, 32, 0, RSEQ_SIG);
49*3c27b408SMichael Jeanson 	errno_copy = errno;
50*3c27b408SMichael Jeanson 	fprintf(stderr, "Registration with unaligned rseq_abi fails with errno set to EINVAL (ret = %d, errno = %s)\n", ret, strerrorname_np(errno_copy));
51*3c27b408SMichael Jeanson 	if (ret == 0 || errno_copy != EINVAL)
52*3c27b408SMichael Jeanson 		goto error;
53*3c27b408SMichael Jeanson 
54*3c27b408SMichael Jeanson 	errno = 0;
55*3c27b408SMichael Jeanson 	ret = sys_rseq(global_rseq, 31, 0, RSEQ_SIG);
56*3c27b408SMichael Jeanson 	errno_copy = errno;
57*3c27b408SMichael Jeanson 	fprintf(stderr, "Registration with invalid size fails with errno set to EINVAL (ret = %d, errno = %s)\n", ret, strerrorname_np(errno_copy));
58*3c27b408SMichael Jeanson 	if (ret == 0 || errno_copy != EINVAL)
59*3c27b408SMichael Jeanson 		goto error;
60*3c27b408SMichael Jeanson 
61*3c27b408SMichael Jeanson 
62*3c27b408SMichael Jeanson #if defined(__LP64__) && (!defined(__s390__) && !defined(__s390x__))
63*3c27b408SMichael Jeanson 	/*
64*3c27b408SMichael Jeanson 	 * We haven't found a reliable way to find an invalid address when
65*3c27b408SMichael Jeanson 	 * running a 32bit userspace on a 64bit kernel, so only run this test
66*3c27b408SMichael Jeanson 	 * on 64bit builds for the moment.
67*3c27b408SMichael Jeanson 	 *
68*3c27b408SMichael Jeanson 	 * Also exclude architectures that select
69*3c27b408SMichael Jeanson 	 * CONFIG_ALTERNATE_USER_ADDRESS_SPACE where the kernel and userspace
70*3c27b408SMichael Jeanson 	 * have their own address space and this failure can't happen.
71*3c27b408SMichael Jeanson 	 */
72*3c27b408SMichael Jeanson 
73*3c27b408SMichael Jeanson 	/* EFAULT */
74*3c27b408SMichael Jeanson 	errno = 0;
75*3c27b408SMichael Jeanson 	ret = sys_rseq((void *) -4096UL, 32, 0, RSEQ_SIG);
76*3c27b408SMichael Jeanson 	errno_copy = errno;
77*3c27b408SMichael Jeanson 	fprintf(stderr, "Registration with invalid address fails with errno set to EFAULT (ret = %d, errno = %s)\n", ret, strerrorname_np(errno_copy));
78*3c27b408SMichael Jeanson 	if (ret == 0 || errno_copy != EFAULT)
79*3c27b408SMichael Jeanson 		goto error;
80*3c27b408SMichael Jeanson #endif
81*3c27b408SMichael Jeanson 
82*3c27b408SMichael Jeanson 	errno = 0;
83*3c27b408SMichael Jeanson 	ret = sys_rseq(global_rseq, 32, 0, RSEQ_SIG);
84*3c27b408SMichael Jeanson 	errno_copy = errno;
85*3c27b408SMichael Jeanson 	fprintf(stderr, "Registration succeeds for the current thread (ret = %d, errno = %s)\n", ret, strerrorname_np(errno_copy));
86*3c27b408SMichael Jeanson 	if (ret != 0 && errno != 0)
87*3c27b408SMichael Jeanson 		goto error;
88*3c27b408SMichael Jeanson 
89*3c27b408SMichael Jeanson 	/* The current thread is registered. */
90*3c27b408SMichael Jeanson 
91*3c27b408SMichael Jeanson 	/* EBUSY */
92*3c27b408SMichael Jeanson 	errno = 0;
93*3c27b408SMichael Jeanson 	ret = sys_rseq(global_rseq, 32, 0, RSEQ_SIG);
94*3c27b408SMichael Jeanson 	errno_copy = errno;
95*3c27b408SMichael Jeanson 	fprintf(stderr, "Double registration fails with errno set to EBUSY (ret = %d, errno = %s)\n", ret, strerrorname_np(errno_copy));
96*3c27b408SMichael Jeanson 	if (ret == 0 || errno_copy != EBUSY)
97*3c27b408SMichael Jeanson 		goto error;
98*3c27b408SMichael Jeanson 
99*3c27b408SMichael Jeanson 	/* EPERM */
100*3c27b408SMichael Jeanson 	errno = 0;
101*3c27b408SMichael Jeanson 	ret = sys_rseq(global_rseq, 32, RSEQ_ABI_FLAG_UNREGISTER, RSEQ_SIG + 1);
102*3c27b408SMichael Jeanson 	errno_copy = errno;
103*3c27b408SMichael Jeanson 	fprintf(stderr, "Unregistration with wrong RSEQ_SIG fails with errno to EPERM (ret = %d, errno = %s)\n", ret, strerrorname_np(errno_copy));
104*3c27b408SMichael Jeanson 	if (ret == 0 || errno_copy != EPERM)
105*3c27b408SMichael Jeanson 		goto error;
106*3c27b408SMichael Jeanson 
107*3c27b408SMichael Jeanson 	errno = 0;
108*3c27b408SMichael Jeanson 	ret = sys_rseq(global_rseq, 32, RSEQ_ABI_FLAG_UNREGISTER, RSEQ_SIG);
109*3c27b408SMichael Jeanson 	errno_copy = errno;
110*3c27b408SMichael Jeanson 	fprintf(stderr, "Unregistration succeeds for the current thread (ret = %d, errno = %s)\n", ret, strerrorname_np(errno_copy));
111*3c27b408SMichael Jeanson 	if (ret != 0)
112*3c27b408SMichael Jeanson 		goto error;
113*3c27b408SMichael Jeanson 
114*3c27b408SMichael Jeanson 	errno = 0;
115*3c27b408SMichael Jeanson 	ret = sys_rseq(global_rseq, 32, RSEQ_ABI_FLAG_UNREGISTER, RSEQ_SIG);
116*3c27b408SMichael Jeanson 	errno_copy = errno;
117*3c27b408SMichael Jeanson 	fprintf(stderr, "Double unregistration fails with errno set to EINVAL (ret = %d, errno = %s)\n", ret, strerrorname_np(errno_copy));
118*3c27b408SMichael Jeanson 	if (ret == 0 || errno_copy != EINVAL)
119*3c27b408SMichael Jeanson 		goto error;
120*3c27b408SMichael Jeanson 
121*3c27b408SMichael Jeanson 	return 0;
122*3c27b408SMichael Jeanson error:
123*3c27b408SMichael Jeanson 	return -1;
124*3c27b408SMichael Jeanson }
125