Skip to content

Commit 3c27b40

Browse files
mjeansonIngo Molnar
authored andcommitted
selftests/rseq: Add rseq syscall errors test
This test adds coverage of expected errors during rseq registration and unregistration, it disables glibc integration and will thus always exercise the rseq syscall explictly. Signed-off-by: Michael Jeanson <[email protected]> Signed-off-by: Ingo Molnar <[email protected]> Reviewed-by: Mathieu Desnoyers <[email protected]> Cc: Shuah Khan <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 1a5d349 commit 3c27b40

File tree

6 files changed

+145
-5
lines changed

6 files changed

+145
-5
lines changed

tools/testing/selftests/rseq/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ param_test_compare_twice
99
param_test_mm_cid
1010
param_test_mm_cid_benchmark
1111
param_test_mm_cid_compare_twice
12+
syscall_errors_test

tools/testing/selftests/rseq/Makefile

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@ OVERRIDE_TARGETS = 1
1616

1717
TEST_GEN_PROGS = basic_test basic_percpu_ops_test basic_percpu_ops_mm_cid_test param_test \
1818
param_test_benchmark param_test_compare_twice param_test_mm_cid \
19-
param_test_mm_cid_benchmark param_test_mm_cid_compare_twice
19+
param_test_mm_cid_benchmark param_test_mm_cid_compare_twice \
20+
syscall_errors_test
2021

2122
TEST_GEN_PROGS_EXTENDED = librseq.so
2223

23-
TEST_PROGS = run_param_test.sh
24+
TEST_PROGS = run_param_test.sh run_syscall_errors_test.sh
2425

2526
TEST_FILES := settings
2627

@@ -54,3 +55,7 @@ $(OUTPUT)/param_test_mm_cid_benchmark: param_test.c $(TEST_GEN_PROGS_EXTENDED) \
5455
$(OUTPUT)/param_test_mm_cid_compare_twice: param_test.c $(TEST_GEN_PROGS_EXTENDED) \
5556
rseq.h rseq-*.h
5657
$(CC) $(CFLAGS) -DBUILDOPT_RSEQ_PERCPU_MM_CID -DRSEQ_COMPARE_TWICE $< $(LDLIBS) -lrseq -o $@
58+
59+
$(OUTPUT)/syscall_errors_test: syscall_errors_test.c $(TEST_GEN_PROGS_EXTENDED) \
60+
rseq.h rseq-*.h
61+
$(CC) $(CFLAGS) $< $(LDLIBS) -lrseq -o $@

tools/testing/selftests/rseq/rseq.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ static int sys_getcpu(unsigned *cpu, unsigned *node)
8787
return syscall(__NR_getcpu, cpu, node, NULL);
8888
}
8989

90-
int rseq_available(void)
90+
bool rseq_available(void)
9191
{
9292
int rc;
9393

@@ -96,9 +96,9 @@ int rseq_available(void)
9696
abort();
9797
switch (errno) {
9898
case ENOSYS:
99-
return 0;
99+
return false;
100100
case EINVAL:
101-
return 1;
101+
return true;
102102
default:
103103
abort();
104104
}

tools/testing/selftests/rseq/rseq.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,11 @@ int32_t rseq_fallback_current_cpu(void);
159159
*/
160160
int32_t rseq_fallback_current_node(void);
161161

162+
/*
163+
* Returns true if rseq is supported.
164+
*/
165+
bool rseq_available(void);
166+
162167
/*
163168
* Values returned can be either the current CPU number, -1 (rseq is
164169
* uninitialized), or -2 (rseq initialization has failed).
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/bin/bash
2+
# SPDX-License-Identifier: MIT
3+
# SPDX-FileCopyrightText: 2024 Michael Jeanson <[email protected]>
4+
5+
GLIBC_TUNABLES="${GLIBC_TUNABLES:-}:glibc.pthread.rseq=0" ./syscall_errors_test
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
// SPDX-License-Identifier: MIT
2+
// SPDX-FileCopyrightText: 2024 Michael Jeanson <[email protected]>
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+
}

0 commit comments

Comments
 (0)