mirror of
git://sourceware.org/git/glibc.git
synced 2026-10-30 21:07:53 +08:00
nptl: Add pthread_getstack_np
It returns the stack bounds of a thread following the same convention as pthread_attr_getstack applied to the result of pthread_getattr_np. Unlike pthread_getattr_np, it does not acquire the thread descriptor lock nor allocates memory, which make the function async-signal-safe. The function reports the stack set up by the kernel or by pthread_create, the alternate signal stack is not reflected in the result (it can be queried with sigaltstack, which is also async-signal-safe), and stacks switched by user code, such as with makecontext or coroutine libraries, are not tracked. Similar interfaces are provided by Solaris (stack_getbounds), OpenBSD (pthread_stackseg_np), and LLVM libc (pthread_getstack_np). Checked on x86_64-linux-gnu.
This commit is contained in:
@@ -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:
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -383,6 +383,9 @@ libc {
|
||||
}
|
||||
GLIBC_2.43 {
|
||||
}
|
||||
GLIBC_2.45 {
|
||||
pthread_getstack_np;
|
||||
}
|
||||
GLIBC_PRIVATE {
|
||||
__libc_alloca_cutoff;
|
||||
__lll_lock_wake_private;
|
||||
|
||||
@@ -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
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <pthreadP.h>
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -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
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/mount.h>
|
||||
#include <unistd.h>
|
||||
#include <support/check.h>
|
||||
#include <support/namespace.h>
|
||||
#include <support/support.h>
|
||||
#include <support/xsignal.h>
|
||||
#include <support/xthread.h>
|
||||
#include <support/xunistd.h>
|
||||
|
||||
/* 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 <support/test-driver.c>
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -563,6 +563,8 @@ sysdep_routines += \
|
||||
spawnattr_setcgroup_np \
|
||||
# sysdep_routines
|
||||
|
||||
CFLAGS-procutils.c += -fexceptions -fasynchronous-unwind-tables
|
||||
|
||||
tests += \
|
||||
tst-affinity \
|
||||
tst-affinity-pid \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <libc-lock.h>
|
||||
#include <not-cancel.h>
|
||||
#include <procutils.h>
|
||||
#include <stdbool.h>
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user