[PATCH v5 08/23] aarch64: Add GCS support for makecontext

Carlos O'Donell codonell@redhat.com
Wed Dec 11 22:58:51 GMT 2024


On 12/6/24 8:29 AM, Yury Khrustalev wrote:
> From: Szabolcs Nagy <szabolcs.nagy@arm.com>
> 
> 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.

Totally hidden from outside of glibc, this should go in.

I'm tolerant of the quirk that a statically compiled application using GCS
might break if the yet not committed kernel constants change. What I don't
want to do is have a public ABI that gets embedded in other applications
that are dynamically compiled and should expect to have long backwards
compatibility.

LGTM.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>

> ---
>  sysdeps/unix/sysv/linux/aarch64/makecontext.c | 61 ++++++++++++++++++-
>  sysdeps/unix/sysv/linux/aarch64/setcontext.S  |  4 ++
>  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 5aec182013..9e66b6761c 100644
> --- a/sysdeps/unix/sysv/linux/aarch64/makecontext.c
> +++ b/sysdeps/unix/sysv/linux/aarch64/makecontext.c
> @@ -22,6 +22,52 @@
>  #include <stdint.h>
>  #include <ucontext.h>
>  
> +#define GCS_MAGIC 0x47435300
> +
> +static struct _aarch64_ctx *extension (void *p)
> +{
> +  return p;
> +}
> +
> +#ifndef __NR_map_shadow_stack
> +# define __NR_map_shadow_stack 453
> +#endif
> +#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 == (void *) -1)
> +    /* 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 +102,21 @@ __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;
> +      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 e52d7d926a..3224c36c47 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)



More information about the Libc-alpha mailing list