aboutsummaryrefslogtreecommitdiff
path: root/linux-user/syscall.c
diff options
context:
space:
mode:
Diffstat (limited to 'linux-user/syscall.c')
-rw-r--r--linux-user/syscall.c114
1 files changed, 114 insertions, 0 deletions
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index d78b202..8546f48 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -6353,6 +6353,17 @@ abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr)
# define PR_SME_VL_LEN_MASK 0xffff
# define PR_SME_VL_INHERIT (1 << 17)
#endif
+#ifndef PR_GET_SHADOW_STACK_STATUS
+# define PR_GET_SHADOW_STACK_STATUS 74
+# 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"
@@ -6399,6 +6410,15 @@ static abi_long do_prctl_inval1(CPUArchState *env, abi_long arg2)
#ifndef do_prctl_sme_set_vl
#define do_prctl_sme_set_vl do_prctl_inval1
#endif
+#ifndef do_prctl_get_shadow_stack_status
+#define do_prctl_get_shadow_stack_status do_prctl_inval1
+#endif
+#ifndef do_prctl_set_shadow_stack_status
+#define do_prctl_set_shadow_stack_status do_prctl_inval1
+#endif
+#ifndef do_prctl_lock_shadow_stack_status
+#define do_prctl_lock_shadow_stack_status do_prctl_inval1
+#endif
static abi_long do_prctl_syscall_user_dispatch(CPUArchState *env,
abi_ulong arg2, abi_ulong arg3,
@@ -6499,6 +6519,21 @@ static abi_long do_prctl(CPUArchState *env, abi_long option, abi_long arg2,
return -TARGET_EINVAL;
}
return do_prctl_get_tagged_addr_ctrl(env);
+ case PR_GET_SHADOW_STACK_STATUS:
+ if (arg3 || arg4 || arg5) {
+ return -TARGET_EINVAL;
+ }
+ return do_prctl_get_shadow_stack_status(env, arg2);
+ case PR_SET_SHADOW_STACK_STATUS:
+ if (arg3 || arg4 || arg5) {
+ return -TARGET_EINVAL;
+ }
+ return do_prctl_set_shadow_stack_status(env, arg2);
+ case PR_LOCK_SHADOW_STACK_STATUS:
+ if (arg3 || arg4 || arg5) {
+ return -TARGET_EINVAL;
+ }
+ return do_prctl_lock_shadow_stack_status(env, arg2);
case PR_GET_UNALIGN:
return do_prctl_get_unalign(env, arg2);
@@ -6576,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
@@ -6657,6 +6740,21 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp,
ts = g_new0(TaskState, 1);
init_task_state(ts);
+#ifdef TARGET_AARCH64
+ /*
+ * If GCS is enabled in the parent thread, it is also enabled
+ * in the child thread, but with a newly allocated stack.
+ */
+ abi_long new_gcspr = 0;
+ if (env->cp15.gcscr_el[0] & GCSCR_PCRSEL) {
+ new_gcspr = gcs_new_stack(ts);
+ if (new_gcspr == -1) {
+ g_free(ts);
+ return -TARGET_ENOMEM;
+ }
+ }
+#endif
+
/* Grab a mutex so that thread setup appears atomic. */
pthread_mutex_lock(&clone_lock);
@@ -6678,6 +6776,11 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp,
ts->info = parent_ts->info;
ts->signal_mask = parent_ts->signal_mask;
+#ifdef TARGET_AARCH64
+ ts->gcs_el0_locked = parent_ts->gcs_el0_locked;
+ new_env->cp15.gcspr_el[0] = new_gcspr;
+#endif
+
if (flags & CLONE_CHILD_CLEARTID) {
ts->child_tidptr = child_tidptr;
}
@@ -9380,6 +9483,12 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
}
+#ifdef TARGET_AARCH64
+ if (ts->gcs_base) {
+ target_munmap(ts->gcs_base, ts->gcs_size);
+ }
+#endif
+
object_unparent(OBJECT(cpu));
object_unref(OBJECT(cpu));
/*
@@ -14010,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;