@@ -9,6 +9,11 @@ Version 2.37
Major new features:
+* On Linux, it is now possible to create threads with per-thread current
+ directory, umask, and file system root. The functions
+ pthread_attr_setperthreadfs_np and pthread_attr_getperthreadfs_np have
+ been added in support of that.
+
* The getent tool now supports the --no-addrconfig option. The output of
getent with --no-addrconfig may contain addresses of families not
configured on the current host i.e. as-if you had not passed
@@ -626,6 +626,7 @@ the standard.
* Default Thread Attributes:: Setting default attributes for
threads in a process.
* Initial Thread Signal Mask:: Setting the initial mask of threads.
+* Enabling Per-Thread Properties:: Additional per-thread properties.
* Waiting with Explicit Clocks:: Functions for waiting with an
explicit clock specification.
* Single-Threaded:: Detecting single-threaded execution.
@@ -746,6 +747,97 @@ signal mask and use @code{pthread_sigmask} to apply it to the thread.
If the signal mask was copied to a heap allocation, the copy should be
freed.
+@node Enabling Per-Thread Properties
+@subsubsection Enabling Additional Per-Thread Properties
+
+POSIX mandates that the current directory, file system root, umask
+value, and the current user and group IDs are process-global
+properties. For example, if a thread calls the @code{chdir} function
+to change to a different directory, all threads are eventually
+affected by this change. @Theglibc{} implements an extension which
+allows threads to be created which do not share these properties with
+the rest of the process.
+
+The desired behavior is specified at the time the thread is created,
+using the thread attribute. The following constants are used to
+update the attribute:
+
+@vtable @code
+@item PTHREAD_PER_PROCESS_NP
+@standards{GNU, pthread.h}
+This property in question is globally shared across the entire process.
+This is the default.
+
+@item PTHREAD_PER_THREAD_NP
+@standards{GNU, pthread.h}
+This property in question is thread-specific.
+@end vtable
+
+The @code{PTHREAD_PER_THREAD_NP} flag is sticky, in the sense that all
+threads created by a thread created with this flag have per-thread
+properties, even if they are created with the matching thread attribute
+set to the @code{PTHREAD_PER_PROCESS_NP} flag. If an application wants
+to create new threads sharing properties with the main thread, it should
+create a service thread early (perhaps from an ELF constructor) and
+create these threads using this service thread.
+
+Per-thread properties can be set and examined for an attribute using
+the functions below.
+
+@deftypefun int pthread_attr_setperthreadfs_np (pthread_attr_t *@var{attr}, int @var{scope})
+@standards{GNU, pthread.h}
+@safety{@mtsafe{}@assafe{}@acsafe{}}
+Change whether the following properties related to file system access
+are made thread-specific when a new thread is created using the
+attribute @var{attr}:
+
+@itemize @bullet
+@item
+@cindex current directory as a per-thread property
+@cindex per-thread current directory
+@cindex thread-specific current directory
+current directory (as changed by @code{chdir} and related functions)
+
+@item
+@cindex @code{chroot} as a per-thread property
+@cindex per-thread @code{chroot}
+@cindex thread-specific @code{chroot}
+file system root (as changed by @code{chroot})
+
+@item
+@cindex umask as a per-thread property
+@cindex per-thread @code{umask}
+@cindex thread-specific @code{umask}
+umask value (as changed by the function of the same name)
+@end itemize
+
+This function returns zero on success. @var{scope} must be one of the
+constants @code{PTHREAD_PER_PROCESS_NP} or
+@code{PTHREAD_PER_THREAD_NP}, otherwise the function returns
+@code{EINVAL}.
+
+If @var{scope} is @code{PTHREAD_PER_THREAD_NP}, the attribute will
+cause the properties listed above to be specific to the thread. The
+initial values of these properties are copied from the creating
+thread, at thread creation time.
+
+If a thread that has been created with the
+@code{PTHREAD_PER_THREAD_NP} flag creates further threads, these
+threads are implicitly created with the @code{PTHREAD_PER_THREAD_NP}
+flag, ignoring the value of this thread creation attribute.
+
+This function is a GNU extension and specific to Linux.
+@end deftypefun
+
+@deftypefun int pthread_attr_getperthreadfs_np (pthread_attr_t *restrict @var{attr}, int *restrict @var{scope})
+@standards{GNU, pthread.h}
+@safety{@mtsafe{}@assafe{}@acsafe{}}
+Obtain the per-thread status of the file system properties in
+@var{attr} and store it in the location @var{scope}.
+
+This function is a GNU extension and specific to Linux.
+@end deftypefun
+
@node Waiting with Explicit Clocks
@subsubsection Functions for Waiting According to a Specific Clock
@@ -71,6 +71,7 @@ routines = \
pthread_attr_getdetachstate \
pthread_attr_getguardsize \
pthread_attr_getinheritsched \
+ pthread_attr_getperthreadfs_np \
pthread_attr_getschedparam \
pthread_attr_getschedpolicy \
pthread_attr_getscope \
@@ -83,6 +84,7 @@ routines = \
pthread_attr_setdetachstate \
pthread_attr_setguardsize \
pthread_attr_setinheritsched \
+ pthread_attr_setperthreadfs_np \
pthread_attr_setschedparam \
pthread_attr_setschedpolicy \
pthread_attr_setscope \
@@ -311,7 +313,9 @@ tests = tst-attr2 tst-attr3 tst-default-attr \
tst-pthread-gdb-attach tst-pthread-gdb-attach-static \
tst-pthread_exit-nothreads \
tst-pthread_exit-nothreads-static \
- tst-thread-setspecific
+ tst-thread-setspecific \
+ tst-pthread-perthreadfs \
+ tst-pthread-perthreadfs-chroot \
tests-nolibpthread = \
tst-pthread_exit-nothreads \
@@ -378,6 +378,10 @@ libc {
tss_get;
tss_set;
}
+ GLIBC_2.37 {
+ pthread_attr_setperthreadfs_np;
+ pthread_attr_getperthreadfs_np;
+ }
GLIBC_PRIVATE {
__libc_alloca_cutoff;
__lll_lock_wake_private;
new file mode 100644
@@ -0,0 +1,32 @@
+/* Read the private file system flag in thread attributes.
+ Copyright (C) 2019 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
+ <http://www.gnu.org/licenses/>. */
+
+#include <pthread.h>
+#include <internaltypes.h>
+
+int
+pthread_attr_getperthreadfs_np (const pthread_attr_t *__restrict attr,
+ int *__restrict scope)
+{
+ struct pthread_attr *iattr = (struct pthread_attr *) attr;
+ if (iattr->flags & ATTR_FLAG_PERTHREADFS)
+ *scope = PTHREAD_PER_THREAD_NP;
+ else
+ *scope = PTHREAD_PER_PROCESS_NP;
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,39 @@
+/* Change the private file system flag in thread attributes.
+ Copyright (C) 2019 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
+ <http://www.gnu.org/licenses/>. */
+
+#include <pthread.h>
+#include <internaltypes.h>
+#include <errno.h>
+
+int
+pthread_attr_setperthreadfs_np (pthread_attr_t *attr, int scope)
+{
+ struct pthread_attr *iattr = (struct pthread_attr *) attr;
+ switch (scope)
+ {
+ case PTHREAD_PER_PROCESS_NP:
+ iattr->flags &= ~ATTR_FLAG_PERTHREADFS;
+ return 0;
+ break;
+ case PTHREAD_PER_THREAD_NP:
+ iattr->flags |= ATTR_FLAG_PERTHREADFS;
+ return 0;
+ default:
+ return EINVAL;
+ }
+}
@@ -250,7 +250,9 @@ static int create_thread (struct pthread *pd, const struct pthread_attr *attr,
CLONE_VM, CLONE_FS, CLONE_FILES
These flags select semantics with shared address space and
- file descriptors according to what POSIX requires.
+ file descriptors according to what POSIX requires. CLONE_FS
+ is optional; it can be controlled using the function
+ pthread_attr_setperthreadfs_np.
CLONE_SIGHAND, CLONE_THREAD
This flag selects the POSIX signal semantics and various
@@ -274,11 +276,13 @@ static int create_thread (struct pthread *pd, const struct pthread_attr *attr,
The termination signal is chosen to be zero which means no signal
is sent. */
- const int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SYSVSEM
- | CLONE_SIGHAND | CLONE_THREAD
- | CLONE_SETTLS | CLONE_PARENT_SETTID
- | CLONE_CHILD_CLEARTID
- | 0);
+ int clone_flags = (CLONE_VM | CLONE_FILES | CLONE_SYSVSEM
+ | CLONE_SIGHAND | CLONE_THREAD
+ | CLONE_SETTLS | CLONE_PARENT_SETTID
+ | CLONE_CHILD_CLEARTID
+ | 0);
+ if ((attr->flags & ATTR_FLAG_PERTHREADFS) == 0)
+ clone_flags |= CLONE_FS;
TLS_DEFINE_INIT_TP (tp, pd);
@@ -681,8 +685,8 @@ __pthread_create_2_1 (pthread_t *newthread, const pthread_attr_t *attr,
/* Copy the thread attribute flags. */
struct pthread *self = THREAD_SELF;
- pd->flags = ((iattr->flags & ~(ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET))
- | (self->flags & (ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET)));
+ pd->flags = ((iattr->flags & ~ATTR_FLAGS_IGNORED_ATTR)
+ | (self->flags & ATTR_FLAGS_INHERITED));
/* Inherit rseq registration state. Without seccomp filters, rseq
registration will either always fail or always succeed. */
new file mode 100644
@@ -0,0 +1,255 @@
+/* Test per-thread file system attributes, chroot version.
+ Copyright (C) 2019 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
+ <http://www.gnu.org/licenses/>. */
+
+/* This thread is separate from tst-pthread-perthreadfs because it
+ requires chroot support. */
+
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/namespace.h>
+#include <support/support.h>
+#include <support/temp_file.h>
+#include <support/xthread.h>
+#include <support/xunistd.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+/* Paths for chroot operations. */
+static char *chroot_1;
+static char *chroot_2;
+static char *chroot_3;
+
+/* These paths are used for recognizing chroots. */
+static const char *const chroot_1_marker = "/chroot-1-marker";
+static const char *const chroot_2_marker = "/chroot-2-marker";
+
+/* Directory available in the chroot for a second chroot call. */
+static const char *const next_chroot = "/next_chroot";
+
+/* Return 0 for no chroot, 1 for first chroot, 2 for second chroot, or
+ -1 for error. */
+static int
+which_chroot (void)
+{
+ /* If the full (out-of-chroot) path is present, we are not in a
+ chroot. */
+ if (access (chroot_1, F_OK) == 0)
+ return 0;
+ if (access (chroot_1_marker, F_OK) == 0)
+ return 1;
+ if (access (chroot_2_marker, F_OK) == 0)
+ return 2;
+ return -1;
+}
+
+/* Thread attribute requesting per-thread file system attributes. */
+pthread_attr_t attr_perthreadfs;
+
+/* Used to synchronize operations among the threads. This is needed
+ so that the chroot changes happen in the expected order. */
+static pthread_barrier_t barrier;
+
+static void *
+thread_which_chroot_helper (void *closure)
+{
+ int *result = closure;
+ *result = which_chroot ();
+ return NULL;
+}
+
+static void *
+thread_which_chroot_helper_perthread (void *closure)
+{
+ int *result = closure;
+ *result = which_chroot ();
+ if (*result == 0)
+ /* No /next-chroot available without a first chroot. */
+ xchroot (chroot_3);
+ else
+ xchroot (next_chroot);
+ return NULL;
+}
+
+/* Determine the current chroot on another thread. */
+static int
+thread_which_chroot (void)
+{
+ int result1;
+ pthread_t thr = xpthread_create (NULL, thread_which_chroot_helper, &result1);
+ xpthread_join (thr);
+ /* Same using per-thread attributes. They are supposed to be equal.
+ Also change the chroot to check isolation. */
+ int result2;
+ thr = xpthread_create (&attr_perthreadfs,
+ thread_which_chroot_helper_perthread, &result2);
+ xpthread_join (thr);
+ TEST_COMPARE (result1, result2);
+ return result1;
+}
+
+/* Verify that the file system attributes for the current thread are
+ as expected. */
+static void
+check_attributes (const char *where, int expected_chroot)
+{
+ printf ("info: reading file attributes in %s\n", where);
+ TEST_COMPARE (which_chroot (), expected_chroot);
+ TEST_COMPARE (thread_which_chroot (), expected_chroot);
+}
+
+/* Thread function launched with per-thread file system
+ attributes. */
+static void *
+thread_perthreadfs (void *closure)
+{
+ puts ("info: changing file attributes in thread_perthreadfs");
+ xchroot (chroot_1);
+ check_attributes ("thread_perthreadfs", 1);
+
+ pthread_barrier_wait (&barrier);
+ /* File attributes read in thread_sharedfs here. */
+ pthread_barrier_wait (&barrier);
+ /* File attributes read in main thread here. */
+ pthread_barrier_wait (&barrier);
+ /* File attributes changed in thread_sharedfs here. */
+ pthread_barrier_wait (&barrier);
+
+ check_attributes ("thread_perthreadfs", 1);
+
+ pthread_barrier_wait (&barrier);
+ /* File attributes read in main thread here. */
+ pthread_barrier_wait (&barrier);
+ return NULL;
+}
+
+/* Thread function launched with shared file system attributes. */
+static void *
+thread_sharedfs (void *closure)
+{
+ /* Wait for thread_perthreadfs to update chroot. */
+ pthread_barrier_wait (&barrier);
+
+ check_attributes ("thread_sharedfs", 0);
+
+ pthread_barrier_wait (&barrier);
+ /* File attributes read in main thread here. */
+ pthread_barrier_wait (&barrier);
+
+ puts ("info: changing file attributes in thread_sharedfs");
+ xchroot (chroot_2);
+ check_attributes ("thread_sharedfs", 2);
+
+ pthread_barrier_wait (&barrier);
+ /* File attributes read in thread_perthreadfs here. */
+ pthread_barrier_wait (&barrier);
+ /* File attributes read in main thread here. */
+ pthread_barrier_wait (&barrier);
+ return NULL;
+}
+
+static int
+do_test (void)
+{
+ support_become_root ();
+ if (!support_can_chroot ())
+ FAIL_UNSUPPORTED ("chroot not supported");
+
+ /* Used to revert the effect of chroot. */
+ int original_chroot_fd = xopen ("/", O_DIRECTORY | O_RDONLY, 0);
+
+ TEST_COMPARE (access (chroot_1_marker, F_OK), -1);
+ TEST_COMPARE (access (chroot_2_marker, F_OK), -1);
+ TEST_COMPARE (access (next_chroot, F_OK), -1);
+
+ chroot_1 = support_create_temp_directory
+ ("tst-pthread-perthreadfs-chroot-1-");
+ chroot_2 = support_create_temp_directory
+ ("tst-pthread-perthreadfs-chroot-2-");
+ chroot_3 = support_create_temp_directory
+ ("tst-pthread-perthreadfs-chroot-3-");
+ {
+ char *path = xasprintf ("%s%s", chroot_1, chroot_1_marker);
+ xmkdir (path, 0777);
+ add_temp_file (path);
+ free (path);
+
+ path = xasprintf ("%s%s", chroot_1, next_chroot);
+ xmkdir (path, 0777);
+ add_temp_file (path);
+ free (path);
+
+ path = xasprintf ("%s%s", chroot_2, chroot_2_marker);
+ xmkdir (path, 0777);
+ add_temp_file (path);
+ free (path);
+
+ path = xasprintf ("%s%s", chroot_2, next_chroot);
+ xmkdir (path, 0777);
+ add_temp_file (path);
+ free (path);
+ }
+ TEST_COMPARE (which_chroot (), 0);
+
+ xpthread_barrier_init (&barrier, NULL, 3);
+ xpthread_attr_init (&attr_perthreadfs);
+ TEST_COMPARE (pthread_attr_setperthreadfs_np (&attr_perthreadfs,
+ PTHREAD_PER_THREAD_NP), 0);
+
+ pthread_t thr1 = xpthread_create (&attr_perthreadfs,
+ thread_perthreadfs, NULL);
+ pthread_t thr2 = xpthread_create (NULL, thread_sharedfs, NULL);
+
+ /* Wait for thread_perthreadfs to update chroot. */
+ xpthread_barrier_wait (&barrier);
+ /* File attributes read thread_sharedfs here. */
+ xpthread_barrier_wait (&barrier);
+
+ check_attributes ("main thread", 0);
+
+ xpthread_barrier_wait (&barrier);
+ /* File attributes changed thread_sharedfs here. */
+ xpthread_barrier_wait (&barrier);
+ /* File attributes read in thread_perthreadfs here. */
+ xpthread_barrier_wait (&barrier);
+
+ check_attributes ("main thread", 2);
+
+ xpthread_barrier_wait (&barrier);
+
+ xpthread_join (thr2);
+ xpthread_join (thr1);
+
+ xpthread_attr_destroy (&attr_perthreadfs);
+ xpthread_barrier_destroy (&barrier);
+
+ free (chroot_3);
+ free (chroot_2);
+ free (chroot_1);
+
+ xfchdir (original_chroot_fd);
+ xclose (original_chroot_fd);
+ xchroot (".");
+
+ return 0;
+}
+
+#include <support/test-driver.c>
new file mode 100644
@@ -0,0 +1,328 @@
+/* Test per-thread file system attributes.
+ Copyright (C) 2019 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
+ <http://www.gnu.org/licenses/>. */
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/temp_file.h>
+#include <support/xthread.h>
+#include <support/xunistd.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+/* Original values in the parent process. */
+static char *original_cwd;
+static mode_t original_umask;
+
+/* New values for the attributes, distinct from the old. */
+static char *new_cwd_1;
+static char *new_cwd_2;
+static mode_t new_umask_1;
+static mode_t new_umask_2;
+
+/* Thread attribute requesting per-thread file system attributes. */
+pthread_attr_t attr_perthreadfs;
+
+/* Used to synchronize operations among the threads. This is needed
+ so that the attribute changes happen in the expected order, and
+ that get_umask_unlocked can be used safely. */
+static pthread_barrier_t barrier;
+
+/* Return true if the thread has per-thread file system attributes.
+ Note: This function calls pthread_getattr_np on THR, so the caller
+ has to ensure that the thread is still running (and not merely
+ joinable). */
+static bool
+perthread_flag (pthread_t thr)
+{
+ pthread_attr_t attr;
+ int ret = pthread_getattr_np (thr, &attr);
+ if (ret != 0)
+ {
+ errno = ret;
+ FAIL_EXIT1 ("pthread_getattr_np: %m");
+ }
+ int flag = -1;
+ pthread_attr_getperthreadfs_np (&attr, &flag);
+ if (flag != PTHREAD_PER_THREAD_NP)
+ TEST_COMPARE (flag, PTHREAD_PER_PROCESS_NP);
+ xpthread_attr_destroy (&attr);
+ return flag == PTHREAD_PER_THREAD_NP;
+}
+
+/* Linux does not have getumask, so use synchronization and the umask
+ system call to read the value. */
+static mode_t
+get_umask_unlocked (void)
+{
+ mode_t mask = umask (0);
+ TEST_COMPARE (umask (mask), 0);
+ return mask;
+}
+
+/* Return a heap-allocted string with the path of the current
+ directory. If CLOSURE is not null, change to that directory
+ subsequently. */
+static void *
+thread_get_pwd_helper (void *closure)
+{
+ char *result = get_current_dir_name ();
+ if (closure != NULL)
+ xchdir (closure);
+ return result;
+}
+
+/* Obtain the current directory on another thread. */
+static char *
+thread_get_pwd (void)
+{
+ pthread_t thr = xpthread_create (NULL, thread_get_pwd_helper, NULL);
+ char *path1 = xpthread_join (thr);
+ /* Same using per-thread attributes. They are supposed to be equal.
+ Also change the current directory to check isolation. */
+ thr = xpthread_create (&attr_perthreadfs,
+ thread_get_pwd_helper, (char *) "/");
+ char *path2 = xpthread_join (thr);
+ TEST_COMPARE_STRING (path1, path2);
+ free (path2);
+ path2 = get_current_dir_name ();
+ TEST_COMPARE_STRING (path1, path2);
+ free (path2);
+ return path1;
+}
+
+static void *
+thread_get_umask_helper (void *closure)
+{
+ mode_t *pmask = closure;
+ *pmask = get_umask_unlocked ();
+ return NULL;
+}
+
+static void *
+thread_get_umask_helper_perthread (void *closure)
+{
+ mode_t *pmask = closure;
+ *pmask = get_umask_unlocked ();
+ /* Check isolation. This bit pattern is not used anywhere else. */
+ TEST_COMPARE (umask (*pmask ^ 0101), *pmask);
+ return NULL;
+}
+
+/* Obtain the current umask on another thread. */
+static mode_t
+thread_get_umask_unlocked (void)
+{
+ mode_t mask1;
+ pthread_t thr = xpthread_create (NULL, thread_get_umask_helper, &mask1);
+ xpthread_join (thr);
+ mode_t mask2;
+ thr = xpthread_create (&attr_perthreadfs,
+ thread_get_umask_helper_perthread, &mask2);
+ xpthread_join (thr);
+ TEST_COMPARE (mask1, mask2);
+ return mask1;
+}
+
+/* Verify that the file system attributes for the current thread are
+ as expected. */
+static void
+check_attributes (const char *where, mode_t expected_umask,
+ const char *expected_cwd)
+{
+ printf ("info: reading file attributes in %s\n", where);
+ TEST_COMPARE (get_umask_unlocked (), expected_umask);
+ TEST_COMPARE (thread_get_umask_unlocked (), expected_umask);
+ char *cwd = get_current_dir_name ();
+ TEST_COMPARE_STRING (cwd, expected_cwd);
+ free (cwd);
+ cwd = thread_get_pwd ();
+ TEST_COMPARE_STRING (cwd, expected_cwd);
+ free (cwd);
+}
+
+/* Thread function launched with per-thread file system
+ attributes. */
+static void *
+thread_perthreadfs (void *closure)
+{
+ puts ("info: changing file attributes in thread_perthreadfs");
+ TEST_VERIFY (perthread_flag (pthread_self ()));
+ xchdir (new_cwd_1);
+ TEST_COMPARE (umask (new_umask_1), original_umask);
+ check_attributes ("thread_perthreadfs", new_umask_1, new_cwd_1);
+
+ pthread_barrier_wait (&barrier);
+ /* File attributes read in thread_sharedfs here. */
+ pthread_barrier_wait (&barrier);
+ /* File attributes read in main thread here. */
+ pthread_barrier_wait (&barrier);
+ /* File attributes changed in thread_sharedfs here. */
+ pthread_barrier_wait (&barrier);
+
+ check_attributes ("thread_perthreadfs", new_umask_1, new_cwd_1);
+
+ pthread_barrier_wait (&barrier);
+ /* File attributes read in main thread here. */
+ pthread_barrier_wait (&barrier);
+ return NULL;
+}
+
+/* Launched with PTHREAD_PER_THREAD_NP, but runs the actual test on a
+ PTHREAD_PER_PROCESS_NP thread (with a default attribute). */
+static void *
+thread_perthreadfs_indirect (void *closure)
+{
+ TEST_VERIFY (perthread_flag (pthread_self ()));
+ /* Use the default NULL attribute here. Since the per-thread scope
+ is sticky, it will still result in a per-thread file system
+ thread. */
+ pthread_t thr = xpthread_create (NULL, thread_perthreadfs, closure);
+ return xpthread_join (thr);
+}
+
+/* Thread function launched with shared file system attributes. */
+static void *
+thread_sharedfs (void *closure)
+{
+ TEST_VERIFY (!perthread_flag (pthread_self ()));
+
+ /* Wait for thread_perthreadfs to update current directory and
+ umask. */
+ pthread_barrier_wait (&barrier);
+
+ check_attributes ("thread_sharedfs", original_umask, original_cwd);
+
+ pthread_barrier_wait (&barrier);
+ /* File attributes read in main thread here. */
+ pthread_barrier_wait (&barrier);
+
+ puts ("info: changing file attributes in thread_sharedfs");
+ TEST_COMPARE (umask (new_umask_2), original_umask);
+ xchdir (new_cwd_2);
+ check_attributes ("thread_sharedfs", new_umask_2, new_cwd_2);
+
+ pthread_barrier_wait (&barrier);
+ /* File attributes read in thread_perthreadfs here. */
+ pthread_barrier_wait (&barrier);
+ /* File attributes read in main thread here. */
+ pthread_barrier_wait (&barrier);
+ return NULL;
+}
+
+static int
+do_test (void)
+{
+ original_cwd = get_current_dir_name ();
+ TEST_VERIFY_EXIT (original_cwd != NULL);
+ original_umask = get_umask_unlocked ();
+
+ new_cwd_1 = support_create_temp_directory ("tst-pthread-perthreadfs-1-");
+ new_cwd_2 = support_create_temp_directory ("tst-pthread-perthreadfs-2-");
+ /* Arbitrary bit pattern change to obtain distinct values, so that
+ it is possible to check for actual changes. */
+ new_umask_1 = original_umask ^ 0111;
+ new_umask_2 = original_umask ^ 0222;
+
+ TEST_VERIFY (!perthread_flag (pthread_self ()));
+
+ xpthread_barrier_init (&barrier, NULL, 3);
+ xpthread_attr_init (&attr_perthreadfs);
+ {
+ /* Test: Default is PTHREAD_PER_PROCESS_NP. */
+ int scope = -1;
+ TEST_COMPARE (pthread_attr_getperthreadfs_np (&attr_perthreadfs, &scope),
+ 0);
+ TEST_COMPARE (scope, PTHREAD_PER_PROCESS_NP);
+
+ /* Test: The getter shows the effect of the setter. */
+ TEST_COMPARE (pthread_attr_setperthreadfs_np (&attr_perthreadfs,
+ PTHREAD_PER_THREAD_NP), 0);
+ scope = -1;
+ TEST_COMPARE (pthread_attr_getperthreadfs_np (&attr_perthreadfs, &scope),
+ 0);
+ TEST_COMPARE (scope, PTHREAD_PER_THREAD_NP);
+
+ /* Test: Invalid scope values result in an error, without a
+ change. */
+ TEST_COMPARE (pthread_attr_setperthreadfs_np (&attr_perthreadfs, 2),
+ EINVAL);
+ scope = -1;
+ TEST_COMPARE (pthread_attr_getperthreadfs_np (&attr_perthreadfs, &scope),
+ 0);
+ TEST_COMPARE (scope, PTHREAD_PER_THREAD_NP);
+ }
+
+ /* Two test runs, once with perthreadsfs set to
+ PTHREAD_PER_THREAD_NP directly, once indirectly via inheritance
+ from the current (overriding the PTHREAD_PER_PROCESS_NP
+ default). */
+ for (int do_indirect = 0; do_indirect < 2; ++do_indirect)
+ {
+ printf ("info: test iteration with do_indirect == %d\n", do_indirect);
+ pthread_t thr1;
+ if (do_indirect)
+ thr1 = xpthread_create (&attr_perthreadfs,
+ thread_perthreadfs_indirect, NULL);
+ else
+ thr1 = xpthread_create (&attr_perthreadfs, thread_perthreadfs, NULL);
+ pthread_t thr2 = xpthread_create (NULL, thread_sharedfs, NULL);
+ /* Both threads wait on the barrier before exiting, so they are
+ running at his point, and the call to perthread_flag is
+ safe. */
+ TEST_VERIFY (perthread_flag (thr1));
+ TEST_VERIFY (!perthread_flag (thr2));
+
+ /* Wait for thread_perthreadfs to update current directory and
+ umask. */
+ xpthread_barrier_wait (&barrier);
+ /* File attributes read thread_sharedfs here. */
+ xpthread_barrier_wait (&barrier);
+
+ check_attributes ("main thread", original_umask, original_cwd);
+
+ xpthread_barrier_wait (&barrier);
+ /* File attributes changed thread_sharedfs here. */
+ xpthread_barrier_wait (&barrier);
+ /* File attributes read in thread_perthreadfs here. */
+ xpthread_barrier_wait (&barrier);
+
+ check_attributes ("main thread", new_umask_2, new_cwd_2);
+
+ xpthread_barrier_wait (&barrier);
+
+ xpthread_join (thr2);
+ xpthread_join (thr1);
+
+ xchdir (original_cwd);
+ umask (original_umask);
+ }
+
+ xpthread_attr_destroy (&attr_perthreadfs);
+ xpthread_barrier_destroy (&barrier);
+
+ free (new_cwd_2);
+ free (new_cwd_1);
+ free (original_cwd);
+ return 0;
+}
+
+#include <support/test-driver.c>
@@ -49,6 +49,18 @@ struct pthread_attr
#define ATTR_FLAG_SCHED_SET 0x0020
#define ATTR_FLAG_POLICY_SET 0x0040
#define ATTR_FLAG_DO_RSEQ 0x0080
+#define ATTR_FLAG_PERTHREADFS 0x0100
+
+/* These flags are not copied from the thread attribute at
+ pthread_create time. */
+#define ATTR_FLAGS_IGNORED_ATTR \
+ (ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET)
+
+/* These flags are inherited from the current thread during
+ pthread_create even if they are not specified in the thread
+ attribute. */
+#define ATTR_FLAGS_INHERITED \
+ ATTR_FLAG_PERTHREADFS
/* Used to allocate a pthread_attr_t object which is also accessed
internally. */
@@ -149,7 +149,16 @@ enum
#define PTHREAD_PROCESS_SHARED PTHREAD_PROCESS_SHARED
};
-
+#ifdef __USE_GNU
+/* Thread-private or process-global flag. */
+enum
+{
+ PTHREAD_PER_PROCESS_NP,
+# define PTHREAD_PER_PROCESS_NP PTHREAD_PER_PROCESS_NP
+ PTHREAD_PER_THREAD_NP
+# define PTHREAD_PER_THREAD_NP PTHREAD_PER_THREAD_NP
+};
+#endif /* __USE_GNU */
/* Conditional variable handling. */
#define PTHREAD_COND_INITIALIZER { { {0}, {0}, {0, 0}, {0, 0}, 0, 0, {0, 0} } }
@@ -403,6 +412,21 @@ extern int pthread_attr_getaffinity_np (const pthread_attr_t *__attr,
cpu_set_t *__cpuset)
__THROW __nonnull ((1, 3));
+
+/* Change the property in *ATTR which indicates whether the thread has
+ its own current directory, file system root, and umask attribute.
+ SCOPE must be PTHREAD_PER_PROCESS_NP or PTHREAD_PER_THREAD_NP. By
+ default, new threads share these attributes with their creating
+ thread (PTHREAD_PER_PROCESS_NP). */
+int pthread_attr_setperthreadfs_np (pthread_attr_t *__attr, int __scope)
+ __THROW __nonnull ((1));
+
+/* Get the per-thread/per-process scope of file system attributes from
+ *ATTR and store it in *SCOPE. */
+int pthread_attr_getperthreadfs_np (const pthread_attr_t *__restrict __attr,
+ int *__restrict __scope)
+ __THROW __nonnull ((1, 2));
+
/* Get the default attributes used by pthread_create in this process. */
extern int pthread_getattr_default_np (pthread_attr_t *__attr)
__THROW __nonnull ((1));
@@ -2633,3 +2633,5 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
@@ -2730,6 +2730,8 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
GLIBC_2.4 _IO_fprintf F
GLIBC_2.4 _IO_printf F
GLIBC_2.4 _IO_sprintf F
@@ -2394,3 +2394,5 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
@@ -513,6 +513,8 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
GLIBC_2.4 _Exit F
GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
@@ -510,6 +510,8 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
GLIBC_2.4 _Exit F
GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
@@ -2669,3 +2669,5 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
@@ -2618,6 +2618,8 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
GLIBC_2.4 __confstr_chk F
GLIBC_2.4 __fgets_chk F
GLIBC_2.4 __fgets_unlocked_chk F
@@ -2802,6 +2802,8 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
GLIBC_2.4 __confstr_chk F
GLIBC_2.4 __fgets_chk F
GLIBC_2.4 __fgets_unlocked_chk F
@@ -2568,6 +2568,8 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
GLIBC_2.4 __confstr_chk F
GLIBC_2.4 __fgets_chk F
GLIBC_2.4 __fgets_unlocked_chk F
@@ -2154,3 +2154,5 @@ GLIBC_2.36 wprintf F
GLIBC_2.36 write F
GLIBC_2.36 writev F
GLIBC_2.36 wscanf F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
@@ -514,6 +514,8 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
GLIBC_2.4 _Exit F
GLIBC_2.4 _IO_2_1_stderr_ D 0x98
GLIBC_2.4 _IO_2_1_stdin_ D 0x98
@@ -2745,6 +2745,8 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
GLIBC_2.4 __confstr_chk F
GLIBC_2.4 __fgets_chk F
GLIBC_2.4 __fgets_unlocked_chk F
@@ -2718,3 +2718,5 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
@@ -2715,3 +2715,5 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
@@ -2710,6 +2710,8 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
GLIBC_2.4 __confstr_chk F
GLIBC_2.4 __fgets_chk F
GLIBC_2.4 __fgets_unlocked_chk F
@@ -2708,6 +2708,8 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
GLIBC_2.4 __confstr_chk F
GLIBC_2.4 __fgets_chk F
GLIBC_2.4 __fgets_unlocked_chk F
@@ -2716,6 +2716,8 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
GLIBC_2.4 __confstr_chk F
GLIBC_2.4 __fgets_chk F
GLIBC_2.4 __fgets_unlocked_chk F
@@ -2619,6 +2619,8 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
GLIBC_2.4 __confstr_chk F
GLIBC_2.4 __fgets_chk F
GLIBC_2.4 __fgets_unlocked_chk F
@@ -2757,3 +2757,5 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
@@ -2140,3 +2140,5 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
@@ -2772,6 +2772,8 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
GLIBC_2.4 _IO_fprintf F
GLIBC_2.4 _IO_printf F
GLIBC_2.4 _IO_sprintf F
@@ -2805,6 +2805,8 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
GLIBC_2.4 _IO_fprintf F
GLIBC_2.4 _IO_printf F
GLIBC_2.4 _IO_sprintf F
@@ -2527,6 +2527,8 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
GLIBC_2.4 _IO_fprintf F
GLIBC_2.4 _IO_printf F
GLIBC_2.4 _IO_sprintf F
@@ -2829,3 +2829,5 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
@@ -2396,3 +2396,5 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
@@ -2596,3 +2596,5 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
@@ -2770,6 +2770,8 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
GLIBC_2.4 _IO_fprintf F
GLIBC_2.4 _IO_printf F
GLIBC_2.4 _IO_sprintf F
@@ -2564,6 +2564,8 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
GLIBC_2.4 _IO_fprintf F
GLIBC_2.4 _IO_printf F
GLIBC_2.4 _IO_sprintf F
@@ -2625,6 +2625,8 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
GLIBC_2.4 __confstr_chk F
GLIBC_2.4 __fgets_chk F
GLIBC_2.4 __fgets_unlocked_chk F
@@ -2622,6 +2622,8 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
GLIBC_2.4 __confstr_chk F
GLIBC_2.4 __fgets_chk F
GLIBC_2.4 __fgets_unlocked_chk F
@@ -2765,6 +2765,8 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
GLIBC_2.4 _IO_fprintf F
GLIBC_2.4 _IO_printf F
GLIBC_2.4 _IO_sprintf F
@@ -2591,6 +2591,8 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
GLIBC_2.4 __confstr_chk F
GLIBC_2.4 __fgets_chk F
GLIBC_2.4 __fgets_unlocked_chk F
@@ -2542,6 +2542,8 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F
GLIBC_2.4 __confstr_chk F
GLIBC_2.4 __fgets_chk F
GLIBC_2.4 __fgets_unlocked_chk F
@@ -2648,3 +2648,5 @@ GLIBC_2.36 pidfd_open F
GLIBC_2.36 pidfd_send_signal F
GLIBC_2.36 process_madvise F
GLIBC_2.36 process_mrelease F
+GLIBC_2.37 pthread_attr_getperthreadfs_np F
+GLIBC_2.37 pthread_attr_setperthreadfs_np F