diff --git a/NEWS b/NEWS
index 56c6b581df..1964ebdf81 100644
--- a/NEWS
+++ b/NEWS
@@ -9,7 +9,9 @@ Version 2.45
Major new features:
- [Add new features here]
+* The function pthread_getstack_np has been added. It returns the stack
+ bounds of a thread, like pthread_attr_getstack applied to the result of
+ pthread_getattr_np, in a async-signal-safe manner.
Deprecated and removed features, and other changes affecting compatibility:
diff --git a/manual/threads.texi b/manual/threads.texi
index 0331599bc0..6b04ff3755 100644
--- a/manual/threads.texi
+++ b/manual/threads.texi
@@ -1148,6 +1148,7 @@ the standard.
* Thread CPU Affinity:: Limiting which CPUs can run a thread.
* Joining Threads:: Wait for a thread to terminate.
* Thread Names:: Changing the name of a thread.
+* Thread Stack Bounds:: Determining the stack of a thread.
* Single-Threaded:: Detecting single-threaded execution.
* Restartable Sequences:: Linux-specific restartable sequences
integration.
@@ -1352,6 +1353,37 @@ Retrieves the name of thread @var{th}.
@manpagefunctionstub{pthread_getname_np, 3}
@end deftypefun
+@node Thread Stack Bounds
+@subsubsection Thread Stack Bounds
+
+@deftypefun int pthread_getstack_np (pthread_t @var{th}, void **@var{stackaddr}, size_t *@var{stacksize})
+@standards{GNU, pthread.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+Store the lowest usable address of the stack of thread @var{th} in
+@code{*@var{stackaddr}} and the number of bytes usable from that
+address in @code{*@var{stacksize}}, following the same convention as
+@code{pthread_attr_getstack}. The reported size does not include the
+guard area.
+
+For the initial thread the bounds are computed from the current
+@code{RLIMIT_STACK} resource limit and the process memory map, so the
+result may change if the limit is adjusted. On failure an error code
+is returned, for instance @code{ENOENT} when the stack bounds cannot
+be determined.
+
+Unlike @code{pthread_getattr_np}, this function does not acquire any
+lock nor allocate memory, and it is async-signal-safe.
+
+The function reports the stack set up by the kernel or by
+@code{pthread_create}. The alternate signal stack is not reflected in
+the result, and a handler running on it still gets the bounds of the
+thread stack. The alternate stack can be queried with @code{sigaltstack}
+(@pxref{Signal Stack}), which is also async-signal-safe.
+
+Stacks switched by user code, such as with @code{makecontext} or coroutine
+libraries, are not tracked by @theglibc{} and are not reported either.
+@end deftypefun
+
@node Single-Threaded
@subsubsection Detecting Single-Threaded Execution
diff --git a/nptl/Makefile b/nptl/Makefile
index 1a284d1dd2..5621756f2c 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -118,6 +118,7 @@ routines = \
pthread_getname \
pthread_getschedparam \
pthread_getspecific \
+ pthread_getstack_np \
pthread_gettid_np \
pthread_join \
pthread_join_common \
@@ -328,6 +329,7 @@ tests = \
tst-pthread-timedlock-lockloop \
tst-pthread_exit-nothreads \
tst-pthread_exit-nothreads-static \
+ tst-pthread_getstack_np \
tst-pthread_gettid_np \
tst-robust-fork \
tst-robust12 \
diff --git a/nptl/Versions b/nptl/Versions
index b813b675b9..43f122e15c 100644
--- a/nptl/Versions
+++ b/nptl/Versions
@@ -383,6 +383,9 @@ libc {
}
GLIBC_2.43 {
}
+ GLIBC_2.45 {
+ pthread_getstack_np;
+ }
GLIBC_PRIVATE {
__libc_alloca_cutoff;
__lll_lock_wake_private;
diff --git a/nptl/pthread_getstack_np.c b/nptl/pthread_getstack_np.c
new file mode 100644
index 0000000000..cfd95e1d2f
--- /dev/null
+++ b/nptl/pthread_getstack_np.c
@@ -0,0 +1,53 @@
+/* Get the stack bounds of a thread.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ . */
+
+#include
+
+int
+pthread_getstack_np (pthread_t threadid, void **stackaddr, size_t *stacksize)
+{
+ struct pthread *thread = (struct pthread *) threadid;
+
+ if (__glibc_likely (thread->stackblock != NULL))
+ {
+ /* As per pthread_attr_getstack, the returned size does not include the
+ guard area. */
+ *stacksize = thread->stackblock_size - thread->guardsize;
+#if _STACK_GROWS_DOWN
+ *stackaddr = (char *) thread->stackblock + thread->guardsize;
+#else
+ *stackaddr = thread->stackblock;
+#endif
+ return 0;
+ }
+
+ /* The initial thread stack is set up by the kernel. */
+ void *addr;
+ size_t size;
+ int ret = __pthread_main_stack (&addr, &size);
+ if (ret != 0)
+ return ret;
+
+#if _STACK_GROWS_DOWN
+ *stackaddr = (char *) addr - size;
+#else
+ *stackaddr = addr;
+#endif
+ *stacksize = size;
+ return 0;
+}
diff --git a/nptl/tst-pthread_getstack_np.c b/nptl/tst-pthread_getstack_np.c
new file mode 100644
index 0000000000..18b80d0460
--- /dev/null
+++ b/nptl/tst-pthread_getstack_np.c
@@ -0,0 +1,285 @@
+/* Test pthread_getstack_np.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ . */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* The result must match what pthread_getattr_np plus pthread_attr_getstack
+ return. */
+static void
+check_against_getattr_np (pthread_t thr)
+{
+ void *stackaddr;
+ size_t stacksize;
+ TEST_COMPARE (pthread_getstack_np (thr, &stackaddr, &stacksize), 0);
+
+ pthread_attr_t attr;
+ TEST_COMPARE (pthread_getattr_np (thr, &attr), 0);
+ void *attr_stackaddr;
+ size_t attr_stacksize;
+ TEST_COMPARE (pthread_attr_getstack (&attr, &attr_stackaddr,
+ &attr_stacksize), 0);
+ TEST_COMPARE (pthread_attr_destroy (&attr), 0);
+
+ TEST_VERIFY (stackaddr == attr_stackaddr);
+ TEST_COMPARE (stacksize, attr_stacksize);
+}
+
+/* A local variable of the calling thread lies within the reported bounds. */
+static void
+check_contains_local (void)
+{
+ void *stackaddr;
+ size_t stacksize;
+ TEST_COMPARE (pthread_getstack_np (pthread_self (), &stackaddr,
+ &stacksize), 0);
+ char here;
+ TEST_VERIFY ((uintptr_t) &here >= (uintptr_t) stackaddr);
+ TEST_VERIFY ((uintptr_t) &here < (uintptr_t) stackaddr + stacksize);
+}
+
+static void *
+thread_func (void *arg)
+{
+ check_against_getattr_np (pthread_self ());
+ check_contains_local ();
+ return NULL;
+}
+
+static void *main_stackaddr;
+static size_t main_stacksize;
+
+/* Runs on the alternate signal stack, the reported bounds are still the ones
+ of the thread stack. */
+static void
+signal_handler (int sig)
+{
+ void *stackaddr;
+ size_t stacksize;
+ TEST_COMPARE (pthread_getstack_np (pthread_self (), &stackaddr, &stacksize),
+ 0);
+ TEST_VERIFY (stackaddr == main_stackaddr);
+ TEST_COMPARE (stacksize, main_stacksize);
+}
+
+/* Mount /proc so that /proc/self/maps cannot be opened, which forces the
+ AT_EXECFN fallback for the initial thread. */
+static void
+check_proc_fallback (void *closure)
+{
+ if (!support_become_root ())
+ {
+ puts ("info: cannot become root, skipping /proc fallback check");
+ return;
+ }
+ if (!support_enter_mount_namespace ())
+ {
+ puts ("info: cannot enter mount namespace, skipping /proc fallback"
+ " check");
+ return;
+ }
+ if (mount ("none", "/proc", "tmpfs", 0, NULL) != 0)
+ {
+ printf ("info: cannot mount tmpfs on /proc (%m), skipping /proc"
+ " fallback check\n");
+ return;
+ }
+
+ /* The maps reader must now fail, so the fallback is exercised. */
+ TEST_VERIFY (open64 ("/proc/self/maps", O_RDONLY) < 0);
+
+ void *stackaddr;
+ size_t stacksize;
+ int ret = pthread_getstack_np (pthread_self (), &stackaddr, &stacksize);
+
+ pthread_attr_t attr;
+ int gret = pthread_getattr_np (pthread_self (), &attr);
+ TEST_COMPARE (ret, gret);
+
+ if (ret == 0)
+ {
+ /* The fallback (downward-growing stack) reproduces the value
+ computed from the maps, captured in the parent before the fork. */
+ void *attr_stackaddr;
+ size_t attr_stacksize;
+ TEST_COMPARE (pthread_attr_getstack (&attr, &attr_stackaddr,
+ &attr_stacksize), 0);
+ TEST_COMPARE (pthread_attr_destroy (&attr), 0);
+ TEST_VERIFY (stackaddr == attr_stackaddr);
+ TEST_COMPARE (stacksize, attr_stacksize);
+ TEST_VERIFY (stackaddr == main_stackaddr);
+ TEST_COMPARE (stacksize, main_stacksize);
+
+ char here;
+ TEST_VERIFY ((uintptr_t) &here >= (uintptr_t) stackaddr);
+ TEST_VERIFY ((uintptr_t) &here < (uintptr_t) stackaddr + stacksize);
+ }
+ else
+ {
+ /* Configurations without the fallback (e.g. an upward-growing
+ stack) report the failure instead of a stack. */
+ printf ("info: no /proc fallback in this configuration (ret %d)\n",
+ ret);
+ TEST_COMPARE (ret, ENOENT);
+ }
+}
+
+
+/* Read the current [stack] vma from /proc/self/maps. */
+static bool
+stack_vma (uintptr_t *lo, uintptr_t *hi)
+{
+ FILE *f = fopen ("/proc/self/maps", "r");
+ if (f == NULL)
+ return false;
+ bool found = false;
+ char line[512];
+ while (fgets (line, sizeof line, f) != NULL)
+ if (strstr (line, "[stack]") != NULL
+ && sscanf (line, "%" SCNxPTR "-%" SCNxPTR, lo, hi) == 2)
+ {
+ found = true;
+ break;
+ }
+ fclose (f);
+ return found;
+}
+
+static __attribute__ ((noinline)) bool
+stack_grows_down_1 (uintptr_t outer)
+{
+ char inner;
+ return (uintptr_t) &inner < outer;
+}
+
+static bool
+stack_grows_down (void)
+{
+ char outer;
+ return stack_grows_down_1 ((uintptr_t) &outer);
+}
+
+/* The kernel does not grow the stack into the guard gap above the mapping
+ below it. With a mapping planted within that distance below the current
+ vma the stack can not grow at all, so the reported lowest address must be
+ the current vma bottom rather than the end of the planted mapping. This
+ must be the last check, since afterwards the stack can not grow. */
+static void
+check_stack_guard_gap (void)
+{
+ if (!stack_grows_down ())
+ {
+ puts ("info: stack grows up, skipping guard gap check");
+ return;
+ }
+ uintptr_t lo, hi;
+ if (!stack_vma (&lo, &hi))
+ {
+ puts ("info: no [stack] in /proc/self/maps, skipping guard gap check");
+ return;
+ }
+
+ long pagesz = sysconf (_SC_PAGESIZE);
+ uintptr_t planted = (lo - 100 * 1024) & -(uintptr_t) pagesz;
+ void *m = mmap ((void *) planted, pagesz, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE, -1, 0);
+ if (m == MAP_FAILED)
+ {
+ printf ("info: cannot map below the stack (%m), skipping guard gap"
+ " check\n");
+ return;
+ }
+ TEST_VERIFY ((uintptr_t) m == planted);
+
+ void *stackaddr;
+ size_t stacksize;
+ TEST_COMPARE (pthread_getstack_np (pthread_self (), &stackaddr, &stacksize),
+ 0);
+ /* The vma may only have grown down since it was read. */
+ uintptr_t lo2, hi2;
+ TEST_VERIFY (stack_vma (&lo2, &hi2));
+ TEST_VERIFY (lo2 <= (uintptr_t) stackaddr && (uintptr_t) stackaddr <= lo);
+ TEST_VERIFY ((uintptr_t) stackaddr > planted + pagesz);
+
+ check_against_getattr_np (pthread_self ());
+}
+
+static int
+do_test (void)
+{
+ check_against_getattr_np (pthread_self ());
+ check_contains_local ();
+ TEST_COMPARE (pthread_getstack_np (pthread_self (), &main_stackaddr,
+ &main_stacksize), 0);
+
+ /* A thread with the default stack size, queried both by itself and
+ by another thread. */
+ {
+ pthread_t thr = xpthread_create (NULL, thread_func, NULL);
+ check_against_getattr_np (thr);
+ xpthread_join (thr);
+ }
+
+ /* A thread with a non-default stack size. */
+ {
+ pthread_attr_t attr;
+ xpthread_attr_init (&attr);
+ xpthread_attr_setstacksize (&attr, 256 * 1024);
+ xpthread_join (xpthread_create (&attr, thread_func, NULL));
+ xpthread_attr_destroy (&attr);
+ }
+
+ /* From a signal handler running on the alternate signal stack. */
+ {
+ struct support_stack altstack = support_stack_alloc (SIGSTKSZ);
+ stack_t ss = { .ss_sp = altstack.stack, .ss_size = altstack.size };
+ TEST_COMPARE (sigaltstack (&ss, NULL), 0);
+ xsigaction (SIGUSR1,
+ &(struct sigaction) {
+ .sa_handler = signal_handler,
+ .sa_flags = SA_ONSTACK },
+ NULL);
+ xraise (SIGUSR1);
+ support_stack_free (&altstack);
+ }
+
+ /* The initial thread fallback when /proc is not accessible. */
+ support_isolate_in_subprocess (check_proc_fallback, NULL);
+
+ check_stack_guard_gap ();
+
+ return 0;
+}
+
+#include
diff --git a/sysdeps/nptl/pthread.h b/sysdeps/nptl/pthread.h
index 95c0eb7e03..05e99845bf 100644
--- a/sysdeps/nptl/pthread.h
+++ b/sysdeps/nptl/pthread.h
@@ -1320,6 +1320,12 @@ extern int pthread_getcpuclockid (pthread_t __thread_id,
#ifdef __USE_GNU
/* Return the Linux TID for THREAD_ID. Returns -1 on failure. */
extern pid_t pthread_gettid_np (pthread_t __thread_id);
+
+/* Store the lowest usable address of the stack of THREAD_ID in *STACKADDR and
+ the number of usable bytes from that address in *STACKSIZE. */
+extern int pthread_getstack_np (pthread_t __thread_id,
+ void **__stackaddr, size_t *__stacksize)
+ __THROW __nonnull ((2, 3));
#endif
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index b66eed99d1..c93e82652b 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -563,6 +563,8 @@ sysdep_routines += \
spawnattr_setcgroup_np \
# sysdep_routines
+CFLAGS-procutils.c += -fexceptions -fasynchronous-unwind-tables
+
tests += \
tst-affinity \
tst-affinity-pid \
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index 3156688add..98709d2629 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2775,3 +2775,4 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index 8af5b0b581..706b936d9e 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -3122,6 +3122,7 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist
index 35fcef2cc4..da979d7c6e 100644
--- a/sysdeps/unix/sysv/linux/arc/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arc/libc.abilist
@@ -2536,3 +2536,4 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
index a6c6b951bf..aa1355c769 100644
--- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
@@ -2828,6 +2828,7 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
index e76015fe66..1d58ccb253 100644
--- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
@@ -2825,6 +2825,7 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
index 1fb7cdcad5..169145176b 100644
--- a/sysdeps/unix/sysv/linux/csky/libc.abilist
+++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
@@ -2812,3 +2812,4 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 0710ccecf9..964f06daba 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -2849,6 +2849,7 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index 3afe3a88eb..a77d27f2bb 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -3032,6 +3032,7 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/loongarch/ilp32/libc.abilist b/sysdeps/unix/sysv/linux/loongarch/ilp32/libc.abilist
index 7c6d7055c3..b2ef03ff16 100644
--- a/sysdeps/unix/sysv/linux/loongarch/ilp32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/loongarch/ilp32/libc.abilist
@@ -2287,3 +2287,4 @@ GLIBC_2.44 wprintf F
GLIBC_2.44 write F
GLIBC_2.44 writev F
GLIBC_2.44 wscanf F
+GLIBC_2.45 pthread_getstack_np F
diff --git a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
index c2b3a66d3a..8ec209bf27 100644
--- a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
@@ -2296,3 +2296,4 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index d6855131e8..eccbd04296 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -2808,6 +2808,7 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index 4e3fe9c42f..4721e8e983 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -2975,6 +2975,7 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
index 29f0c5f954..dcd444a2ce 100644
--- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
@@ -2861,3 +2861,4 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
index 2ef62838f7..29a94f43fc 100644
--- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
@@ -2858,3 +2858,4 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index 031e8961ac..94e5afff89 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -2938,6 +2938,7 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index 8dc99d81b4..f4fe1b36cf 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -2936,6 +2936,7 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 054c5b6391..6546b9d204 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -2944,6 +2944,7 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 13f0148bc0..3fb80ff9bb 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -2846,6 +2846,7 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/or1k/libc.abilist b/sysdeps/unix/sysv/linux/or1k/libc.abilist
index e7ffe07dd8..332829cf0b 100644
--- a/sysdeps/unix/sysv/linux/or1k/libc.abilist
+++ b/sysdeps/unix/sysv/linux/or1k/libc.abilist
@@ -2286,3 +2286,4 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index dea4b20f05..42ff2ef985 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -3165,6 +3165,7 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index b45e127463..d48eb09369 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -3210,6 +3210,7 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
index 942cf6a027..99c1a046c0 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
@@ -2919,6 +2919,7 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
index 65d78e5076..8ff2eabecd 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
@@ -2995,3 +2995,4 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
diff --git a/sysdeps/unix/sysv/linux/procutils.c b/sysdeps/unix/sysv/linux/procutils.c
index c254fa4a78..56e3fa21ad 100644
--- a/sysdeps/unix/sysv/linux/procutils.c
+++ b/sysdeps/unix/sysv/linux/procutils.c
@@ -16,6 +16,7 @@
License along with the GNU C Library; if not, see
. */
+#include
#include
#include
#include
@@ -109,6 +110,12 @@ next_line (struct line_reader *lr, char **r)
}
}
+static void
+close_handler (void *arg)
+{
+ __close_nocancel_nostatus (*(int *) arg);
+}
+
enum procutils_read_result_t
__libc_procutils_read_file (const char *filename, char *buffer,
size_t buffer_size, procutils_closure_t closure,
@@ -127,7 +134,11 @@ __libc_procutils_read_file (const char *filename, char *buffer,
if (lr.fd == -1)
return procutils_read_error;
+ /* The cleanup handler is required to avoid leaking the descriptor if
+ the thread is asynchronously canceled. */
enum next_line_result_t r;
+ __libc_cleanup_push (close_handler, &lr.fd);
+
char *line;
while ((r = next_line (&lr, &line)) == next_line_ok)
if (closure (line, arg) != 0)
@@ -135,6 +146,8 @@ __libc_procutils_read_file (const char *filename, char *buffer,
__close_nocancel_nostatus (lr.fd);
+ __libc_cleanup_pop (0);
+
if (r == next_line_error)
return procutils_read_error;
return r == next_line_ok ? procutils_read_stop : procutils_read_eof;
diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
index dcab30d72e..2a7b4cc5b9 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
@@ -2539,3 +2539,4 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index 796ef35e26..bfc7c72113 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2739,3 +2739,4 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
diff --git a/sysdeps/unix/sysv/linux/s390/libc.abilist b/sysdeps/unix/sysv/linux/s390/libc.abilist
index 8f2350ee0b..836850107a 100644
--- a/sysdeps/unix/sysv/linux/s390/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/libc.abilist
@@ -2956,6 +2956,7 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
index 7aa98c5aed..3ed78f5f0b 100644
--- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
@@ -2855,6 +2855,7 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
index 6bd4f8f63a..4771b5ca2d 100644
--- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
@@ -2852,6 +2852,7 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index b52cab2a35..91e6722b52 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -3186,6 +3186,7 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index ff99cd4f21..7c72b93287 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -2822,6 +2822,7 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index 306cd627fd..207f868422 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -2771,6 +2771,7 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F
GLIBC_2.5 __readlinkat_chk F
GLIBC_2.5 inet6_opt_append F
GLIBC_2.5 inet6_opt_find F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 8b9c448742..f17a31d3f0 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2790,3 +2790,4 @@ GLIBC_2.43 memset_explicit F
GLIBC_2.43 mseal F
GLIBC_2.43 openat2 F
GLIBC_2.43 umaxabs F
+GLIBC_2.45 pthread_getstack_np F