]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
aarch64: Try to free the GCS of makecontext
authorSzabolcs Nagy <szabolcs.nagy@arm.com>
Mon, 17 Jul 2023 15:54:15 +0000 (16:54 +0100)
committerYury Khrustalev <yury.khrustalev@arm.com>
Thu, 10 Oct 2024 12:40:35 +0000 (13:40 +0100)
Free GCS after a makecontext start func returns and at thread exit, so
assume makecontext cannot outlive the thread where it was created.

This is an attempt to bound the lifetime of the GCS allocated for
makecontext, but it is still possible to have significant GCS leaks,
new GCS aware APIs could solve that, but that would not allow using
GCS with existing code transparently.

include/set-freeres.h
malloc/thread-freeres.c
sysdeps/unix/sysv/linux/aarch64/makecontext.c
sysdeps/unix/sysv/linux/aarch64/setcontext.S
sysdeps/unix/sysv/linux/aarch64/sysdep.h

index 4177b453faf1f975d98dbcf862dc0be5ea09d19a..c3d64b4f4150657aa57ab34c6176dea364ae8509 100644 (file)
@@ -78,6 +78,10 @@ extern void __nss_database_freeres (void) attribute_hidden;
 extern int _IO_cleanup (void) attribute_hidden;;
 /* From dlfcn/dlerror.c */
 extern void __libc_dlerror_result_free (void) attribute_hidden;
+/* From libc.so, arch specific.  */
+#ifdef ARCH_THREAD_FREERES
+extern void ARCH_THREAD_FREERES (void) attribute_hidden;
+#endif
 
 /* From either libc.so or libpthread.so  */
 extern void __libpthread_freeres (void) attribute_hidden;
index 55ba4e7b835be38ead13c0d3049932bae3047541..69867f3a3bc237725970fb0ec46558daaae78bfc 100644 (file)
@@ -29,6 +29,9 @@
 void
 __libc_thread_freeres (void)
 {
+#ifdef ARCH_THREAD_FREERES
+  call_function_static_weak (ARCH_THREAD_FREERES);
+#endif
 #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_32)
   __rpc_thread_destroy ();
 #endif
index 9e66b6761cf7cd4719aec6174be30ebb8665df62..779f7e55aa9f3e8ef7a2e85dc04518ec06ce2ab1 100644 (file)
@@ -20,7 +20,9 @@
 #include <sysdep.h>
 #include <stdarg.h>
 #include <stdint.h>
+#include <stdlib.h>
 #include <ucontext.h>
+#include <sys/mman.h>
 
 #define GCS_MAGIC 0x47435300
 
@@ -29,6 +31,47 @@ static struct _aarch64_ctx *extension (void *p)
   return p;
 }
 
+struct gcs_list {
+  struct gcs_list *next;
+  void *base;
+  size_t size;
+};
+
+static __thread struct gcs_list *gcs_list_head = NULL;
+
+static void
+record_gcs (void *base, size_t size)
+{
+  struct gcs_list *p = malloc (sizeof *p);
+  if (p == NULL)
+    abort ();
+  p->base = base;
+  p->size = size;
+  p->next = gcs_list_head;
+  gcs_list_head = p;
+}
+
+static void
+free_gcs_list (void)
+{
+  for (;;)
+    {
+      struct gcs_list *p = gcs_list_head;
+      if (p == NULL)
+       break;
+      gcs_list_head = p->next;
+      __munmap (p->base, p->size);
+      free (p);
+    }
+}
+
+/* Called during thread shutdown to free resources.  */
+void
+__libc_aarch64_thread_freeres (void)
+{
+  free_gcs_list ();
+}
+
 #ifndef __NR_map_shadow_stack
 # define __NR_map_shadow_stack 453
 #endif
@@ -58,6 +101,9 @@ alloc_makecontext_gcs (size_t stack_size)
   if (base == (void *) -1)
     /* ENOSYS, bad size or OOM.  */
     abort ();
+
+  record_gcs (base, size);
+
   uint64_t *gcsp = (uint64_t *) ((char *) base + size);
   /* Skip end of GCS token.  */
   gcsp--;
@@ -69,6 +115,25 @@ alloc_makecontext_gcs (size_t stack_size)
   return gcsp + 1;
 }
 
+void
+__free_makecontext_gcs (void *gcs)
+{
+  struct gcs_list *p = gcs_list_head;
+  struct gcs_list **q = &gcs_list_head;
+  for (;;)
+    {
+      if (p == NULL)
+       abort ();
+      if (gcs == p->base + p->size - 8)
+       break;
+      q = &p->next;
+      p = p->next;
+    }
+  *q = p->next;
+  __munmap (p->base, p->size);
+  free (p);
+}
+
 /* makecontext sets up a stack and the registers for the
    user context.  The stack looks like this:
 
index 17b0a48ce116a0926e5a0dc28a76d840f9550eca..853dcb7a7341821e37977b72f29c67a4c7d81ba4 100644 (file)
@@ -34,6 +34,9 @@
        .text
 
 ENTRY (__setcontext)
+       /* If x10 is set then old GCS is freed.  */
+       mov     x10, 0
+__setcontext_internal:
        PTR_ARG (0)
        /* Save a copy of UCP.  */
        mov     x9, x0
@@ -145,7 +148,8 @@ ENTRY (__setcontext)
        ldr     x3, [x2, #oGCSPR]
        MRS_GCSPR (x2)
        mov     x4, x3
-       /* x2: GCSPR now.  x3, x4: target GCSPR.  x5, x6: tmp regs.  */
+       mov     x1, x2
+       /* x1, x2: GCSPR now.  x3, x4: target GCSPR.  x5, x6: tmp regs.  */
 L(gcs_scan):
        cmp     x2, x4
        b.eq    L(gcs_pop)
@@ -162,10 +166,18 @@ L(gcs_switch):
        GCSSS2 (xzr)
 L(gcs_pop):
        cmp     x2, x3
-       b.eq    L(gcs_done)
+       b.eq    L(gcs_free_old)
        GCSPOPM (xzr)
        add     x2, x2, 8
        b       L(gcs_pop)
+L(gcs_free_old):
+       cbz     x10, L(gcs_done)
+       mov     x28, x0
+       mov     x0, x1
+       bl      __free_makecontext_gcs
+       mov     x0, x28
+       ldp     x28, x29, [x0, oX0 + 28 * SZREG]
+       ldr     x30,      [x0, oX0 + 30 * SZREG]
 L(gcs_done):
 
 2:
@@ -186,6 +198,7 @@ ENTRY (__startcontext)
        cfi_undefined (x30)
        blr     x20
        mov     x0, x19
-       cbnz    x0, __setcontext
+       mov     x10, 1
+       cbnz    x0, __setcontext_internal
 1:     b       HIDDEN_JUMPTARGET (exit)
 END (__startcontext)
index bbbe35723cac80ef1b1202fe4c173397d0d41af9..590318dee806745156d4ab2c871fd452e7c73f8d 100644 (file)
 
 #include <tls.h>
 
-/* In order to get __set_errno() definition in INLINE_SYSCALL.  */
 #ifndef __ASSEMBLER__
+/* Thread cleanup function.  */
+#define ARCH_THREAD_FREERES __libc_aarch64_thread_freeres
+void __libc_aarch64_thread_freeres (void) attribute_hidden;
+
+/* In order to get __set_errno() definition in INLINE_SYSCALL.  */
 #include <errno.h>
 #endif