[v7,21/23] aarch64: Add GCS user-space allocation logic

Message ID 20250103154141.47731-22-yury.khrustalev@arm.com (mailing list archive)
State Superseded
Headers
Series aarch64: Add support for Guarded Control Stack extension |

Checks

Context Check Description
redhat-pt-bot/TryBot-apply_patch success Patch applied to master at the time it was sent

Commit Message

Yury Khrustalev Jan. 3, 2025, 3:41 p.m. UTC
  From: Szabolcs Nagy <szabolcs.nagy@arm.com>

Allocate GCS based on the stack size, this can be used for coroutines
(makecontext) and thread creation (if the kernel allows user allocated
GCS).

Reviewed-by: Wilco Dijkstra <Wilco.Dijkstra@arm.com>
---
 sysdeps/aarch64/Makefile      |  3 +-
 sysdeps/aarch64/__alloc_gcs.c | 66 +++++++++++++++++++++++++++++++++++
 sysdeps/aarch64/aarch64-gcs.h | 28 +++++++++++++++
 3 files changed, 96 insertions(+), 1 deletion(-)
 create mode 100644 sysdeps/aarch64/__alloc_gcs.c
 create mode 100644 sysdeps/aarch64/aarch64-gcs.h
  

Comments

Adhemerval Zanella Netto Jan. 7, 2025, 5:11 p.m. UTC | #1
On 03/01/25 12:41, Yury Khrustalev wrote:
> From: Szabolcs Nagy <szabolcs.nagy@arm.com>
> 
> Allocate GCS based on the stack size, this can be used for coroutines
> (makecontext) and thread creation (if the kernel allows user allocated
> GCS).
> 
> Reviewed-by: Wilco Dijkstra <Wilco.Dijkstra@arm.com>
> ---
>  sysdeps/aarch64/Makefile      |  3 +-
>  sysdeps/aarch64/__alloc_gcs.c | 66 +++++++++++++++++++++++++++++++++++
>  sysdeps/aarch64/aarch64-gcs.h | 28 +++++++++++++++
>  3 files changed, 96 insertions(+), 1 deletion(-)
>  create mode 100644 sysdeps/aarch64/__alloc_gcs.c
>  create mode 100644 sysdeps/aarch64/aarch64-gcs.h
> 
> diff --git a/sysdeps/aarch64/Makefile b/sysdeps/aarch64/Makefile
> index 74479604f2..4b7f8a5c07 100644
> --- a/sysdeps/aarch64/Makefile
> +++ b/sysdeps/aarch64/Makefile
> @@ -71,7 +71,8 @@ sysdep_headers += sys/ifunc.h
>  sysdep_routines += \
>    __mtag_tag_zero_region \
>    __mtag_tag_region \
> -  __arm_za_disable
> +  __arm_za_disable \
> +  __alloc_gcs
>  
>  tests += \
>    tst-sme-jmp
> diff --git a/sysdeps/aarch64/__alloc_gcs.c b/sysdeps/aarch64/__alloc_gcs.c
> new file mode 100644
> index 0000000000..8268c78abd
> --- /dev/null
> +++ b/sysdeps/aarch64/__alloc_gcs.c
> @@ -0,0 +1,66 @@
> +/* AArch64 GCS allocation.
> +   Copyright (C) 2024-2025 Free Software Foundation, Inc.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <sysdep.h>
> +#include <unistd.h>
> +#include <sys/mman.h>
> +
> +#ifndef __NR_map_shadow_stack
> +# define __NR_map_shadow_stack 453
> +#endif

This is already define at sysdeps/unix/sysv/linux/aarch64/arch-syscall.h

> +#ifndef SHADOW_STACK_SET_TOKEN
> +# define SHADOW_STACK_SET_TOKEN (1UL << 0)
> +# define SHADOW_STACK_SET_MARKER (1UL << 1)

x86 defines SHADOW_STACK_SET_TOKEN on its bits/mman.h, maybe do the same.

> +#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
> +
> +void *
> +__alloc_gcs (size_t stack_size, void **ss_base, size_t *ss_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)

I think map_shadow_stack is essentially a mmap wrapper, so maybe use
MMAP_FAILED here.

> +    return NULL;
> +
> +  *ss_base = base;
> +  *ss_size = size;
> +
> +  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)

This is essentially aliasing violations, but since aarch64 allows unaligned
access I think this is fine.

> +    {
> +      __munmap (base, size);
> +      return NULL;
> +    }
> +  /* Return the target GCS pointer for context switch.  */
> +  return gcsp + 1;
> +}
> diff --git a/sysdeps/aarch64/aarch64-gcs.h b/sysdeps/aarch64/aarch64-gcs.h
> new file mode 100644
> index 0000000000..162ef18726
> --- /dev/null
> +++ b/sysdeps/aarch64/aarch64-gcs.h
> @@ -0,0 +1,28 @@
> +/* AArch64 GCS (Guarded Control Stack) declarations.
> +   This file is part of the GNU C Library.
> +   Copyright (C) 2024-2025 Free Software Foundation, Inc.
> +   Copyright The GNU Toolchain Authors.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#ifndef _AARCH64_GCS_H
> +#define _AARCH64_GCS_H
> +
> +#include <stddef.h>
> +#include <stdbool.h>
> +
> +void *__alloc_gcs (size_t, void **, size_t *) attribute_hidden;
> +
> +#endif
  
Yury Khrustalev Jan. 8, 2025, 2:03 p.m. UTC | #2
On Tue, Jan 07, 2025 at 02:11:13PM -0300, Adhemerval Zanella Netto wrote:
> 
> > +#include <sysdep.h>
> > +#include <unistd.h>
> > +#include <sys/mman.h>
> > +
> > +#ifndef __NR_map_shadow_stack
> > +# define __NR_map_shadow_stack 453
> > +#endif
> 
> This is already define at sysdeps/unix/sysv/linux/aarch64/arch-syscall.h

Fair point. I will remove this bit.

> 
> > +#ifndef SHADOW_STACK_SET_TOKEN
> > +# define SHADOW_STACK_SET_TOKEN (1UL << 0)
> > +# define SHADOW_STACK_SET_MARKER (1UL << 1)
> 
> x86 defines SHADOW_STACK_SET_TOKEN on its bits/mman.h, maybe do the same.

The idea here is to define these constants locally rather than in
any of the public headers. Once Linux kernel is released there will
be another patch to declare some of these constants in the public
headers, but this will likely be after the upcoming Glibc release.

I will leave this unchanged.

> > +  unsigned long flags = SHADOW_STACK_SET_MARKER | SHADOW_STACK_SET_TOKEN;
> > +  void *base = map_shadow_stack (NULL, size, flags);
> > +  if (base == (void *) -1)
> 
> I think map_shadow_stack is essentially a mmap wrapper, so maybe use
> MMAP_FAILED here.

I will use MAP_FAILED here, it makes sense.

> > +  if (((uint64_t)gcsp & 0xfffffffffffff000) + 1 != *gcsp)
> 
> This is essentially aliasing violations, but since aarch64 allows unaligned
> access I think this is fine.

OK.

Thanks,
Yury
  

Patch

diff --git a/sysdeps/aarch64/Makefile b/sysdeps/aarch64/Makefile
index 74479604f2..4b7f8a5c07 100644
--- a/sysdeps/aarch64/Makefile
+++ b/sysdeps/aarch64/Makefile
@@ -71,7 +71,8 @@  sysdep_headers += sys/ifunc.h
 sysdep_routines += \
   __mtag_tag_zero_region \
   __mtag_tag_region \
-  __arm_za_disable
+  __arm_za_disable \
+  __alloc_gcs
 
 tests += \
   tst-sme-jmp
diff --git a/sysdeps/aarch64/__alloc_gcs.c b/sysdeps/aarch64/__alloc_gcs.c
new file mode 100644
index 0000000000..8268c78abd
--- /dev/null
+++ b/sysdeps/aarch64/__alloc_gcs.c
@@ -0,0 +1,66 @@ 
+/* AArch64 GCS allocation.
+   Copyright (C) 2024-2025 Free Software Foundation, Inc.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <sysdep.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#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
+
+void *
+__alloc_gcs (size_t stack_size, void **ss_base, size_t *ss_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)
+    return NULL;
+
+  *ss_base = base;
+  *ss_size = size;
+
+  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)
+    {
+      __munmap (base, size);
+      return NULL;
+    }
+  /* Return the target GCS pointer for context switch.  */
+  return gcsp + 1;
+}
diff --git a/sysdeps/aarch64/aarch64-gcs.h b/sysdeps/aarch64/aarch64-gcs.h
new file mode 100644
index 0000000000..162ef18726
--- /dev/null
+++ b/sysdeps/aarch64/aarch64-gcs.h
@@ -0,0 +1,28 @@ 
+/* AArch64 GCS (Guarded Control Stack) declarations.
+   This file is part of the GNU C Library.
+   Copyright (C) 2024-2025 Free Software Foundation, Inc.
+   Copyright The GNU Toolchain Authors.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#ifndef _AARCH64_GCS_H
+#define _AARCH64_GCS_H
+
+#include <stddef.h>
+#include <stdbool.h>
+
+void *__alloc_gcs (size_t, void **, size_t *) attribute_hidden;
+
+#endif