From patchwork Fri Jan 3 15:41:27 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yury Khrustalev X-Patchwork-Id: 103958 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 1F6093858C33 for ; Fri, 3 Jan 2025 15:47:44 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 1F6093858C33 X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by sourceware.org (Postfix) with ESMTP id 8CCCC3858408 for ; Fri, 3 Jan 2025 15:42:41 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 8CCCC3858408 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=arm.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 8CCCC3858408 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1735918961; cv=none; b=eNAeaBscVI3GOq/gpRgwMTjJSIFA/qJY9QcDSqLRBA14ahnfHfw3yg7ITp6Tr1filurpWsoM0lZaHg/vjpy3uT02PxlohsoStFS0ymXMLe2Sh0zpPpaoZbh8QUbvisOwCeKSvdER84fykrLS0yGnJa/ExvcSWJvSGeZ8aUJP9vI= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1735918961; c=relaxed/simple; bh=pA+4z3peJUmlvWBZ02tli42t4it0bMTxyQwLRLYyUDM=; h=From:To:Subject:Date:Message-Id:MIME-Version; b=qQiI1YxmL74BcSvCxVJ8g11htLoCjQEjPTewJFTbYEnG6WRcO2jFDrraZHk4KKv2YchQat/rmPvu4byokb6+nSViPFQ5OyrFVv0oBYeinCj7cb69nrUHbawAPsIsOPpLnAecZyA3xxmxIm+rEFyBK2Vc06fZgZyCDjOzEv5tkPM= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 8CCCC3858408 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 8DC911480; Fri, 3 Jan 2025 07:43:09 -0800 (PST) Received: from udebian.localdomain (unknown [10.57.3.206]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 7182D3F6A8; Fri, 3 Jan 2025 07:42:40 -0800 (PST) From: Yury Khrustalev To: libc-alpha@sourceware.org Cc: fweimer@redhat.com, adhemerval.zanella@linaro.org, codonell@redhat.com, nsz@gcc.gnu.org, schwab@suse.de, wilco.dijkstra@arm.com Subject: [PATCH v7 09/23] aarch64: Try to free the GCS of makecontext Date: Fri, 3 Jan 2025 15:41:27 +0000 Message-Id: <20250103154141.47731-10-yury.khrustalev@arm.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250103154141.47731-1-yury.khrustalev@arm.com> References: <20250103154141.47731-1-yury.khrustalev@arm.com> MIME-Version: 1.0 X-Spam-Status: No, score=-13.3 required=5.0 tests=BAYES_00, GIT_PATCH_0, KAM_DMARC_NONE, KAM_DMARC_STATUS, KAM_LAZY_DOMAIN_SECURITY, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org From: Szabolcs Nagy 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 | 2 + malloc/thread-freeres.c | 9 +++ sysdeps/unix/sysv/linux/aarch64/makecontext.c | 65 +++++++++++++++++++ sysdeps/unix/sysv/linux/aarch64/setcontext.S | 19 +++++- sysdeps/unix/sysv/linux/aarch64/sysdep.h | 6 +- 5 files changed, 97 insertions(+), 4 deletions(-) diff --git a/include/set-freeres.h b/include/set-freeres.h index bb1d0a8f72..0f81344e43 100644 --- a/include/set-freeres.h +++ b/include/set-freeres.h @@ -78,6 +78,8 @@ 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. */ +extern void ARCH_THREAD_FREERES (void) attribute_hidden; /* From either libc.so or libpthread.so */ extern void __libpthread_freeres (void) attribute_hidden; diff --git a/malloc/thread-freeres.c b/malloc/thread-freeres.c index c7ec9f99e9..5ed5df1a3f 100644 --- a/malloc/thread-freeres.c +++ b/malloc/thread-freeres.c @@ -22,6 +22,14 @@ #include #include +/* Define empty function if no arch-specific clean-up + function has been defined. */ +#ifndef ARCH_THREAD_FREERES +void __always_inline +__libc_arch_thread_freeres (void) {} +#define ARCH_THREAD_FREERES __libc_arch_thread_freeres +#endif + /* Thread shutdown function. Note that this function must be called for threads during shutdown for correctness reasons. Unlike __libc_freeres, skipping calls to it is not a valid optimization. @@ -29,6 +37,7 @@ void __libc_thread_freeres (void) { + call_function_static_weak (ARCH_THREAD_FREERES); #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_32) __rpc_thread_destroy (); #endif diff --git a/sysdeps/unix/sysv/linux/aarch64/makecontext.c b/sysdeps/unix/sysv/linux/aarch64/makecontext.c index 99ba5b3f7c..f43fc45c76 100644 --- a/sysdeps/unix/sysv/linux/aarch64/makecontext.c +++ b/sysdeps/unix/sysv/linux/aarch64/makecontext.c @@ -20,7 +20,9 @@ #include #include #include +#include #include +#include #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: diff --git a/sysdeps/unix/sysv/linux/aarch64/setcontext.S b/sysdeps/unix/sysv/linux/aarch64/setcontext.S index 695fc5b9b5..f926a5dd84 100644 --- a/sysdeps/unix/sysv/linux/aarch64/setcontext.S +++ b/sysdeps/unix/sysv/linux/aarch64/setcontext.S @@ -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) diff --git a/sysdeps/unix/sysv/linux/aarch64/sysdep.h b/sysdeps/unix/sysv/linux/aarch64/sysdep.h index b813805931..e2fc7e21a6 100644 --- a/sysdeps/unix/sysv/linux/aarch64/sysdep.h +++ b/sysdeps/unix/sysv/linux/aarch64/sysdep.h @@ -29,8 +29,12 @@ #include -/* 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 #endif