aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSzabolcs Nagy <szabolcs.nagy@arm.com>2023-04-04 10:42:21 +0100
committerYury Khrustalev <yury.khrustalev@arm.com>2025-01-20 09:22:41 +0000
commit3ac237fb716b86ba7967edb84eb85d15364aaebc (patch)
tree51dcd944ebd206276009c759168e869eb0ffd971
parent7d22054db7df3b0b84f1a2142195e27c5d34285b (diff)
downloadglibc-3ac237fb716b86ba7967edb84eb85d15364aaebc.zip
glibc-3ac237fb716b86ba7967edb84eb85d15364aaebc.tar.gz
glibc-3ac237fb716b86ba7967edb84eb85d15364aaebc.tar.bz2
aarch64: Add GCS support for makecontext
Changed the makecontext logic: previously the first setcontext jumped straight to the user callback function and the return address is set to __startcontext. This does not work when GCS is enabled as the integrity of the return address is protected, so instead the context is setup such that setcontext jumps to __startcontext which calls the user callback (passed in x20). The map_shadow_stack syscall is used to allocate a suitably sized GCS (which includes some reserved area to account for altstack signal handlers and otherwise supports maximum number of 16 byte aligned stack frames on the given stack) however the GCS is never freed as the lifetime of ucontext and related stack is user managed. Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
-rw-r--r--sysdeps/unix/sysv/linux/aarch64/makecontext.c61
-rw-r--r--sysdeps/unix/sysv/linux/aarch64/setcontext.S4
2 files changed, 63 insertions, 2 deletions
diff --git a/sysdeps/unix/sysv/linux/aarch64/makecontext.c b/sysdeps/unix/sysv/linux/aarch64/makecontext.c
index 11516b7..09e5443 100644
--- a/sysdeps/unix/sysv/linux/aarch64/makecontext.c
+++ b/sysdeps/unix/sysv/linux/aarch64/makecontext.c
@@ -21,7 +21,51 @@
#include <stdarg.h>
#include <stdint.h>
#include <ucontext.h>
+#include <sys/mman.h>
+#define GCS_MAGIC 0x47435300
+
+static struct _aarch64_ctx *extension (void *p)
+{
+ return p;
+}
+
+#ifndef SHADOW_STACK_SET_TOKEN
+# define SHADOW_STACK_SET_TOKEN (1UL << 0)
+# define SHADOW_STACK_SET_MARKER (1UL << 1)
+#endif
+
+static void *
+map_shadow_stack (void *addr, size_t size, unsigned long flags)
+{
+ return (void *) INLINE_SYSCALL_CALL (map_shadow_stack, addr, size, flags);
+}
+
+#define GCS_MAX_SIZE (1UL << 31)
+#define GCS_ALTSTACK_RESERVE 160
+
+static void *
+alloc_makecontext_gcs (size_t stack_size)
+{
+ size_t size = (stack_size / 2 + GCS_ALTSTACK_RESERVE) & -8UL;
+ if (size > GCS_MAX_SIZE)
+ size = GCS_MAX_SIZE;
+
+ unsigned long flags = SHADOW_STACK_SET_MARKER | SHADOW_STACK_SET_TOKEN;
+ void *base = map_shadow_stack (NULL, size, flags);
+ if (base == MAP_FAILED)
+ /* ENOSYS, bad size or OOM. */
+ abort ();
+ uint64_t *gcsp = (uint64_t *) ((char *) base + size);
+ /* Skip end of GCS token. */
+ gcsp--;
+ /* Verify GCS cap token. */
+ gcsp--;
+ if (((uint64_t)gcsp & 0xfffffffffffff000) + 1 != *gcsp)
+ abort ();
+ /* Return the target GCS pointer for context switch. */
+ return gcsp + 1;
+}
/* makecontext sets up a stack and the registers for the
user context. The stack looks like this:
@@ -56,10 +100,23 @@ __makecontext (ucontext_t *ucp, void (*func) (void), int argc, ...)
sp = (uint64_t *) (((uintptr_t) sp) & -16L);
ucp->uc_mcontext.regs[19] = (uintptr_t) ucp->uc_link;
+ ucp->uc_mcontext.regs[20] = (uintptr_t) func;
ucp->uc_mcontext.sp = (uintptr_t) sp;
- ucp->uc_mcontext.pc = (uintptr_t) func;
+ ucp->uc_mcontext.pc = (uintptr_t) __startcontext;
ucp->uc_mcontext.regs[29] = (uintptr_t) 0;
- ucp->uc_mcontext.regs[30] = (uintptr_t) &__startcontext;
+ ucp->uc_mcontext.regs[30] = (uintptr_t) 0;
+
+ void *p = ucp->uc_mcontext.__reserved;
+ if (extension (p)->magic == FPSIMD_MAGIC)
+ p = (char *)p + extension (p)->size;
+ if (extension (p)->magic == GCS_MAGIC)
+ {
+ /* Using the kernel struct gcs_context layout. */
+ struct { uint64_t x, gcspr, y, z; } *q = p;
+ /* TODO: this allocation remains mapped even after thread
+ that uses it exits. */
+ q->gcspr = (uint64_t) alloc_makecontext_gcs (ucp->uc_stack.ss_size);
+ }
va_start (ap, argc);
for (i = 0; i < argc; ++i)
diff --git a/sysdeps/unix/sysv/linux/aarch64/setcontext.S b/sysdeps/unix/sysv/linux/aarch64/setcontext.S
index 848229f..695fc5b 100644
--- a/sysdeps/unix/sysv/linux/aarch64/setcontext.S
+++ b/sysdeps/unix/sysv/linux/aarch64/setcontext.S
@@ -180,7 +180,11 @@ L(gcs_done):
PSEUDO_END (__setcontext)
weak_alias (__setcontext, setcontext)
+/* makecontext start function: receives uc_link in x19 and func in x20.
+ Arguments of func, x29, x30 and sp are set up by the caller. */
ENTRY (__startcontext)
+ cfi_undefined (x30)
+ blr x20
mov x0, x19
cbnz x0, __setcontext
1: b HIDDEN_JUMPTARGET (exit)