201 lines
4.8 KiB
C
201 lines
4.8 KiB
C
|
// SPDX-License-Identifier: GPL-2.0-only
|
||
|
/*
|
||
|
* Copyright (C) 2023 ARM Limited.
|
||
|
*
|
||
|
* Tests for GCS mode locking. These tests rely on both having GCS
|
||
|
* unconfigured on entry and on the kselftest harness running each
|
||
|
* test in a fork()ed process which will have it's own mode.
|
||
|
*/
|
||
|
|
||
|
#include <limits.h>
|
||
|
|
||
|
#include <sys/auxv.h>
|
||
|
#include <sys/prctl.h>
|
||
|
|
||
|
#include <asm/hwcap.h>
|
||
|
|
||
|
#include "kselftest_harness.h"
|
||
|
|
||
|
#include "gcs-util.h"
|
||
|
|
||
|
#define my_syscall2(num, arg1, arg2) \
|
||
|
({ \
|
||
|
register long _num __asm__ ("x8") = (num); \
|
||
|
register long _arg1 __asm__ ("x0") = (long)(arg1); \
|
||
|
register long _arg2 __asm__ ("x1") = (long)(arg2); \
|
||
|
register long _arg3 __asm__ ("x2") = 0; \
|
||
|
register long _arg4 __asm__ ("x3") = 0; \
|
||
|
register long _arg5 __asm__ ("x4") = 0; \
|
||
|
\
|
||
|
__asm__ volatile ( \
|
||
|
"svc #0\n" \
|
||
|
: "=r"(_arg1) \
|
||
|
: "r"(_arg1), "r"(_arg2), \
|
||
|
"r"(_arg3), "r"(_arg4), \
|
||
|
"r"(_arg5), "r"(_num) \
|
||
|
: "memory", "cc" \
|
||
|
); \
|
||
|
_arg1; \
|
||
|
})
|
||
|
|
||
|
/* No mode bits are rejected for locking */
|
||
|
TEST(lock_all_modes)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
ret = prctl(PR_LOCK_SHADOW_STACK_STATUS, ULONG_MAX, 0, 0, 0);
|
||
|
ASSERT_EQ(ret, 0);
|
||
|
}
|
||
|
|
||
|
FIXTURE(valid_modes)
|
||
|
{
|
||
|
};
|
||
|
|
||
|
FIXTURE_VARIANT(valid_modes)
|
||
|
{
|
||
|
unsigned long mode;
|
||
|
};
|
||
|
|
||
|
FIXTURE_VARIANT_ADD(valid_modes, enable)
|
||
|
{
|
||
|
.mode = PR_SHADOW_STACK_ENABLE,
|
||
|
};
|
||
|
|
||
|
FIXTURE_VARIANT_ADD(valid_modes, enable_write)
|
||
|
{
|
||
|
.mode = PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_WRITE,
|
||
|
};
|
||
|
|
||
|
FIXTURE_VARIANT_ADD(valid_modes, enable_push)
|
||
|
{
|
||
|
.mode = PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_PUSH,
|
||
|
};
|
||
|
|
||
|
FIXTURE_VARIANT_ADD(valid_modes, enable_write_push)
|
||
|
{
|
||
|
.mode = PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_WRITE |
|
||
|
PR_SHADOW_STACK_PUSH,
|
||
|
};
|
||
|
|
||
|
FIXTURE_SETUP(valid_modes)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
FIXTURE_TEARDOWN(valid_modes)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/* We can set the mode at all */
|
||
|
TEST_F(valid_modes, set)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
|
||
|
variant->mode);
|
||
|
ASSERT_EQ(ret, 0);
|
||
|
|
||
|
_exit(0);
|
||
|
}
|
||
|
|
||
|
/* Enabling, locking then disabling is rejected */
|
||
|
TEST_F(valid_modes, enable_lock_disable)
|
||
|
{
|
||
|
unsigned long mode;
|
||
|
int ret;
|
||
|
|
||
|
ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
|
||
|
variant->mode);
|
||
|
ASSERT_EQ(ret, 0);
|
||
|
|
||
|
ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
|
||
|
ASSERT_EQ(ret, 0);
|
||
|
ASSERT_EQ(mode, variant->mode);
|
||
|
|
||
|
ret = prctl(PR_LOCK_SHADOW_STACK_STATUS, variant->mode, 0, 0, 0);
|
||
|
ASSERT_EQ(ret, 0);
|
||
|
|
||
|
ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS, 0);
|
||
|
ASSERT_EQ(ret, -EBUSY);
|
||
|
|
||
|
_exit(0);
|
||
|
}
|
||
|
|
||
|
/* Locking then enabling is rejected */
|
||
|
TEST_F(valid_modes, lock_enable)
|
||
|
{
|
||
|
unsigned long mode;
|
||
|
int ret;
|
||
|
|
||
|
ret = prctl(PR_LOCK_SHADOW_STACK_STATUS, variant->mode, 0, 0, 0);
|
||
|
ASSERT_EQ(ret, 0);
|
||
|
|
||
|
ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
|
||
|
variant->mode);
|
||
|
ASSERT_EQ(ret, -EBUSY);
|
||
|
|
||
|
ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
|
||
|
ASSERT_EQ(ret, 0);
|
||
|
ASSERT_EQ(mode, 0);
|
||
|
|
||
|
_exit(0);
|
||
|
}
|
||
|
|
||
|
/* Locking then changing other modes is fine */
|
||
|
TEST_F(valid_modes, lock_enable_disable_others)
|
||
|
{
|
||
|
unsigned long mode;
|
||
|
int ret;
|
||
|
|
||
|
ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
|
||
|
variant->mode);
|
||
|
ASSERT_EQ(ret, 0);
|
||
|
|
||
|
ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
|
||
|
ASSERT_EQ(ret, 0);
|
||
|
ASSERT_EQ(mode, variant->mode);
|
||
|
|
||
|
ret = prctl(PR_LOCK_SHADOW_STACK_STATUS, variant->mode, 0, 0, 0);
|
||
|
ASSERT_EQ(ret, 0);
|
||
|
|
||
|
ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
|
||
|
PR_SHADOW_STACK_ALL_MODES);
|
||
|
ASSERT_EQ(ret, 0);
|
||
|
|
||
|
ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
|
||
|
ASSERT_EQ(ret, 0);
|
||
|
ASSERT_EQ(mode, PR_SHADOW_STACK_ALL_MODES);
|
||
|
|
||
|
|
||
|
ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
|
||
|
variant->mode);
|
||
|
ASSERT_EQ(ret, 0);
|
||
|
|
||
|
ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
|
||
|
ASSERT_EQ(ret, 0);
|
||
|
ASSERT_EQ(mode, variant->mode);
|
||
|
|
||
|
_exit(0);
|
||
|
}
|
||
|
|
||
|
int main(int argc, char **argv)
|
||
|
{
|
||
|
unsigned long mode;
|
||
|
int ret;
|
||
|
|
||
|
if (!(getauxval(AT_HWCAP) & HWCAP_GCS))
|
||
|
ksft_exit_skip("SKIP GCS not supported\n");
|
||
|
|
||
|
ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
|
||
|
if (ret) {
|
||
|
ksft_print_msg("Failed to read GCS state: %d\n", ret);
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
|
||
|
if (mode & PR_SHADOW_STACK_ENABLE) {
|
||
|
ksft_print_msg("GCS was enabled, test unsupported\n");
|
||
|
return KSFT_SKIP;
|
||
|
}
|
||
|
|
||
|
return test_harness_run(argc, argv);
|
||
|
}
|