From patchwork Fri Jun 5 09:55:36 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Wupeng X-Patchwork-Id: 136573 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 162584BA799F for ; Fri, 5 Jun 2026 09:56:43 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 162584BA799F X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from h3cspam02-ex.h3c.com (smtp.h3c.com [60.191.123.50]) by sourceware.org (Postfix) with ESMTPS id 6049E4BA5436 for ; Fri, 5 Jun 2026 09:56:05 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 6049E4BA5436 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=h3c.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=h3c.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 6049E4BA5436 Authentication-Results: sourceware.org; arc=none smtp.remote-ip=60.191.123.50 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1780653366; cv=none; b=J9FmGmmC2DlXALDLW+XcJL03UsfaV4prTFnXXfAnGTz2S8iTYWj7fM+jiv+ofdzqFXXrXtq0Y+DloZfo586UVcO+8CdR2HG9WVpK1sfyGAGpV+cn8moX7Ax40YbSsqjMs2s+LDqMNSigl1eMDzzlhL4woi2rAGDdMAt97RFEklI= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1780653366; c=relaxed/simple; bh=G9NZVLE6wOKEh+LbZXMqoIfQmtKmzlC/JZ3H1tYBMCY=; h=From:To:Subject:Date:Message-ID:MIME-Version; b=j1ti2QfZ1IgVNCMVj2yYTbqhkRQGK0L4lS4hBaup6SOZ83KhQKILByWKgPpTgSR2CJWmcj/ImoZ+WdwkGU1fYJmTUAHe/dndxqip6xXYgZwCjM1R5xNbB0B8hFegnfMyiwl8OU0bfVYSxbYpxbRlqcotThW34zrdciL7uCHZTiI= ARC-Authentication-Results: i=1; sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 6049E4BA5436 Received: from mail.maildlp.com ([172.25.15.154]) by h3cspam02-ex.h3c.com with ESMTP id 6559tkbZ048241; Fri, 5 Jun 2026 17:55:46 +0800 (+08) (envelope-from wu.pengA@h3c.com) Received: from DAG6EX13-BJD.srv.huawei-3com.com (unknown [10.153.34.15]) by mail.maildlp.com (Postfix) with ESMTP id 5E33320045A8; Fri, 5 Jun 2026 18:08:11 +0800 (CST) Received: from DAG6EX10-BJD.srv.huawei-3com.com (10.153.34.12) by DAG6EX13-BJD.srv.huawei-3com.com (10.153.34.15) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1258.27; Fri, 5 Jun 2026 17:55:36 +0800 Received: from DAG6EX10-BJD.srv.huawei-3com.com ([fe80::ade6:b219:16f8:9aa8]) by DAG6EX10-BJD.srv.huawei-3com.com ([fe80::ade6:b219:16f8:9aa8%6]) with mapi id 15.02.1258.027; Fri, 5 Jun 2026 17:55:36 +0800 From: Wupeng To: "libc-alpha@sourceware.org'" CC: "glibc@sourceware.org'" , Lumingxiang , Wangshijie , Zhangchun Subject: [PATCH] nptl: avoid invalid memory access in pthread_join after fork Thread-Topic: [PATCH] nptl: avoid invalid memory access in pthread_join after fork Thread-Index: Adz00UTsaldC+6ejSXaKpFgc266HHw== Date: Fri, 5 Jun 2026 09:55:36 +0000 Message-ID: <422aa1208b23451a85c4290cbd9bc19b@h3c.com> Accept-Language: en-US Content-Language: zh-CN X-MS-Has-Attach: X-MS-TNEF-Correlator: x-originating-ip: [10.142.33.32] x-sender-location: DAG2 MIME-Version: 1.0 X-DNSRBL: X-SPAM-SOURCE-CHECK: pass X-MAIL: h3cspam02-ex.h3c.com 6559tkbZ048241 X-Spam-Status: No, score=-8.8 required=5.0 tests=BAYES_00, BODY_8BITS, GIT_PATCH_0, HTML_MESSAGE, KAM_DMARC_STATUS, MIME_CHARSET_FARAWAY, SPF_HELO_NONE, SPF_PASS, TXREP shortcircuit=no autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on 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 Hi all, We recently encountered a crash scenario related to pthread_join() after fork(), and would like to propose a fix for discussion. Problem description =================== In application, a shared library creates worker threads internally and joins them in the library destructor. Under normal conditions this works correctly. However, after fork(), the child process may crash with SIGSEGV during pthread_join(). After reviewing the the glibc internals, we found that: After fork(), only the calling thread survives in the child process. All other threads are terminated, but their stack mappings are not unmapped immediately . Instead, glibc moves them into dl_stack_cache: list_splice (&GL (dl_stack_used), &GL (dl_stack_cache)); // sysdeps/nptl/fork.h:135 These cached stacks may later be partially unmapped when the cache exceeds its size limit: if (__glibc_unlikely (GL (dl_stack_cache_actsize) > __nptl_stack_cache_maxsize)) __nptl_free_stacks (__nptl_stack_cache_maxsize); This means that a stale pthread_t in the child process may eventually point to memory that has already been unmapped or reused. If pthread_join() is called on such a thread descriptor, glibc may dereference invalid memory and crash. Calling pthread_join() on threads that no longer exist after fork() is technically undefined behavior. However, in real-world engineering scenarios this is difficult to avoid completely, especially when: - pthread_join() is located in shared cleanup/destructor paths - parent and child processes share common cleanup logic - third-party libraries internally manage threads It may be worthwhile for glibc to handle this case more defensively, to avoid potential segmentation faults. Proposed fix ============ The patch checks whether the target pthread descriptor still exists in: - dl_stack_used - dl_stack_user If the descriptor is already in dl_stack_cache, or no longer exists in any known stack list, pthread_join() returns ESRCH instead of dereferencing potentially invalid memory. Patch --- commit 5966c8e3750f3fa9555b0693e3510e705168be5e Author: PengWu Date: Fri Jun 5 17:15:39 2026 +0800 nptl: avoid invalid memory access in pthread_join after fork Signed-off-by: PengWu Co-authored-by: MingxiangLu ----- Testcase: #include #include #include #include #include #include #include #define THREAD_STACK_SIZE (256 * 1024) int thread_count = 0; void *thread_func(void *arg) { sleep(10); return NULL; } int main(int argc, char *argv[]) { int thread_max; pthread_t *threads; pthread_attr_t attr; pid_t pid; int i, ret; if (argc != 2) return 1; thread_max = atoi(argv[1]); threads = malloc(sizeof(pthread_t) * thread_max); pthread_attr_init(&attr); pthread_attr_setstacksize(&attr, THREAD_STACK_SIZE); for (i = 0; i < thread_max; i++) { ret = pthread_create(&threads[i], &attr, thread_func, NULL); if (ret != 0) break; thread_count++; } pid = fork(); if (pid == 0) { printf("Child process start\n"); } else { int status; printf("Parent process wait child pid=%d\n", pid); waitpid(pid, &status, 0); printf("Parent detected child exit\n"); if (WIFSIGNALED(status)) { printf("child killed by signal %d\n", WTERMSIG(status)); } } for (i = 0; i < thread_count; i++) { ret = pthread_join(threads[i], NULL); if (ret) printf("pthread_join error: %s\n", strerror(ret)); } return 0; } Reproducer result before fix: Main process before fork, pid=1616 Parent process wait child pid=1717 Child process start, pid=1717 ... tid[58]:0x7f3e19a156c0 child killed by signal 11 Reproducer result after fix: Main process before fork, pid=1732 Parent process wait child pid=1833 Child process start, pid=1833 ... pthread_join threads[96] error: Resource temporarily unavailable(ret=11) ... pthread_join over ------------------------------------------------------------------------------------------------------------------------------------- 本邮件及其附件含有新华三集团的保密信息,仅限于发送给上面地址中列出的个人或群组。 禁止任何其他人以任何形式使用(包括但不限于全部或部分地泄露、复制、或散发)本邮件中的信息。 如果您错收了本邮件,请您立即电话或邮件通知发件人并删除本邮件! This e-mail and its attachments contain confidential information from New H3C, which is intended only for the person or entity whose address is listed above. Any use of the information contained herein in any way (including, but not limited to, total or partial disclosure, reproduction, or dissemination) by persons other than the intended recipient(s) is prohibited. If you receive this e-mail in error, please notify the sender by phone or email immediately and delete it! diff --git a/nptl/pthread_join_common.c b/nptl/pthread_join_common.c index 701f6a53..f4f1bfe6 100644 --- a/nptl/pthread_join_common.c +++ b/nptl/pthread_join_common.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include static void cleanup (void *arg) @@ -39,6 +41,37 @@ __pthread_clockjoin_ex (pthread_t threadid, void **thread_return, { struct pthread *pd = (struct pthread *) threadid; + lll_lock (GL (dl_stack_cache_lock), LLL_PRIVATE); + int tid_in_maps = 0; + list_t *runp; + + list_for_each (runp, &GL (dl_stack_cache)) + { + struct pthread *curr = list_entry (runp, struct pthread, list); + if (pd == curr) + return ESRCH; + } + list_for_each (runp, &GL (dl_stack_used)) + { + struct pthread *curr = list_entry (runp, struct pthread, list); + if (tid_in_maps) + break; + if (pd == curr) + tid_in_maps = 1; + } + list_for_each (runp, &GL (dl_stack_user)) + { + struct pthread *curr = list_entry (runp, struct pthread, list); + if (tid_in_maps) + break; + if (pd == curr) + tid_in_maps = 1; + } + lll_unlock (GL (dl_stack_cache_lock), LLL_PRIVATE); + + if (!tid_in_maps) + return ESRCH; + /* Make sure the descriptor is valid. */ if (INVALID_NOT_TERMINATED_TD_P (pd)) /* Not a valid thread handle. */