aarch64: Try to free the GCS of makecontext

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.
This commit is contained in:
Szabolcs Nagy 2023-07-17 16:54:15 +01:00 committed by Yury Khrustalev
parent 491de01415
commit d2768c779c
5 changed files with 93 additions and 4 deletions

View 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;

View 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

View 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:

View 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)

View File

@ -29,8 +29,12 @@
#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