From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id MbSXOOxxi2lO7TMAWB0awg (envelope-from ) for ; Tue, 10 Feb 2026 12:59:08 -0500 Authentication-Results: simark.ca; dkim=pass (2048-bit key; unprotected) header.d=ibm.com header.i=@ibm.com header.a=rsa-sha256 header.s=pp1 header.b=JS20txK5; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id C8E961E08D; Tue, 10 Feb 2026 12:59:08 -0500 (EST) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-2.3 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIM_SIGNED,DKIM_VALID,HTML_MESSAGE,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,RCVD_IN_VALIDITY_CERTIFIED_BLOCKED, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id C51281E08D for ; Tue, 10 Feb 2026 12:59:06 -0500 (EST) Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 03C464BA2E24 for ; Tue, 10 Feb 2026 17:59:06 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 03C464BA2E24 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=ibm.com header.i=@ibm.com header.a=rsa-sha256 header.s=pp1 header.b=JS20txK5 Received: from mx0a-001b2d01.pphosted.com (mx0a-001b2d01.pphosted.com [148.163.156.1]) by sourceware.org (Postfix) with ESMTPS id 53AF94BA2E10 for ; Tue, 10 Feb 2026 17:58:35 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 53AF94BA2E10 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=linux.ibm.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=linux.ibm.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 53AF94BA2E10 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=148.163.156.1 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1770746315; cv=none; b=jSdYqrMxSViNUuwImDZ2L2D3Az939xz5Nk63uRdzHKEIjhZNbqhrcscWSzXzXyuIZB+5zNpdpbrGKjc1lNrwig7jWKBJgwb3PmtUtsnkE7SducuBH5HqqIrdXoWgkC+g1uB3tG/JILXIlVtym6RibzDtkw+cpTnuEj/by5YM+PE= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1770746315; c=relaxed/simple; bh=ux87rxAGToiq6oCC29snmTaVRv7UADWODI+rRUy3OKs=; h=DKIM-Signature:Message-ID:Date:MIME-Version:Subject:To:From; b=V6+aaLjpXmjFjm9I4rUewAi85E+DcZEZLYz/XuoR+QG9pamXQ5VA/IosSkV9C1UlqCV6WF42fsRd0zHtyKBn/l//mFJ504fC0EjlloTD09loSFoZrchMtAP/2qPqNINnNwmMGYmVj3crvFnQ2hFmn9u7zmnHUphVdmEvaV5BuAo= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 53AF94BA2E10 Received: from pps.filterd (m0353729.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 61AAgrrg326349; Tue, 10 Feb 2026 17:58:32 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ibm.com; h= content-type:date:from:in-reply-to:message-id:mime-version :references:subject:to; s=pp1; bh=e6edxsYLinTcAUI4k1W/807rXCRMR2 UW/b3S7eodIm8=; b=JS20txK5st910zq4U9JW67otCFaKSFyJSWAvfrbNo1fEGI WA3a84hZ4NCXJRQ0W67c5kglIHIfUwjv3qlmQE/VIIUr+HN77qrmSHx87visPy9y RiR2pqEd/23HIPbNXWfD+jrxDh4zCnPqLCgJrI482/lWarJ7GxG+8DPT08SJThwt DZqueqLOn6H0KwSdhtyFXZ4pNoCiUxvpzEITWwH3yb5CzEHgUcbYnq6ojjh2ESlg n9xbl3dOOt7MLrR6eU2juTnUnrKRg/4OLebQOqzFVlDyfd5VnzWDdr1zX8Pm3X2b QBBUP3G9h0itPrchRm4ei3ME3yOuErjN1JScP9Xg== Received: from ppma22.wdc07v.mail.ibm.com (5c.69.3da9.ip4.static.sl-reverse.com [169.61.105.92]) by mx0a-001b2d01.pphosted.com (PPS) with ESMTPS id 4c696w5n2e-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 10 Feb 2026 17:58:32 +0000 (GMT) Received: from pps.filterd (ppma22.wdc07v.mail.ibm.com [127.0.0.1]) by ppma22.wdc07v.mail.ibm.com (8.18.1.2/8.18.1.2) with ESMTP id 61AHm1Eg008390; Tue, 10 Feb 2026 17:58:31 GMT Received: from smtprelay06.wdc07v.mail.ibm.com ([172.16.1.73]) by ppma22.wdc07v.mail.ibm.com (PPS) with ESMTPS id 4c6g3yajyr-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 10 Feb 2026 17:58:31 +0000 Received: from smtpav05.wdc07v.mail.ibm.com (smtpav05.wdc07v.mail.ibm.com [10.39.53.232]) by smtprelay06.wdc07v.mail.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id 61AHwUhN27919020 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 10 Feb 2026 17:58:30 GMT Received: from smtpav05.wdc07v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 9241558053; Tue, 10 Feb 2026 17:58:30 +0000 (GMT) Received: from smtpav05.wdc07v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 1D58458043; Tue, 10 Feb 2026 17:58:29 +0000 (GMT) Received: from [9.111.140.9] (unknown [9.111.140.9]) by smtpav05.wdc07v.mail.ibm.com (Postfix) with ESMTP; Tue, 10 Feb 2026 17:58:28 +0000 (GMT) Content-Type: multipart/alternative; boundary="------------3FfuAC8bjvLpY89q6CZozcTb" Message-ID: Date: Tue, 10 Feb 2026 23:28:27 +0530 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH] gcore: Handle unreadable pages within readable memory regions Content-Language: en-GB To: Kevin Buettner , gdb-patches@sourceware.org References: <20260130082212.2002944-2-kevinb@redhat.com> From: Abhay Kandpal In-Reply-To: <20260130082212.2002944-2-kevinb@redhat.com> X-TM-AS-GCONF: 00 X-Authority-Analysis: v=2.4 cv=YeCwJgRf c=1 sm=1 tr=0 ts=698b71c8 cx=c_pps a=5BHTudwdYE3Te8bg5FgnPg==:117 a=5BHTudwdYE3Te8bg5FgnPg==:17 a=HzLeVaNsDn8A:10 a=VkNPw1HP01LnGYTKEx00:22 a=Mpw57Om8IfrbqaoTuvik:22 a=GgsMoib0sEa3-_RKJdDe:22 a=r77TgQKjGQsHNAKrUKIA:9 a=VwQbUJbxAAAA:8 a=CCpqsmhAAAAA:8 a=Gd1C5fCPD0zy7c3WJKAA:9 a=3ZKOabzyN94A:10 a=QEXdDO2ut3YA:10 a=20KFwNOVAAAA:8 a=rJ296VyCdUD6NjR-:21 a=_W_S_7VecoQA:10 a=lqcHg5cX4UMA:10 a=ul9cdbp4aOFLsgKbc677:22 X-Proofpoint-GUID: j9XWWTFMPSCfRzqF9qNOZxbXy2wOgDZn X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwMjEwMDE0OCBTYWx0ZWRfX3umjJFi62TaZ gG/KmA/Mud7DutFtJ+RBgLm5l14MiOIr479cTQyzBIWvx+j3peMuEccanpilgobzSrghuMR7BXf nSbT/1TUy+T09oiALFidTUCQuFPe9Ffm4qyVQ0t0rs7Gq8t6M3yBo6pUIY/wXh8GxRPF2qxQ5+P LyuEHlQNw9lacbTwHLwntTydlASC83rbVn8jibzFRGmgsYeqxdyz2YFDJzzGi+UFdTCyg+FwQmN Rw1+nwAkBP0Px2W4AgWa7UF38ctjYqWXT0bMTAUSMN3UHrqhzK+lORv++KmKL8yGOS4+baiz4ti sq9vy+B0A5reSC3mPReDH5s7v2yljty1IT3Gm7PD+dH3KzjgpxZykNBy0Uf5ogT0bUZg/tpofe7 y8qoNPgsEjBry13pjbSW0DeysIKkf+BeB/gAmy/hsLf/Mq1kuDoIUf6mE/s5Q+tGpBc4+8LNprW KdAyUbOL3tMQbWZIrEg== X-Proofpoint-ORIG-GUID: j9XWWTFMPSCfRzqF9qNOZxbXy2wOgDZn X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1121,Hydra:6.1.51,FMLib:17.12.100.49 definitions=2026-02-10_02,2026-02-10_02,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 suspectscore=0 adultscore=0 bulkscore=0 malwarescore=0 phishscore=0 priorityscore=1501 lowpriorityscore=0 clxscore=1011 impostorscore=0 spamscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2601150000 definitions=main-2602100148 X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org This is a multi-part message in MIME format. --------------3FfuAC8bjvLpY89q6CZozcTb Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit Hi Kevin, Thanks for sharing this patch and for the detailed end-to-end explanation — it matches exactly what I’ve been seeing. I’ve independently hit the same issue with glibc 2.42 on kernels > 6.3, due to the MADV_GUARD_INSTALL change. The behavior is reproducible with both upstream and custom GDB. I tested your patch on ppc64le and x86_64, and it fixes the problem on both. This looks like a generic GDB issue triggered by the new glibc stack layout rather than an architecture-specific problem. The approach of falling back to page-by-page reads on failure looks correct to me. Thanks again for the clear analysis and fix. BR Abhay On 30/01/26 13:52, Kevin Buettner wrote: > GLIBC 2.42 changed how thread stack guard pages are implemented [2]. > In GLIBC 2.41 and earlier, guard pages were set up using mprotect() to > mark guard regions with no permissions. Once configured, guard pages > were visible as separate entries in /proc/PID/maps with no permissions > (i.e. they're inaccessible). In GLIBC 2.42, guard pages are > installed using the kernel's MADV_GUARD_INSTALL mechanism [1], which > marks them at the page table entry (PTE) level within the existing > mapping. > > As a consequence, guard pages do not appear as separate entries in > /proc/PID/maps, but remain as part of the containing mapping. Moreover, > thread stacks from multiple mmap() calls may be merged into a single > virtual memory area (VMA) with read and write permissions since there's > no guard page VMA to separate them. These guard pages cannot be > distinguished by examining VMA listings but do return EIO when read > from /proc/PID/mem. > > GDB's gcore code reads /proc/PID/smaps to discover memory regions and > creates one BFD section per mapping. (On linux, this is performed in > linux_find_memory_regions_full in linux-tdep.c.) With the old layout, > memory areas with guard pages appeared separately with no permissions, > which were filtered out. Each thread stack became its own section > containing only readable data. With the new layout, using > MADV_GUARD_INSTALL instead of the older mechanism, it's often the case > that thread stacks created with multiple calls to mmap() are exposed > as a single mapping appearing in /proc/PID/smaps with read and write > permissions. Should that happen, GDB's code creates a single section > covering all thread stacks and their guard pages. (Even if each > thread stack appears in its own mapping, the fact remains that there > will be an inaccessible portion of the mapping. When one or more > thread stacks are coalesced into a single mapping, there will be > several inaccessible "holes" representing the guard pages.) > > When gcore_copy_callback copies section contents, it reads memory in > 1MB (MAX_COPY_BYTES) chunks. If any page in the chunk is a guard page, > the call to target_read_memory() fails. The old code responded by > breaking out of the copy loop, abandoning the entire section. This > prevents correct copying of thread stack data, resulting in core files > with zero-filled thread stacks, resulting in nearly empty backtraces. > > Fix this by falling back to page-by-page reading when a 1MB chunk read > fails. Individual pages that cannot be read are filled with zeros, > allowing the remaining readable memory to be captured. > > I also considered a simpler change using SPARSE_BLOCK_SIZE (4096) > as the read size instead of MAX_COPY_BYTES (1MB). This would avoid > the fallback logic but would cause up to 256x more syscalls. The > proposed approach also allows meaningful warnings: we warn only if an > entire region is unreadable (indicating a real problem), whereas > per-page reads would make it harder to distinguish guard page failures > from actual errors. Since guard pages are at offset 0 for > downward-growing stacks, a large target_read_memory() fails early at > the first unreadable byte anyway. > > With this fix, I see 16 failures resolved in the following test cases: > > gdb.ada/task_switch_in_core.exp > gdb.arch/i386-tls-regs.exp > gdb.threads/threadcrash.exp > gdb.threads/tls-core.exp > > Looking at just one of these, from gdb.log without the fix, I see: > > thread apply 5 backtrace > > Thread 5 (LWP 3414829): > #0 0x00007ffff7d1d982 in __syscall_cancel_arch () from /lib64/libc.so.6 > #1 0x0000000000000000 in ?? () > (gdb) FAIL: gdb.threads/threadcrash.exp: test_gcore: thread apply 5 backtrace > > And this is what it looks like with the fix in place (some paths have > been shortened): > > thread apply 5 backtrace > > Thread 5 (Thread 0x7fffeffff6c0 (LWP 1282651) "threadcrash"): #0 0x00007ffff7d1d982 in __syscall_cancel_arch () from > /lib64/libc.so.6 #1 0x00007ffff7d11c3c in __internal_syscall_cancel () > from /lib64/libc.so.6 #2 0x00007ffff7d61b62 in > clock_nanosleep@GLIBC_2.2.5 () from /lib64/libc.so.6 #3 > 0x00007ffff7d6db37 in nanosleep () from /lib64/libc.so.6 #4 > 0x00007ffff7d8008e in sleep () from /lib64/libc.so.6 #5 > 0x00000000004006a8 in do_syscall_task (location=NORMAL) at > threadcrash.c:158 #6 0x0000000000400885 in thread_function > (arg=0x404340) at threadcrash.c:277 #7 0x00007ffff7d15464 in > start_thread () from /lib64/libc.so.6 #8 0x00007ffff7d985ac in > __clone3 () from /lib64/libc.so.6 (gdb) PASS: > gdb.threads/threadcrash.exp: test_live_inferior: thread apply 5 > backtrace Regression testing on Fedora 42 (glibc 2.41) shows no new > failures. References: [1] Linux commit 662df3e5c376 ("mm: madvise: implement lightweight > guard page mechanism") > https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=662df3e5c37666d6ed75c88098699e070a4b35b5 > [2] glibc commit a6fbe36b7f31 ("nptl: Add support for setup guard > pages with MADV_GUARD_INSTALL") > https://sourceware.org/git/?p=glibc.git;a=commit;h=a6fbe36b7f31292981422692236465ab56670ea9 > > Claude Opus 4.5 and GLM 4.7 assisted with the development of this commit. > > Bug:https://sourceware.org/bugzilla/show_bug.cgi?id=33855 > --- > gdb/gcore.c | 46 ++++++++++++++++++++++++++++++++++++++-------- > 1 file changed, 38 insertions(+), 8 deletions(-) > > diff --git a/gdb/gcore.c b/gdb/gcore.c > index 5a3ad145d4c..6b36e6064ac 100644 > --- a/gdb/gcore.c > +++ b/gdb/gcore.c > @@ -765,15 +765,45 @@ gcore_copy_callback (bfd *obfd, asection *osec) > if (size > total_size) > size = total_size; > > - if (target_read_memory (bfd_section_vma (osec) + offset, > - memhunk.data (), size) != 0) > + CORE_ADDR vma = bfd_section_vma (osec) + offset; > + > + if (target_read_memory (vma, memhunk.data (), size) != 0) > { > - warning (_("Memory read failed for corefile " > - "section, %s bytes at %s."), > - plongest (size), > - paddress (current_inferior ()->arch (), > - bfd_section_vma (osec))); > - break; > + /* Large read failed. This can happen when the memory region > + contains unreadable pages (such as guard pages embedded within > + a larger mapping). Fall back to reading page by page, filling > + unreadable pages with zeros. */ > + gdb_byte *p = memhunk.data (); > + bfd_size_type remaining = size; > + CORE_ADDR addr = vma; > + bool at_least_one_page_read = false; > + > + while (remaining > 0) > + { > + bfd_size_type chunk_size > + = std::min (remaining, (bfd_size_type) SPARSE_BLOCK_SIZE); > + > + if (target_read_memory (addr, p, chunk_size) != 0) > + { > + /* Failed to read this page. Fill with zeros. This > + handles guard pages and other unreadable regions > + that may exist within a larger readable mapping. */ > + memset (p, 0, chunk_size); > + } > + else > + at_least_one_page_read = true; > + > + p += chunk_size; > + addr += chunk_size; > + remaining -= chunk_size; > + } > + /* Warn only if the entire region was unreadable - this > + indicates a real problem, not just embedded guard pages. */ > + if (!at_least_one_page_read) > + warning (_("Memory read failed for corefile " > + "section, %s bytes at %s."), > + plongest (size), > + paddress (current_inferior ()->arch (), vma)); > } > > if (!sparse_bfd_set_section_contents (obfd, osec, memhunk.data (), --------------3FfuAC8bjvLpY89q6CZozcTb Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: 8bit
Hi Kevin,
Thanks for sharing this patch and for the detailed end-to-end explanation — it matches exactly what I’ve been seeing.
I’ve independently hit the same issue with glibc 2.42 on kernels > 6.3, due to the MADV_GUARD_INSTALL change.
The behavior is reproducible with both upstream and custom GDB.
I tested your patch on ppc64le and x86_64, and it fixes the problem on both.
This looks like a generic GDB issue triggered by the new glibc stack layout rather than an architecture-specific problem.
The approach of falling back to page-by-page reads on failure looks correct to me.
Thanks again for the clear analysis and fix.
BR
Abhay

On 30/01/26 13:52, Kevin Buettner wrote:
GLIBC 2.42 changed how thread stack guard pages are implemented [2].
In GLIBC 2.41 and earlier, guard pages were set up using mprotect() to
mark guard regions with no permissions.  Once configured, guard pages
were visible as separate entries in /proc/PID/maps with no permissions
(i.e. they're inaccessible).  In GLIBC 2.42, guard pages are
installed using the kernel's MADV_GUARD_INSTALL mechanism [1], which
marks them at the page table entry (PTE) level within the existing
mapping.

As a consequence, guard pages do not appear as separate entries in
/proc/PID/maps, but remain as part of the containing mapping.  Moreover,
thread stacks from multiple mmap() calls may be merged into a single
virtual memory area (VMA) with read and write permissions since there's
no guard page VMA to separate them.  These guard pages cannot be
distinguished by examining VMA listings but do return EIO when read
from /proc/PID/mem.

GDB's gcore code reads /proc/PID/smaps to discover memory regions and
creates one BFD section per mapping.  (On linux, this is performed in
linux_find_memory_regions_full in linux-tdep.c.) With the old layout,
memory areas with guard pages appeared separately with no permissions,
which were filtered out.  Each thread stack became its own section
containing only readable data.  With the new layout, using
MADV_GUARD_INSTALL instead of the older mechanism, it's often the case
that thread stacks created with multiple calls to mmap() are exposed
as a single mapping appearing in /proc/PID/smaps with read and write
permissions.  Should that happen, GDB's code creates a single section
covering all thread stacks and their guard pages.  (Even if each
thread stack appears in its own mapping, the fact remains that there
will be an inaccessible portion of the mapping.  When one or more
thread stacks are coalesced into a single mapping, there will be
several inaccessible "holes" representing the guard pages.)

When gcore_copy_callback copies section contents, it reads memory in
1MB (MAX_COPY_BYTES) chunks.  If any page in the chunk is a guard page,
the call to target_read_memory() fails.  The old code responded by
breaking out of the copy loop, abandoning the entire section.  This
prevents correct copying of thread stack data, resulting in core files
with zero-filled thread stacks, resulting in nearly empty backtraces.

Fix this by falling back to page-by-page reading when a 1MB chunk read
fails.  Individual pages that cannot be read are filled with zeros,
allowing the remaining readable memory to be captured.

I also considered a simpler change using SPARSE_BLOCK_SIZE (4096)
as the read size instead of MAX_COPY_BYTES (1MB).  This would avoid
the fallback logic but would cause up to 256x more syscalls.  The
proposed approach also allows meaningful warnings: we warn only if an
entire region is unreadable (indicating a real problem), whereas
per-page reads would make it harder to distinguish guard page failures
from actual errors.  Since guard pages are at offset 0 for
downward-growing stacks, a large target_read_memory() fails early at
the first unreadable byte anyway.

With this fix, I see 16 failures resolved in the following test cases:

    gdb.ada/task_switch_in_core.exp
    gdb.arch/i386-tls-regs.exp
    gdb.threads/threadcrash.exp
    gdb.threads/tls-core.exp

Looking at just one of these, from gdb.log without the fix, I see:

  thread apply 5 backtrace

  Thread 5 (LWP 3414829):
  #0  0x00007ffff7d1d982 in __syscall_cancel_arch () from /lib64/libc.so.6
  #1  0x0000000000000000 in ?? ()
  (gdb) FAIL: gdb.threads/threadcrash.exp: test_gcore: thread apply 5 backtrace

And this is what it looks like with the fix in place (some paths have
been shortened):

  thread apply 5 backtrace

  Thread 5 (Thread 0x7fffeffff6c0 (LWP 1282651) "threadcrash"):
  #0  0x00007ffff7d1d982 in __syscall_cancel_arch () from /lib64/libc.so.6
  #1  0x00007ffff7d11c3c in __internal_syscall_cancel () from /lib64/libc.so.6
  #2  0x00007ffff7d61b62 in clock_nanosleep@GLIBC_2.2.5 () from /lib64/libc.so.6
  #3  0x00007ffff7d6db37 in nanosleep () from /lib64/libc.so.6
  #4  0x00007ffff7d8008e in sleep () from /lib64/libc.so.6
  #5  0x00000000004006a8 in do_syscall_task (location=NORMAL) at threadcrash.c:158
  #6  0x0000000000400885 in thread_function (arg=0x404340) at threadcrash.c:277
  #7  0x00007ffff7d15464 in start_thread () from /lib64/libc.so.6
  #8  0x00007ffff7d985ac in __clone3 () from /lib64/libc.so.6
  (gdb) PASS: gdb.threads/threadcrash.exp: test_live_inferior: thread apply 5 backtrace

Regression testing on Fedora 42 (glibc 2.41) shows no new failures.

References:

[1] Linux commit 662df3e5c376 ("mm: madvise: implement lightweight
    guard page mechanism")
    https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=662df3e5c37666d6ed75c88098699e070a4b35b5
[2] glibc commit a6fbe36b7f31 ("nptl: Add support for setup guard
    pages with MADV_GUARD_INSTALL")
    https://sourceware.org/git/?p=glibc.git;a=commit;h=a6fbe36b7f31292981422692236465ab56670ea9

Claude Opus 4.5 and GLM 4.7 assisted with the development of this commit.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=33855
---
 gdb/gcore.c | 46 ++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 38 insertions(+), 8 deletions(-)

diff --git a/gdb/gcore.c b/gdb/gcore.c
index 5a3ad145d4c..6b36e6064ac 100644
--- a/gdb/gcore.c
+++ b/gdb/gcore.c
@@ -765,15 +765,45 @@ gcore_copy_callback (bfd *obfd, asection *osec)
       if (size > total_size)
 	size = total_size;
 
-      if (target_read_memory (bfd_section_vma (osec) + offset,
-			      memhunk.data (), size) != 0)
+      CORE_ADDR vma = bfd_section_vma (osec) + offset;
+
+      if (target_read_memory (vma, memhunk.data (), size) != 0)
 	{
-	  warning (_("Memory read failed for corefile "
-		     "section, %s bytes at %s."),
-		   plongest (size),
-		   paddress (current_inferior ()->arch (),
-			     bfd_section_vma (osec)));
-	  break;
+	  /* Large read failed.  This can happen when the memory region
+	     contains unreadable pages (such as guard pages embedded within
+	     a larger mapping).  Fall back to reading page by page, filling
+	     unreadable pages with zeros.  */
+	  gdb_byte *p = memhunk.data ();
+	  bfd_size_type remaining = size;
+	  CORE_ADDR addr = vma;
+	  bool at_least_one_page_read = false;
+
+	  while (remaining > 0)
+	    {
+	      bfd_size_type chunk_size
+		= std::min (remaining, (bfd_size_type) SPARSE_BLOCK_SIZE);
+
+	      if (target_read_memory (addr, p, chunk_size) != 0)
+		{
+		  /* Failed to read this page.  Fill with zeros.  This
+		     handles guard pages and other unreadable regions
+		     that may exist within a larger readable mapping.  */
+		  memset (p, 0, chunk_size);
+		}
+	      else
+		at_least_one_page_read = true;
+
+	      p += chunk_size;
+	      addr += chunk_size;
+	      remaining -= chunk_size;
+	    }
+	  /* Warn only if the entire region was unreadable - this
+	     indicates a real problem, not just embedded guard pages. */
+	  if (!at_least_one_page_read)
+	    warning (_("Memory read failed for corefile "
+		       "section, %s bytes at %s."),
+		     plongest (size),
+		     paddress (current_inferior ()->arch (), vma));
 	}
 
       if (!sparse_bfd_set_section_contents (obfd, osec, memhunk.data (),
--------------3FfuAC8bjvLpY89q6CZozcTb--