mirror of
https://github.com/torvalds/linux.git
synced 2025-04-09 14:45:27 +00:00

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 <mjeanson@efficios.com> Signed-off-by: Ingo Molnar <mingo@kernel.org> Reviewed-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> Cc: Shuah Khan <skhan@linuxfoundation.org> Link: https://lore.kernel.org/r/20250121213402.1754762-1-mjeanson@efficios.com
125 lines
3.7 KiB
C
125 lines
3.7 KiB
C
// 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;
|
|
}
|