diff options
| -rw-r--r-- | tools/testing/selftests/rseq/.gitignore | 1 | ||||
| -rw-r--r-- | tools/testing/selftests/rseq/Makefile | 9 | ||||
| -rw-r--r-- | tools/testing/selftests/rseq/rseq.c | 6 | ||||
| -rw-r--r-- | tools/testing/selftests/rseq/rseq.h | 5 | ||||
| -rwxr-xr-x | tools/testing/selftests/rseq/run_syscall_errors_test.sh | 5 | ||||
| -rw-r--r-- | tools/testing/selftests/rseq/syscall_errors_test.c | 124 | 
6 files changed, 145 insertions, 5 deletions
| diff --git a/tools/testing/selftests/rseq/.gitignore b/tools/testing/selftests/rseq/.gitignore index 16496de5f6ce..0fda241fa62b 100644 --- a/tools/testing/selftests/rseq/.gitignore +++ b/tools/testing/selftests/rseq/.gitignore @@ -9,3 +9,4 @@ param_test_compare_twice  param_test_mm_cid  param_test_mm_cid_benchmark  param_test_mm_cid_compare_twice +syscall_errors_test diff --git a/tools/testing/selftests/rseq/Makefile b/tools/testing/selftests/rseq/Makefile index 5a3432fceb58..0d0a5fae5954 100644 --- a/tools/testing/selftests/rseq/Makefile +++ b/tools/testing/selftests/rseq/Makefile @@ -16,11 +16,12 @@ OVERRIDE_TARGETS = 1  TEST_GEN_PROGS = basic_test basic_percpu_ops_test basic_percpu_ops_mm_cid_test param_test \  		param_test_benchmark param_test_compare_twice param_test_mm_cid \ -		param_test_mm_cid_benchmark param_test_mm_cid_compare_twice +		param_test_mm_cid_benchmark param_test_mm_cid_compare_twice \ +		syscall_errors_test  TEST_GEN_PROGS_EXTENDED = librseq.so -TEST_PROGS = run_param_test.sh +TEST_PROGS = run_param_test.sh run_syscall_errors_test.sh  TEST_FILES := settings @@ -54,3 +55,7 @@ $(OUTPUT)/param_test_mm_cid_benchmark: param_test.c $(TEST_GEN_PROGS_EXTENDED) \  $(OUTPUT)/param_test_mm_cid_compare_twice: param_test.c $(TEST_GEN_PROGS_EXTENDED) \  					rseq.h rseq-*.h  	$(CC) $(CFLAGS) -DBUILDOPT_RSEQ_PERCPU_MM_CID -DRSEQ_COMPARE_TWICE $< $(LDLIBS) -lrseq -o $@ + +$(OUTPUT)/syscall_errors_test: syscall_errors_test.c $(TEST_GEN_PROGS_EXTENDED) \ +					rseq.h rseq-*.h +	$(CC) $(CFLAGS) $< $(LDLIBS) -lrseq -o $@ diff --git a/tools/testing/selftests/rseq/rseq.c b/tools/testing/selftests/rseq/rseq.c index f6156790c3b4..1e29db92094d 100644 --- a/tools/testing/selftests/rseq/rseq.c +++ b/tools/testing/selftests/rseq/rseq.c @@ -87,7 +87,7 @@ static int sys_getcpu(unsigned *cpu, unsigned *node)  	return syscall(__NR_getcpu, cpu, node, NULL);  } -int rseq_available(void) +bool rseq_available(void)  {  	int rc; @@ -96,9 +96,9 @@ int rseq_available(void)  		abort();  	switch (errno) {  	case ENOSYS: -		return 0; +		return false;  	case EINVAL: -		return 1; +		return true;  	default:  		abort();  	} diff --git a/tools/testing/selftests/rseq/rseq.h b/tools/testing/selftests/rseq/rseq.h index ba424ce80a71..f51a5fdb0444 100644 --- a/tools/testing/selftests/rseq/rseq.h +++ b/tools/testing/selftests/rseq/rseq.h @@ -160,6 +160,11 @@ int32_t rseq_fallback_current_cpu(void);  int32_t rseq_fallback_current_node(void);  /* + * Returns true if rseq is supported. + */ +bool rseq_available(void); + +/*   * Values returned can be either the current CPU number, -1 (rseq is   * uninitialized), or -2 (rseq initialization has failed).   */ diff --git a/tools/testing/selftests/rseq/run_syscall_errors_test.sh b/tools/testing/selftests/rseq/run_syscall_errors_test.sh new file mode 100755 index 000000000000..9272246b39f2 --- /dev/null +++ b/tools/testing/selftests/rseq/run_syscall_errors_test.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# SPDX-License-Identifier: MIT +# SPDX-FileCopyrightText: 2024 Michael Jeanson <mjeanson@efficios.com> + +GLIBC_TUNABLES="${GLIBC_TUNABLES:-}:glibc.pthread.rseq=0" ./syscall_errors_test diff --git a/tools/testing/selftests/rseq/syscall_errors_test.c b/tools/testing/selftests/rseq/syscall_errors_test.c new file mode 100644 index 000000000000..a5d9e1f8a2dc --- /dev/null +++ b/tools/testing/selftests/rseq/syscall_errors_test.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2024 Michael Jeanson <mjeanson@efficios.com> + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <assert.h> +#include <stdint.h> +#include <syscall.h> +#include <string.h> +#include <unistd.h> + +#include "rseq.h" + +static int sys_rseq(void *rseq_abi, uint32_t rseq_len, +		    int flags, uint32_t sig) +{ +	return syscall(__NR_rseq, rseq_abi, rseq_len, flags, sig); +} + +/* + * Check the value of errno on some expected failures of the rseq syscall. + */ + +int main(void) +{ +	struct rseq_abi *global_rseq = rseq_get_abi(); +	int ret; +	int errno_copy; + +	if (!rseq_available()) { +		fprintf(stderr, "rseq syscall unavailable"); +		goto error; +	} + +	/* The current thread is NOT registered. */ + +	/* EINVAL */ +	errno = 0; +	ret = sys_rseq(global_rseq, 32, -1, RSEQ_SIG); +	errno_copy = errno; +	fprintf(stderr, "Registration with invalid flag fails with errno set to EINVAL (ret = %d, errno = %s)\n", ret, strerrorname_np(errno_copy)); +	if (ret == 0 || errno_copy != EINVAL) +		goto error; + +	errno = 0; +	ret = sys_rseq((char *) global_rseq + 1, 32, 0, RSEQ_SIG); +	errno_copy = errno; +	fprintf(stderr, "Registration with unaligned rseq_abi fails with errno set to EINVAL (ret = %d, errno = %s)\n", ret, strerrorname_np(errno_copy)); +	if (ret == 0 || errno_copy != EINVAL) +		goto error; + +	errno = 0; +	ret = sys_rseq(global_rseq, 31, 0, RSEQ_SIG); +	errno_copy = errno; +	fprintf(stderr, "Registration with invalid size fails with errno set to EINVAL (ret = %d, errno = %s)\n", ret, strerrorname_np(errno_copy)); +	if (ret == 0 || errno_copy != EINVAL) +		goto error; + + +#if defined(__LP64__) && (!defined(__s390__) && !defined(__s390x__)) +	/* +	 * We haven't found a reliable way to find an invalid address when +	 * running a 32bit userspace on a 64bit kernel, so only run this test +	 * on 64bit builds for the moment. +	 * +	 * Also exclude architectures that select +	 * CONFIG_ALTERNATE_USER_ADDRESS_SPACE where the kernel and userspace +	 * have their own address space and this failure can't happen. +	 */ + +	/* EFAULT */ +	errno = 0; +	ret = sys_rseq((void *) -4096UL, 32, 0, RSEQ_SIG); +	errno_copy = errno; +	fprintf(stderr, "Registration with invalid address fails with errno set to EFAULT (ret = %d, errno = %s)\n", ret, strerrorname_np(errno_copy)); +	if (ret == 0 || errno_copy != EFAULT) +		goto error; +#endif + +	errno = 0; +	ret = sys_rseq(global_rseq, 32, 0, RSEQ_SIG); +	errno_copy = errno; +	fprintf(stderr, "Registration succeeds for the current thread (ret = %d, errno = %s)\n", ret, strerrorname_np(errno_copy)); +	if (ret != 0 && errno != 0) +		goto error; + +	/* The current thread is registered. */ + +	/* EBUSY */ +	errno = 0; +	ret = sys_rseq(global_rseq, 32, 0, RSEQ_SIG); +	errno_copy = errno; +	fprintf(stderr, "Double registration fails with errno set to EBUSY (ret = %d, errno = %s)\n", ret, strerrorname_np(errno_copy)); +	if (ret == 0 || errno_copy != EBUSY) +		goto error; + +	/* EPERM */ +	errno = 0; +	ret = sys_rseq(global_rseq, 32, RSEQ_ABI_FLAG_UNREGISTER, RSEQ_SIG + 1); +	errno_copy = errno; +	fprintf(stderr, "Unregistration with wrong RSEQ_SIG fails with errno to EPERM (ret = %d, errno = %s)\n", ret, strerrorname_np(errno_copy)); +	if (ret == 0 || errno_copy != EPERM) +		goto error; + +	errno = 0; +	ret = sys_rseq(global_rseq, 32, RSEQ_ABI_FLAG_UNREGISTER, RSEQ_SIG); +	errno_copy = errno; +	fprintf(stderr, "Unregistration succeeds for the current thread (ret = %d, errno = %s)\n", ret, strerrorname_np(errno_copy)); +	if (ret != 0) +		goto error; + +	errno = 0; +	ret = sys_rseq(global_rseq, 32, RSEQ_ABI_FLAG_UNREGISTER, RSEQ_SIG); +	errno_copy = errno; +	fprintf(stderr, "Double unregistration fails with errno set to EINVAL (ret = %d, errno = %s)\n", ret, strerrorname_np(errno_copy)); +	if (ret == 0 || errno_copy != EINVAL) +		goto error; + +	return 0; +error: +	return -1; +} | 
