linux-user/aarch64: Implement map_shadow_stack syscall

Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20251008215613.300150-67-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Richard Henderson 2025-10-08 14:56:06 -07:00 committed by Peter Maydell
parent 1c94480377
commit ad1afe433f

View file

@ -6358,6 +6358,12 @@ abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr)
# define PR_SET_SHADOW_STACK_STATUS 75
# define PR_LOCK_SHADOW_STACK_STATUS 76
#endif
#ifndef SHADOW_STACK_SET_TOKEN
# define SHADOW_STACK_SET_TOKEN (1u << 0)
#endif
#ifndef SHADOW_STACK_SET_MARKER
# define SHADOW_STACK_SET_MARKER (1u << 1)
#endif
#include "target_prctl.h"
@ -6605,6 +6611,54 @@ static abi_long do_prctl(CPUArchState *env, abi_long option, abi_long arg2,
}
}
#ifdef TARGET_AARCH64
static abi_long do_map_shadow_stack(CPUArchState *env, abi_ulong addr,
abi_ulong size, abi_int flags)
{
ARMCPU *cpu = env_archcpu(env);
abi_ulong alloc_size;
if (!cpu_isar_feature(aa64_gcs, cpu)) {
return -TARGET_EOPNOTSUPP;
}
if (flags & ~(SHADOW_STACK_SET_TOKEN | SHADOW_STACK_SET_MARKER)) {
return -TARGET_EINVAL;
}
if (addr & ~TARGET_PAGE_MASK) {
return -TARGET_EINVAL;
}
if (size == 8 || !QEMU_IS_ALIGNED(size, 8)) {
return -TARGET_EINVAL;
}
alloc_size = TARGET_PAGE_ALIGN(size);
if (alloc_size < size) {
return -TARGET_EOVERFLOW;
}
mmap_lock();
addr = gcs_alloc(addr, alloc_size);
if (addr != -1) {
if (flags & SHADOW_STACK_SET_TOKEN) {
abi_ptr cap_ptr = addr + size - 8;
uint64_t cap_val;
if (flags & SHADOW_STACK_SET_MARKER) {
/* Leave an extra empty frame at top-of-stack. */
cap_ptr -= 8;
}
cap_val = (cap_ptr & TARGET_PAGE_MASK) | 1;
if (put_user_u64(cap_val, cap_ptr)) {
/* Allocation succeeded above. */
g_assert_not_reached();
}
}
}
mmap_unlock();
return get_errno(addr);
}
#endif
#define NEW_STACK_SIZE 0x40000
@ -14065,6 +14119,11 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
return do_riscv_hwprobe(cpu_env, arg1, arg2, arg3, arg4, arg5);
#endif
#ifdef TARGET_AARCH64
case TARGET_NR_map_shadow_stack:
return do_map_shadow_stack(cpu_env, arg1, arg2, arg3);
#endif
default:
qemu_log_mask(LOG_UNIMP, "Unsupported syscall: %d\n", num);
return -TARGET_ENOSYS;