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 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 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