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