This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH] Linux: Implement per-thread user and group IDs


On 7/1/19 12:35 PM, Florian Weimer wrote:
* Carlos O'Donell:

On 7/1/19 12:49 AM, Florian Weimer wrote:
* Carlos O'Donell:

We might have to use SIGSETXID for something else in the future (or
perhaps we already do).  I would leave this as it is today.

Can you expand on this?

Today SIGSETXID is reserved explicitly for this use, and it's a per-process
signal handler, so it's always active until you remove the last use of
SIGSETXID by replacing it with a kernel mechanism for transitioning IDs.

I meant that we might want to use the signal broadcast for something else,
not just set*id calls.

We can remove the blocking code / assertions at that point?

I think even a minimal check in the handler that checks to see if there
is a per-thread uid/gid in effect and does nothing might be sufficient
here to pevent any accidental usage?

Right now your only consistency checks are on the caller side.

We could probably start looking at siginfo_t and verify that the signal
was sent by the same process.  Most of such checking would be rather
general, and unrelated to the skipped threads.  That would be a separate
change, I think.

The other concerns I've addressed with the patch below, I hope.

Thanks,
Florian

Linux: Implement per-thread user and group IDs

This commit adds the functions pthread_attr_setperthreadids_np and
pthread_attr_getperthreadids_np.  Threads created with the new flag
will be exempted from the setxid broadcast.  setuid and related
functions will only update the credentials for the current thread.

Multi-threaded file servers typically need this functionality and
call the system calls directly to implement this.


This patch is OK for master if you cleanup the public comment you
added in pthread.h to avoid the use of "flag" and instead only talk
about scope.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>

2019-06-28  Florian Weimer  <fweimer@redhat.com>

	Linux: Implement per-thread user and group IDs.
	* manual/threads.texi (Enabling Per-Thread Properties): Document
	pthread_attr_setperthreadids_np, pthread_attr_getperthreadids_np.
	* sysdeps/nptl/pthread.h (pthread_attr_setperthreadids_np)
	(pthread_attr_getperthreadids_np): Declare.
	* nptl/Makefile (routines): Add pthread_attr_setperthreadids_np,
	pthread_attr_getperthreadids_np.
	* nptl/Versions (GLIBC_2.29): Export
	pthread_attr_setperthreadids_np, pthread_attr_getperthreadids_np.
	* nptl/allocatestack.c (thread_excluded_from_setxid_broadcast):
	New function.
	(__nptl_setxid): Use it.
	* nptl/pthreadP.h (nptl_current_thread_has_separate_ids): New
	function.
	* susdeps/nptl/setxid.h (INLINE_SETXID_SYSCALL): Check
	nptl_current_thread_has_separate_ids before setxid broadcast.
	* nptl/pthread_attr_setperthreadids_np.c: New file
	* nptl/pthread_attr_getperthreadids_np.c: Likewise.
	* sysdeps/nptl/internaltypes.h (ATTR_FLAG_PERTHREADIDS)
	(ATTR_FLAGS_IGNORED_ATTR, ATTR_FLAGS_INHERITED): New macros.
	* nptl/pthread_create.c (__pthread_create_2_1): Use
	ATTR_FLAGS_IGNORED_ATTR and ATTR_FLAGS_INHERITED to compute the
	flags for the new thread.
	* nptl/tst-pthread-perthreadids.c: New file.
	* support/Makefile (libsupport-routines): Add xgetresgid,
	xgetresuid.
	* support/xgetresgid.c: New file.
	* support/xsetresgid.c: Likewise.
	* support/xunistd.h (xgetresuid, xgetresgid): Declare.
	* sysdeps/unix/sysv/linux/aarch64/libc.abilist (GLIBC_2.30): Add
	pthread_attr_getperthreadids_np, pthread_attr_setperthreadids_np.
	* sysdeps/unix/sysv/linux/alpha/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/arm/libc.abilist (GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/csky/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/hppa/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/i386/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/ia64/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/microblaze/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/nios2/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/sh/libc.abilist (GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/x86_64/64/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist (GLIBC_2.30):
	Likewise.

diff --git a/NEWS b/NEWS
index e63b69b930..1bbabcf2d6 100644
--- a/NEWS
+++ b/NEWS
@@ -45,6 +45,11 @@ Major new features:
    pthread_attr_setperthreadfs_np and pthread_attr_getperthreadfs_np have
    been added in support of that.
+* On Linux, threads can now be created in such a way that they retain
+  per-thread user and group IDs.  The functions
+  pthread_attr_setperthreadids_np and pthread_attr_getperthreadids_np have
+  been added in support of that.

OK.

+
  Deprecated and removed features, and other changes affecting compatibility:
* The copy_file_range function fails with ENOSYS if the kernel does not
diff --git a/manual/threads.texi b/manual/threads.texi
index 827ecb0929..a64d535afa 100644
--- a/manual/threads.texi
+++ b/manual/threads.texi
@@ -698,11 +698,12 @@ This property in question is thread-specific.
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.
+properties of the requested kind, 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.

OK.

Per-thread properties can be set and examined for an attribute using
  the functions below.
@@ -764,6 +765,58 @@ Obtain the per-thread status of the file system properties in
  This function is a GNU extension and specific to Linux.
  @end deftypefun
+@deftypefun int pthread_attr_setperthreadids_np (pthread_attr_t *@var{attr}, int @var{scope})

OK.

+@standards{GNU, pthread.h}
+@safety{@prelim{}@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 per-thread user ID
+@cindex thread-specific user ID
+real, effective and saved user ID (as returned by the
+@code{getresuid} function)
+
+@item
+@cindex per-thread group ID
+@cindex thread-specific group ID
+real, effective and saved group ID (as returned by the
+@code{getresgid} function)
+
+@item
+supplementary group list (as returned by the @code{getgroups}
+function)
+@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 IDs listed above to be specific to the thread.  The initial
+values of these IDs 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_getperthreadids_np (pthread_attr_t *restrict @var{attr}, int *restrict @var{scope})

OK.

+@standards{GNU, pthread.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+Obtain the per-thread status of user and group IDs in @var{attr} and
+store it in the location @var{scope}.
+
+This function is a GNU extension and specific to Linux.
+@end deftypefun
+
  @c FIXME these are undocumented:
  @c pthread_atfork
  @c pthread_attr_destroy
diff --git a/nptl/Makefile b/nptl/Makefile
index 1374838339..52913cc2f0 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -31,7 +31,8 @@ routines = alloca_cutoff forward libc-lowlevellock libc-cancellation \
  	   libc-cleanup libc_pthread_init libc_multiple_threads \
  	   register-atfork pthread_atfork pthread_self thrd_current \
  	   thrd_equal thrd_sleep thrd_yield \
-	   pthread_attr_setperthreadfs_np pthread_attr_getperthreadfs_np
+	   pthread_attr_setperthreadfs_np pthread_attr_getperthreadfs_np \
+	   pthread_attr_setperthreadids_np pthread_attr_getperthreadids_np

OK.

  shared-only-routines = forward
  static-only-routines = pthread_atfork
@@ -337,6 +338,9 @@ xtests = tst-setuid1 tst-setuid1-static tst-setuid2 \
  # and then cause the make process to fail too, see bug 24537.
  xtests += tst-eintr1
+# This thread calls various set*id functions.
+xtests += tst-pthread-perthreadids
+
  test-srcs = tst-oddstacklimit
# Test expected to fail on most targets (except x86_64) due to bug
diff --git a/nptl/Versions b/nptl/Versions
index 817fec04f3..bedc73878e 100644
--- a/nptl/Versions
+++ b/nptl/Versions
@@ -35,6 +35,8 @@ libc {
    GLIBC_2.30 {
      pthread_attr_setperthreadfs_np;
      pthread_attr_getperthreadfs_np;
+    pthread_attr_setperthreadids_np;
+    pthread_attr_getperthreadids_np;

OK.

    }
    GLIBC_PRIVATE {
      __libc_alloca_cutoff;
diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c
index fcbc46f0d7..9d238a001b 100644
--- a/nptl/allocatestack.c
+++ b/nptl/allocatestack.c
@@ -1067,6 +1067,14 @@ __nptl_setxid_error (struct xid_command *cmdp, int error)
    while (atomic_compare_and_exchange_bool_acq (&cmdp->error, error, -1));
  }
+/* The current thread and threads with per-thread user and group IDs
+   are not part of the setxid broadcast.  */
+static inline bool
+thread_excluded_from_setxid_broadcast (struct pthread *self, struct pthread *t)
+{
+  return t == self || (t->flags & ATTR_FLAG_PERTHREADIDS);
+}

OK.

+
  int
  attribute_hidden
  __nptl_setxid (struct xid_command *cmdp)
@@ -1080,13 +1088,14 @@ __nptl_setxid (struct xid_command *cmdp)
    cmdp->error = -1;
struct pthread *self = THREAD_SELF;
+  assert (!nptl_current_thread_has_separate_ids ());

OK.

/* Iterate over the list with system-allocated threads first. */
    list_t *runp;
    list_for_each (runp, &stack_used)
      {
        struct pthread *t = list_entry (runp, struct pthread, list);
-      if (t == self)
+      if (thread_excluded_from_setxid_broadcast (self, t))
  	continue;
setxid_mark_thread (cmdp, t);
@@ -1096,7 +1105,7 @@ __nptl_setxid (struct xid_command *cmdp)
    list_for_each (runp, &__stack_user)
      {
        struct pthread *t = list_entry (runp, struct pthread, list);
-      if (t == self)
+      if (thread_excluded_from_setxid_broadcast (self, t))
  	continue;
setxid_mark_thread (cmdp, t);
@@ -1112,7 +1121,7 @@ __nptl_setxid (struct xid_command *cmdp)
        list_for_each (runp, &stack_used)
  	{
  	  struct pthread *t = list_entry (runp, struct pthread, list);
-	  if (t == self)
+	  if (thread_excluded_from_setxid_broadcast (self, t))
  	    continue;
signalled += setxid_signal_thread (cmdp, t);
@@ -1121,7 +1130,7 @@ __nptl_setxid (struct xid_command *cmdp)
        list_for_each (runp, &__stack_user)
  	{
  	  struct pthread *t = list_entry (runp, struct pthread, list);
-	  if (t == self)
+	  if (thread_excluded_from_setxid_broadcast (self, t))
  	    continue;
signalled += setxid_signal_thread (cmdp, t);
@@ -1142,7 +1151,7 @@ __nptl_setxid (struct xid_command *cmdp)
    list_for_each (runp, &stack_used)
      {
        struct pthread *t = list_entry (runp, struct pthread, list);
-      if (t == self)
+      if (thread_excluded_from_setxid_broadcast (self, t))
  	continue;
setxid_unmark_thread (cmdp, t);
@@ -1151,7 +1160,7 @@ __nptl_setxid (struct xid_command *cmdp)
    list_for_each (runp, &__stack_user)
      {
        struct pthread *t = list_entry (runp, struct pthread, list);
-      if (t == self)
+      if (thread_excluded_from_setxid_broadcast (self, t))
  	continue;

OK.

setxid_unmark_thread (cmdp, t);
diff --git a/nptl/pthreadP.h b/nptl/pthreadP.h
index 66527d8f2d..719bae5173 100644
--- a/nptl/pthreadP.h
+++ b/nptl/pthreadP.h
@@ -598,6 +598,14 @@ check_stacksize_attr (size_t st)
    return EINVAL;
  }
+/* Return true if the current thread has per-thread user and group
+   IDS.  */
+static inline bool
+nptl_current_thread_has_separate_ids (void)
+{
+  return THREAD_SELF->flags & ATTR_FLAG_PERTHREADIDS;
+}

OK.

+
  #define ASSERT_TYPE_SIZE(type, size) 					\
    _Static_assert (sizeof (type) == size,				\
  		  "sizeof (" #type ") != " #size)
diff --git a/nptl/pthread_attr_getperthreadids_np.c b/nptl/pthread_attr_getperthreadids_np.c
new file mode 100644
index 0000000000..18f88f1f3e
--- /dev/null
+++ b/nptl/pthread_attr_getperthreadids_np.c
@@ -0,0 +1,32 @@
+/* Read the per-thread user/group IDs 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_getperthreadids_np (const pthread_attr_t *__restrict attr,
+                                int *__restrict scope)
+{
+  struct pthread_attr *iattr = (struct pthread_attr *) attr;
+  if (iattr->flags & ATTR_FLAG_PERTHREADIDS)
+    *scope = PTHREAD_PER_THREAD_NP;
+  else
+    *scope = PTHREAD_PER_PROCESS_NP;
+  return 0;
+}

OK.

diff --git a/nptl/pthread_attr_setperthreadids_np.c b/nptl/pthread_attr_setperthreadids_np.c
new file mode 100644
index 0000000000..5385ab0bae
--- /dev/null
+++ b/nptl/pthread_attr_setperthreadids_np.c
@@ -0,0 +1,39 @@
+/* Change the per-thread user/group IDs 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_setperthreadids_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_PERTHREADIDS;
+      return 0;
+      break;
+    case PTHREAD_PER_THREAD_NP:
+      iattr->flags |= ATTR_FLAG_PERTHREADIDS;
+      return 0;
+    default:
+      return EINVAL;

OK.

+    }
+}
diff --git a/nptl/tst-pthread-perthreadids.c b/nptl/tst-pthread-perthreadids.c
new file mode 100644
index 0000000000..05ed098944
--- /dev/null
+++ b/nptl/tst-pthread-perthreadids.c
@@ -0,0 +1,541 @@
+/* Test per-thread user and group IDs.
+   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 <array_length.h>
+#include <errno.h>
+#include <grp.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/namespace.h>
+#include <support/support.h>
+#include <support/test-driver.h>
+#include <support/xthread.h>
+#include <support/xunistd.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+/* 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).  */

OK.

+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_getperthreadids_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;
+}
+
+/* Which set*id function to call.  */
+enum operation
+{
+ /* No function is called, but the thread will observe changes made by
+    another thread.  */
+ OP_NONE,
+
+ OP_SETUID,
+ OP_SETEUID,
+ OP_SETREUID,
+ OP_SETRESUID,
+ OP_SETGID,
+ OP_SETEGID,
+ OP_SETREGID,
+ OP_SETRESGID,
+ OP_SETGROUPS,
+};
+
+/* Convert the operation to a descriptive string.  */
+static const char *
+operation_string (enum operation op)
+{
+  switch (op)
+    {
+    case OP_NONE:
+      return "<none>";
+    case OP_SETUID:
+      return "OP_SETUID";
+    case OP_SETEUID:
+      return "OP_SETEUID";
+    case OP_SETREUID:
+      return "OP_SETREUID";
+    case OP_SETRESUID:
+      return "OP_SETRESUID";
+    case OP_SETGID:
+      return "OP_SETGID";
+    case OP_SETEGID:
+      return "OP_SETEGID";
+    case OP_SETREGID:
+      return "OP_SETREGID";
+    case OP_SETRESGID:
+      return "OP_SETRESGID";
+    case OP_SETGROUPS:
+      return "OP_SETGROUPS";
+    }
+
+  FAIL_EXIT1 ("invalid operation: %d", (int) op);
+}
+
+/* One test case to perform.  */
+struct test_case
+{
+  enum operation op;
+  int args[3];
+
+  /* Expected UIDs and GIDs are only used if the current thread has
+     made changes.  */
+  uid_t expected_uid[3];
+  gid_t expected_gid[3];
+};
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic error "-Wmissing-field-initializers"
+static const struct test_case test_cases[] =
+  {
+   /* op             args         expected_gid  expected_gid.   */
+   { OP_NONE,      {  0,  0,  0 }, { 0, 0, 0 }, {  0,  0,  0} },
+
+   { OP_SETUID,    {  1,  0,  0 }, { 1, 1, 1 }, {  0,  0,  0 } },
+   { OP_SETEUID,   {  2,  0,  0 }, { 0, 2, 0 }, {  0,  0,  0 } },
+   { OP_SETREUID,  {  3,  4,  0 }, { 3, 4, 4 }, {  0,  0,  0 } },
+   { OP_SETRESUID, {  3,  4,  5 }, { 3, 4, 5 }, {  0,  0,  0 } },
+
+   { OP_SETGID,    {  6,  0,  0 }, { 0, 0, 0 }, {  6,  6,  6 } },
+   { OP_SETEGID,   {  7,  0,  0 }, { 0, 0, 0 }, {  0,  7,  0 } },
+   { OP_SETREGID,  {  8,  9,  0 }, { 0, 0, 0 }, {  8,  9,  9 } },
+   { OP_SETRESGID, { 10, 11, 12 }, { 0, 0, 0 }, { 10, 11, 12 } },
+
+   { OP_SETGROUPS, { -1,  0,  0 }, { 0, 0, 0 }, {  0,  0,  0 } },
+   { OP_SETGROUPS, { 13, -1,  0 }, { 0, 0, 0 }, {  0,  0,  0 } },
+   { OP_SETGROUPS, { 13, 14, -1 }, { 0, 0, 0 }, {  0,  0,  0 } },
+   { OP_SETGROUPS, { 13, 14, 15 }, { 0, 0, 0 }, {  0,  0,  0 } },
+
+   /* Final round of checks.  */
+   { OP_NONE,      {  0,  0,  0 }, { 0, 0, 0 }, {  0,  0,  0 } },
+  };
+#pragma GCC diagnostic pop

OK. Perfect.

+
+/* Determine the number of supplementary groups in the test case.  */
+static size_t
+supplemetary_count (const struct test_case *test)
+{
+  TEST_COMPARE (test->op, OP_SETGROUPS);
+  size_t count = 0;
+  while (count < array_length (test->args))
+    {
+      if (test->args[count] < 0)
+        break;
+      ++count;
+    }
+  return count;
+}
+
+/* Perform the actions in the test case.  */
+static void
+test_case_run (const struct test_case *test)
+{
+  int ret = -1;
+  switch (test->op)
+    {
+    case OP_NONE:
+      return;
+
+    case OP_SETUID:
+      ret = setuid (test->args[0]);
+      break;
+    case OP_SETEUID:
+      ret = seteuid (test->args[0]);
+      break;
+    case OP_SETREUID:
+      ret = setreuid (test->args[0], test->args[1]);
+      break;
+    case OP_SETRESUID:
+      ret = setresuid (test->args[0], test->args[1], test->args[2]);
+      break;
+
+    case OP_SETGID:
+      ret = setgid (test->args[0]);
+      break;
+    case OP_SETEGID:
+      ret = setegid (test->args[0]);
+      break;
+    case OP_SETREGID:
+      ret = setregid (test->args[0], test->args[1]);
+      break;
+    case OP_SETRESGID:
+      ret = setresgid (test->args[0], test->args[1], test->args[2]);
+      break;
+
+    case OP_SETGROUPS:
+      {
+        gid_t groups[] = { test->args[0], test->args[1], test->args[2] };
+        ret = setgroups (supplemetary_count (test), groups);
+      }
+    }
+
+  if (ret != 0)
+    FAIL_EXIT1 ("%s (%d, %d, %d): %m (%d)",
+                operation_string (test->op),
+                test->args[0], test->args[1], test->args[2], errno);
+}
+
+/* Used to synchronize between threads changing UIDs/GIDs.  */
+static pthread_barrier_t barrier;
+
+/* Used to avoid interleaving the checking phase between threads.  */
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* Argument to the thread.  */
+struct thread_argument
+{
+  size_t thread_index;
+
+  /* Do not actually perform the operation, only verify the result.
+     Used for threads participating in the setxid broadcast.  */
+  bool suppress_operation;
+
+  /* Run the actual test in a newly created thread, with default
+     attributes.  */
+  bool indirect;
+
+  /* Thread is created with per-thread attributes.  */
+  bool perthread;
+};
+
+/* Prepare a heap-allocated thread argument which the started thread
+   should free.  */
+static struct thread_argument *
+create_thread_argument (struct thread_argument arg)
+{
+  struct thread_argument *result = xmalloc (sizeof (*result));
+  *result = arg;
+  return result;
+}
+
+/* An actual test thread.  CLOSURE is a pointer to struct
+   thread_argument, and perform the test according to this
+   information.  */
+static void *
+threadfunc (void *closure)
+{
+  struct thread_argument *arg = closure;
+
+  TEST_COMPARE (perthread_flag (pthread_self ()), arg->perthread);
+
+  if (arg->indirect)
+    {
+      /* Only indirect once.  */
+      arg->indirect = false;
+      /* Use the default attributes (NULL).  This verifies that the
+         per-thread scope is sticky.  */
+      return xpthread_join (xpthread_create (NULL, threadfunc, arg));
+    }
+
+  const struct test_case *expected = &test_cases[0];
+  gid_t expected_supplementary[3] = { -1, -1, -1 };
+  int expected_supplementary_count = 0;
+
+  /* (1) Wait until all threads have started up.  */
+  xpthread_barrier_wait (&barrier);

OK. Great info at each barrier to explain the condition.

+
+  for (size_t test_index = 0; test_index < array_length (test_cases);
+       ++test_index)
+    {
+      if (test_index == arg->thread_index)
+        {
+          if (test_verbose > 0)
+            {
+              if (arg->suppress_operation)
+                printf ("info: thread %zu suppressing operation\n",
+                        test_index);
+              else
+                printf ("info: thread %zu performing operation\n",
+                        test_index);
+            }
+          if (!arg->suppress_operation)
+            test_case_run (&test_cases[test_index]);
+
+          expected = &test_cases[test_index];
+          if (test_cases[test_index].op == OP_SETGROUPS)
+            {
+              expected_supplementary_count
+                = supplemetary_count (&test_cases[test_index]);
+              for (int i = 0; i < expected_supplementary_count; ++i)
+                expected_supplementary[i] = test_cases[test_index].args[i];
+            }
+        }
+
+      /* (2) Wait until all threads have run the requested test.  */
+      xpthread_barrier_wait (&barrier);
+

OK.

+      xpthread_mutex_lock (&mutex);
+
+      if (test_verbose > 0)
+        printf ("info: checking phase for thread %zu, test %zu\n",
+                arg->thread_index, test_index);
+      uid_t actual_uid[3];
+      xgetresuid (&actual_uid[0], &actual_uid[1], &actual_uid[2]);
+      TEST_COMPARE (actual_uid[0], expected->expected_uid[0]);
+      TEST_COMPARE (actual_uid[1], expected->expected_uid[1]);
+      TEST_COMPARE (actual_uid[2], expected->expected_uid[2]);
+      gid_t actual_gid[3];
+      xgetresgid (&actual_gid[0], &actual_gid[1], &actual_gid[2]);
+      TEST_COMPARE (actual_gid[0], expected->expected_gid[0]);
+      TEST_COMPARE (actual_gid[1], expected->expected_gid[1]);
+      TEST_COMPARE (actual_gid[2], expected->expected_gid[2]);
+
+      gid_t actual_supplementary[3];
+      gid_t actual_supplementary_count
+        = getgroups (array_length (actual_supplementary),
+                     actual_supplementary);
+      TEST_COMPARE (actual_supplementary_count, expected_supplementary_count);
+      if (actual_supplementary_count > 0)
+        TEST_COMPARE (actual_supplementary[0], expected_supplementary[0]);
+      if (actual_supplementary_count > 1)
+        TEST_COMPARE (actual_supplementary[1], expected_supplementary[1]);
+      if (actual_supplementary_count > 2)
+        TEST_COMPARE (actual_supplementary[2], expected_supplementary[2]);
+
+      xpthread_mutex_unlock (&mutex);
+
+      /* (3) Wait until all threads have finished checking their view
+         of the results.  */
+      xpthread_barrier_wait (&barrier);

OK.

+    }
+
+  free (arg);
+  return NULL;
+}
+
+/* Used to create threads with per-thread user/group IDs.  */
+static pthread_attr_t attr_perthreadids;
+
+/* Test which verifies that per-thread IDs are thread-specific.  */
+static void
+check_perthread (bool indirect)
+{
+  if (test_verbose > 0)
+    printf ("info: testing per-thread IDs, %s indirection\n",
+            indirect ? "with" : "without");
+  TEST_VERIFY (!perthread_flag (pthread_self ()));
+
+  /* The main thread and another shared thread count as two extra
+     threads.  */
+  xpthread_barrier_init (&barrier, NULL, array_length (test_cases) + 2);
+
+  pthread_t perthread_threads[array_length (test_cases)];
+  /* Use thread index zero with OP_NONE for checking only.  */
+  pthread_t shared_thread
+    = xpthread_create (NULL, threadfunc,
+                       create_thread_argument ((struct thread_argument) { }));
+  /* Thread is still runing because it waits on barrier.  */
+  TEST_VERIFY (!perthread_flag (shared_thread));
+
+  for (size_t i = 0; i < array_length (test_cases); ++i)
+    {
+      struct thread_argument *arg
+        = create_thread_argument ((struct thread_argument) {
+            .thread_index = i, .indirect = indirect, .perthread = true });
+      perthread_threads[i]
+        = xpthread_create (&attr_perthreadids, threadfunc, arg);
+      /* Thread is still runing because it waits on barrier (or it is
+         blocked joining a thread which does).  */
+      TEST_VERIFY (perthread_flag (perthread_threads[i]));
+    }
+  /* Use thread_index 0 with OP_NONE for checking that the main thread
+     is unchanged.  (This also waits on the barrier.)  */
+  threadfunc (create_thread_argument ((struct thread_argument) { }));
+  for (size_t i = 0; i < array_length (test_cases); ++i)
+    xpthread_join (perthread_threads[i]);
+  xpthread_join (shared_thread);
+
+  xpthread_barrier_destroy (&barrier);
+}
+
+/* Closure arguments to the subprocess.  */
+struct subprocess_argument
+{
+  size_t broadcast_test_index;
+  bool broadcast_from_main;
+  bool indirect;
+};
+
+/* Setting the broadcast UID is destructive, so we need to run it in a
+   subprocess.  */
+static void
+subprocess (void *closure)
+{
+  struct subprocess_argument *arg = closure;
+
+  if (test_verbose > 0)
+    printf ("info: testing broadcasting test case %zu,"
+            " %sbroadcasting from main, %s indirection\n",
+            arg->broadcast_test_index,
+            arg->broadcast_from_main ? "" : "not ",
+            arg->indirect ? "with" : "without");
+
+  /* Main thread and two other shared threads are extras.  One
+     per-thread test is skipped, so there are two extra threads.  */
+  xpthread_barrier_init (&barrier, NULL, array_length (test_cases) + 2);
+
+  pthread_t perthread_threads[array_length (test_cases)];
+  /* Use thread index zero for no-op checking.  */
+  pthread_t shared_threads[2];
+  {
+    struct thread_argument thread_arg =
+      {
+       .thread_index = arg->broadcast_test_index,
+       .suppress_operation =arg->broadcast_from_main,
+      };
+    shared_threads[0]
+      = xpthread_create (NULL, threadfunc,
+                         create_thread_argument (thread_arg));
+  }
+  {
+    struct thread_argument thread_arg =
+      {
+       .thread_index = arg->broadcast_test_index,
+       .suppress_operation = true,
+      };
+    shared_threads[1]
+      = xpthread_create (NULL, threadfunc,
+                         create_thread_argument (thread_arg));
+  }
+
+  for (size_t i = 0; i < array_length (test_cases); ++i)
+    /* Skip the test which uses broadcasting; it does not use the
+       per-thread IDs.  */
+    if (i != arg->broadcast_test_index)
+      {
+        struct thread_argument thread_arg =
+          {
+           .thread_index = i,
+           .indirect = arg->indirect,
+           .perthread = true,
+          };
+        perthread_threads[i]
+          = xpthread_create (&attr_perthreadids, threadfunc,
+                             create_thread_argument (thread_arg));
+      }
+
+  /* Call threadfunc from the main thread.  If not broadcasting from
+     main, only perform checking.  */
+  {
+    struct thread_argument thread_arg =
+      {
+       .thread_index = arg->broadcast_test_index,
+       .suppress_operation = !arg->broadcast_from_main,
+       .indirect = arg->indirect,
+      };
+    threadfunc (create_thread_argument (thread_arg));
+  }
+  for (size_t i = 0; i < array_length (test_cases); ++i)
+    /* Skip the test which uses broadcasting.  */
+    if (i != arg->broadcast_test_index)
+      xpthread_join (perthread_threads[i]);
+  xpthread_join (shared_threads[0]);
+  xpthread_join (shared_threads[1]);
+
+  xpthread_barrier_destroy (&barrier);
+}
+
+static int
+do_test (void)
+{
+  if (setuid (0) != 0)
+    FAIL_EXIT1 ("setuid (0): %m");
+  if (setgid (0) != 0)
+    FAIL_EXIT1 ("setgid (0): %m");
+  if (setgroups (0, NULL) != 0)
+    FAIL_EXIT1 ("setgroups (0, NULL): %m");
+
+  xpthread_attr_init (&attr_perthreadids);
+  {
+    /* Test: Default is PTHREAD_PER_PROCESS_NP.  */
+    int scope = -1;
+    TEST_COMPARE (pthread_attr_getperthreadids_np (&attr_perthreadids, &scope),
+                  0);
+    TEST_COMPARE (scope, PTHREAD_PER_PROCESS_NP);
+
+    /* Test: The getter shows the effect of the setter.  */
+    TEST_COMPARE (pthread_attr_setperthreadids_np (&attr_perthreadids,
+                                                  PTHREAD_PER_THREAD_NP), 0);
+    scope = -1;
+    TEST_COMPARE (pthread_attr_getperthreadids_np (&attr_perthreadids, &scope),
+                  0);
+    TEST_COMPARE (scope, PTHREAD_PER_THREAD_NP);
+
+    /* Test: Invalid scope values result in an error, without a
+       change.  */
+    TEST_COMPARE (pthread_attr_setperthreadids_np (&attr_perthreadids, 2),
+                  EINVAL);
+    scope = -1;
+    TEST_COMPARE (pthread_attr_getperthreadids_np (&attr_perthreadids, &scope),
+                  0);
+    TEST_COMPARE (scope, PTHREAD_PER_THREAD_NP);
+  }
+
+  /* Test both the direct and indirect case.  Direct is when test
+     threads are launched with a per-thread attribute.  Indirect is
+     when a thread is first launched with the a per-thread attribute,
+     but the actual test runs in another sub-thread, created with a
+     NULL attribute.  */
+  for (int indirect = 0; indirect < 2; ++indirect)
+    {
+      check_perthread (indirect);
+
+      /* Test two scenarios, one where set*id function is called from
+         the main thread, and other one where it is called from
+         another thread.  */
+      for (int broadcast_from_main = 0; broadcast_from_main < 2;
+           ++broadcast_from_main)
+        /* Perform each of the test cases once, using a broadcast
+           set*id operation across multiple threads (those threads
+           which have per-process scope).  */
+        for (size_t broadcast_test_index = 0;
+             broadcast_test_index < array_length (test_cases);
+             ++broadcast_test_index)
+          {
+            struct subprocess_argument arg =
+              {
+               .broadcast_test_index = broadcast_test_index,
+               .broadcast_from_main = broadcast_from_main,
+               .indirect = indirect,
+              };
+            support_isolate_in_subprocess (subprocess, &arg);
+          }
+    }
+
+  xpthread_attr_destroy (&attr_perthreadids);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/support/Makefile b/support/Makefile
index 774b0a692a..1ded323015 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -94,6 +94,8 @@ libsupport-routines = \
    xfopen \
    xfork \
    xftruncate \
+  xgetresgid \
+  xgetresuid \
    xgetsockname \
    xlisten \
    xlseek \
diff --git a/support/xgetresgid.c b/support/xgetresgid.c
new file mode 100644
index 0000000000..22990fa633
--- /dev/null
+++ b/support/xgetresgid.c
@@ -0,0 +1,27 @@
+/* getresgid with error checking.
+   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 <support/check.h>
+#include <support/xunistd.h>
+
+void
+xgetresgid (gid_t *real, gid_t *effective, gid_t *saved)
+{
+  if (getresgid (real, effective, saved) != 0)
+    FAIL_EXIT1 ("getresgid: %m");
+}
diff --git a/support/xgetresuid.c b/support/xgetresuid.c
new file mode 100644
index 0000000000..b0cd4e938f
--- /dev/null
+++ b/support/xgetresuid.c
@@ -0,0 +1,27 @@
+/* getresuid with error checking.
+   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 <support/check.h>
+#include <support/xunistd.h>
+
+void
+xgetresuid (uid_t *real, uid_t *effective, uid_t *saved)
+{
+  if (getresuid (real, effective, saved) != 0)
+    FAIL_EXIT1 ("getresuid: %m");
+}
diff --git a/support/xunistd.h b/support/xunistd.h
index b470d99be1..2ad2e9a55c 100644
--- a/support/xunistd.h
+++ b/support/xunistd.h
@@ -46,6 +46,8 @@ long xsysconf (int name);
  long long xlseek (int fd, long long offset, int whence);
  void xftruncate (int fd, long long length);
  void xsymlink (const char *target, const char *linkpath);
+void xgetresuid (uid_t *, uid_t *, uid_t *);
+void xgetresgid (gid_t *, gid_t *, gid_t *);
/* Equivalent of "mkdir -p". */
  void xmkdirp (const char *, mode_t);
diff --git a/sysdeps/nptl/internaltypes.h b/sysdeps/nptl/internaltypes.h
index 5ad56908b5..0e8b2b3f65 100644
--- a/sysdeps/nptl/internaltypes.h
+++ b/sysdeps/nptl/internaltypes.h
@@ -49,6 +49,7 @@ struct pthread_attr
  #define ATTR_FLAG_SCHED_SET		0x0020
  #define ATTR_FLAG_POLICY_SET		0x0040
  #define ATTR_FLAG_PERTHREADFS		0x0080
+#define ATTR_FLAG_PERTHREADIDS		0x0100
/* These flags are not copied from the thread attribute at
     pthread_create time.  */
@@ -59,7 +60,7 @@ struct pthread_attr
     pthread_create even if they are not specified in the thread
     attribute.  */
  #define ATTR_FLAGS_INHERITED \
-  ATTR_FLAG_PERTHREADFS
+  (ATTR_FLAG_PERTHREADFS | ATTR_FLAG_PERTHREADIDS)

OK.

/* Mutex attribute data structure. */
diff --git a/sysdeps/nptl/pthread.h b/sysdeps/nptl/pthread.h
index 4b6a90f131..2ba60867e0 100644
--- a/sysdeps/nptl/pthread.h
+++ b/sysdeps/nptl/pthread.h
@@ -430,6 +430,24 @@ int pthread_attr_getperthreadfs_np (const pthread_attr_t *__restrict __attr,
  				    int *__restrict __scope)
    __THROW __nonnull ((1, 2));
+/* Control the flag in ATTR whether the thread has its own user and
+   group IDs.  By default, when the real, effective, or saved user or
+   group ID is changed by a thread, this affects the entire process.
+   If a thread is created with this flag set to true, then changing
+   the IDs within that thread will only affect that thread, and user
+   and group ID changes in other threads (whether they have enabled
+   this flag or not) do not affect it.  If a thread has been created
+   with this flag, threads created by it will also have their own,
+   private user and group IDs.  */

This is a public header and you are mixing the description of the
implemnetation e.g. flags, and the description of the public interface
which is a scope value.

Please cleanup the paragraph to speak only about the scope value, not
"flag set to true." From the user perspective ther are no flags, just
a scope set to one of two values and store in an attribute object.

+int pthread_attr_setperthreadids_np (pthread_attr_t *__attr, int __scope)
+  __THROW __nonnull ((1));
+
+/* Get the per-thread/per-process scope of user and group IDs from
+   *ATTR and store it in *SCOPE.  */
+int pthread_attr_getperthreadids_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));
diff --git a/sysdeps/nptl/setxid.h b/sysdeps/nptl/setxid.h
index 20bf66a5df..bc955e73bb 100644
--- a/sysdeps/nptl/setxid.h
+++ b/sysdeps/nptl/setxid.h
@@ -32,7 +32,8 @@
  # define INLINE_SETXID_SYSCALL(name, nr, args...) \
    ({									\
      int __result;							\
-    if (__builtin_expect (__libc_pthread_functions_init, 0))		\
+    if (__libc_pthread_functions_init					\
+	&& !nptl_current_thread_has_separate_ids ())			\
        {									\
  	struct xid_command __cmd;					\
  	__cmd.syscall_no = __NR_##name;					\
@@ -48,7 +49,8 @@
    ({									\
      extern __typeof (__nptl_setxid) __nptl_setxid __attribute__((weak));\
      int __result;							\
-    if (__glibc_unlikely (__nptl_setxid	!= NULL))			      \
+    if (__nptl_setxid != NULL						\
+	&& !nptl_current_thread_has_separate_ids ())			\
        {									\
  	struct xid_command __cmd;					\
  	__cmd.syscall_no = __NR_##name;					\
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index 2635e16f5e..03507783d3 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2144,6 +2144,8 @@ GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
  GLIBC_2.30 getdents64 F
  GLIBC_2.30 gettid F
  GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_getperthreadids_np F
  GLIBC_2.30 pthread_attr_setperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadids_np F
  GLIBC_2.30 tgkill F
  GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index 12227b9800..0e24e9a39d 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2219,7 +2219,9 @@ GLIBC_2.30 __nldbl_warnx F
  GLIBC_2.30 getdents64 F
  GLIBC_2.30 gettid F
  GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_getperthreadids_np F
  GLIBC_2.30 pthread_attr_setperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadids_np F
  GLIBC_2.30 tgkill F
  GLIBC_2.30 twalk_r F
  GLIBC_2.4 _IO_fprintf F
diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
index 6d09148e1d..d69e1acdfa 100644
--- a/sysdeps/unix/sysv/linux/arm/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
@@ -129,7 +129,9 @@ GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
  GLIBC_2.30 getdents64 F
  GLIBC_2.30 gettid F
  GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_getperthreadids_np F
  GLIBC_2.30 pthread_attr_setperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadids_np F
  GLIBC_2.30 tgkill F
  GLIBC_2.30 twalk_r F
  GLIBC_2.4 _Exit F
diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
index 5ee091972b..cf2fe8fb5e 100644
--- a/sysdeps/unix/sysv/linux/csky/libc.abilist
+++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
@@ -2088,6 +2088,8 @@ GLIBC_2.29 xprt_unregister F
  GLIBC_2.30 getdents64 F
  GLIBC_2.30 gettid F
  GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_getperthreadids_np F
  GLIBC_2.30 pthread_attr_setperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadids_np F
  GLIBC_2.30 tgkill F
  GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 844eaf539b..2ebaff4e7b 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -2040,7 +2040,9 @@ GLIBC_2.3.4 xdr_u_quad_t F
  GLIBC_2.30 getdents64 F
  GLIBC_2.30 gettid F
  GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_getperthreadids_np F
  GLIBC_2.30 pthread_attr_setperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadids_np F
  GLIBC_2.30 tgkill F
  GLIBC_2.30 twalk_r F
  GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/hppa/pthread.h b/sysdeps/unix/sysv/linux/hppa/pthread.h
index 43ffca5551..0d42953a87 100644
--- a/sysdeps/unix/sysv/linux/hppa/pthread.h
+++ b/sysdeps/unix/sysv/linux/hppa/pthread.h
@@ -407,6 +407,24 @@ int pthread_attr_getperthreadfs_np (const pthread_attr_t *__restrict __attr,
  				    int *__restrict __scope)
    __THROW __nonnull ((1, 2));
+/* Control the flag in ATTR whether the thread has its own user and
+   group IDs.  By default, when the real, effective, or saved user or
+   group ID is changed by a thread, this affects the entire process.
+   If a thread is created with this flag set to true, then changing
+   the IDs within that thread will only affect that thread, and user
+   and group ID changes in other threads (whether they have enabled
+   this flag or not) do not affect it.  If a thread has been created
+   with this flag, threads created by it will also have their own,
+   private user and group IDs.  */
+void pthread_attr_setperthreadids_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_getperthreadids_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));
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index efd8cb1685..6a84bb91af 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2206,7 +2206,9 @@ GLIBC_2.3.4 xdr_u_quad_t F
  GLIBC_2.30 getdents64 F
  GLIBC_2.30 gettid F
  GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_getperthreadids_np F
  GLIBC_2.30 pthread_attr_setperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadids_np F
  GLIBC_2.30 tgkill F
  GLIBC_2.30 twalk_r F
  GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index 7f34f4d1e6..3e5144c493 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -2072,7 +2072,9 @@ GLIBC_2.3.4 xdr_u_quad_t F
  GLIBC_2.30 getdents64 F
  GLIBC_2.30 gettid F
  GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_getperthreadids_np F
  GLIBC_2.30 pthread_attr_setperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadids_np F
  GLIBC_2.30 tgkill F
  GLIBC_2.30 twalk_r F
  GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index 34d5f6c91a..63f830cee8 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -130,7 +130,9 @@ GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
  GLIBC_2.30 getdents64 F
  GLIBC_2.30 gettid F
  GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_getperthreadids_np F
  GLIBC_2.30 pthread_attr_setperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadids_np F
  GLIBC_2.30 tgkill F
  GLIBC_2.30 twalk_r F
  GLIBC_2.4 _Exit F
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index 71882c6e23..b90e15cf69 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -2149,7 +2149,9 @@ GLIBC_2.3.4 xdr_u_quad_t F
  GLIBC_2.30 getdents64 F
  GLIBC_2.30 gettid F
  GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_getperthreadids_np F
  GLIBC_2.30 pthread_attr_setperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadids_np F
  GLIBC_2.30 tgkill F
  GLIBC_2.30 twalk_r F
  GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
index 4f605fa67a..99516e20f7 100644
--- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
@@ -2136,6 +2136,8 @@ GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
  GLIBC_2.30 getdents64 F
  GLIBC_2.30 gettid F
  GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_getperthreadids_np F
  GLIBC_2.30 pthread_attr_setperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadids_np F
  GLIBC_2.30 tgkill F
  GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index 62790b0a64..207448d9fc 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -2123,7 +2123,9 @@ GLIBC_2.3.4 xdr_u_quad_t F
  GLIBC_2.30 getdents64 F
  GLIBC_2.30 gettid F
  GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_getperthreadids_np F
  GLIBC_2.30 pthread_attr_setperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadids_np F
  GLIBC_2.30 tgkill F
  GLIBC_2.30 twalk_r F
  GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index eb2ae61601..e87edbde57 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -2121,7 +2121,9 @@ GLIBC_2.3.4 xdr_u_quad_t F
  GLIBC_2.30 getdents64 F
  GLIBC_2.30 gettid F
  GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_getperthreadids_np F
  GLIBC_2.30 pthread_attr_setperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadids_np F
  GLIBC_2.30 tgkill F
  GLIBC_2.30 twalk_r F
  GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 9cf1462270..6f68c19ffc 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -2129,7 +2129,9 @@ GLIBC_2.3.4 xdr_u_quad_t F
  GLIBC_2.30 getdents64 F
  GLIBC_2.30 gettid F
  GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_getperthreadids_np F
  GLIBC_2.30 pthread_attr_setperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadids_np F
  GLIBC_2.30 tgkill F
  GLIBC_2.30 twalk_r F
  GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index d116e7180e..35376c4083 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -2123,7 +2123,9 @@ GLIBC_2.3.4 xdr_u_quad_t F
  GLIBC_2.30 getdents64 F
  GLIBC_2.30 gettid F
  GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_getperthreadids_np F
  GLIBC_2.30 pthread_attr_setperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadids_np F
  GLIBC_2.30 tgkill F
  GLIBC_2.30 twalk_r F
  GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index 0e8d5bfcc7..957efe283c 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2177,6 +2177,8 @@ GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
  GLIBC_2.30 getdents64 F
  GLIBC_2.30 gettid F
  GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_getperthreadids_np F
  GLIBC_2.30 pthread_attr_setperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadids_np F
  GLIBC_2.30 tgkill F
  GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index 16e66c2fa4..594f3c7888 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -2179,7 +2179,9 @@ GLIBC_2.30 __nldbl_warnx F
  GLIBC_2.30 getdents64 F
  GLIBC_2.30 gettid F
  GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_getperthreadids_np F
  GLIBC_2.30 pthread_attr_setperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadids_np F
  GLIBC_2.30 tgkill F
  GLIBC_2.30 twalk_r F
  GLIBC_2.4 _IO_fprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index fff0295a63..1db7320d13 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -2212,7 +2212,9 @@ GLIBC_2.30 __nldbl_warnx F
  GLIBC_2.30 getdents64 F
  GLIBC_2.30 gettid F
  GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_getperthreadids_np F
  GLIBC_2.30 pthread_attr_setperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadids_np F
  GLIBC_2.30 tgkill F
  GLIBC_2.30 twalk_r F
  GLIBC_2.4 _IO_fprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
index 808f021335..85965392d2 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
@@ -2042,7 +2042,9 @@ GLIBC_2.30 __nldbl_warnx F
  GLIBC_2.30 getdents64 F
  GLIBC_2.30 gettid F
  GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_getperthreadids_np F
  GLIBC_2.30 pthread_attr_setperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadids_np F
  GLIBC_2.30 tgkill F
  GLIBC_2.30 twalk_r F
  GLIBC_2.4 _IO_fprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
index a894bc49be..25872a4d1b 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
@@ -2246,6 +2246,8 @@ GLIBC_2.30 __nldbl_warnx F
  GLIBC_2.30 getdents64 F
  GLIBC_2.30 gettid F
  GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_getperthreadids_np F
  GLIBC_2.30 pthread_attr_setperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadids_np F
  GLIBC_2.30 tgkill F
  GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index e220b0fd0c..36a6548695 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2106,6 +2106,8 @@ GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
  GLIBC_2.30 getdents64 F
  GLIBC_2.30 gettid F
  GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_getperthreadids_np F
  GLIBC_2.30 pthread_attr_setperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadids_np F
  GLIBC_2.30 tgkill F
  GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index 1567d2ff1d..1ab615c10d 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -2174,7 +2174,9 @@ GLIBC_2.30 __nldbl_warnx F
  GLIBC_2.30 getdents64 F
  GLIBC_2.30 gettid F
  GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_getperthreadids_np F
  GLIBC_2.30 pthread_attr_setperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadids_np F
  GLIBC_2.30 tgkill F
  GLIBC_2.30 twalk_r F
  GLIBC_2.4 _IO_fprintf F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index f9d88d588e..8a0c4ad926 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -2078,7 +2078,9 @@ GLIBC_2.30 __nldbl_warnx F
  GLIBC_2.30 getdents64 F
  GLIBC_2.30 gettid F
  GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_getperthreadids_np F
  GLIBC_2.30 pthread_attr_setperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadids_np F
  GLIBC_2.30 tgkill F
  GLIBC_2.30 twalk_r F
  GLIBC_2.4 _IO_fprintf F
diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
index affd74df4c..80df6c75ac 100644
--- a/sysdeps/unix/sysv/linux/sh/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
@@ -2044,7 +2044,9 @@ GLIBC_2.3.4 xdr_u_quad_t F
  GLIBC_2.30 getdents64 F
  GLIBC_2.30 gettid F
  GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_getperthreadids_np F
  GLIBC_2.30 pthread_attr_setperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadids_np F
  GLIBC_2.30 tgkill F
  GLIBC_2.30 twalk_r F
  GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index c12cc83bb2..6308f6055c 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -2168,7 +2168,9 @@ GLIBC_2.30 __nldbl_warnx F
  GLIBC_2.30 getdents64 F
  GLIBC_2.30 gettid F
  GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_getperthreadids_np F
  GLIBC_2.30 pthread_attr_setperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadids_np F
  GLIBC_2.30 tgkill F
  GLIBC_2.30 twalk_r F
  GLIBC_2.4 _IO_fprintf F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index 37c9dff44c..6b73de3257 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -2095,7 +2095,9 @@ GLIBC_2.3.4 xdr_u_quad_t F
  GLIBC_2.30 getdents64 F
  GLIBC_2.30 gettid F
  GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_getperthreadids_np F
  GLIBC_2.30 pthread_attr_setperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadids_np F
  GLIBC_2.30 tgkill F
  GLIBC_2.30 twalk_r F
  GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index 71b7cc4ff9..6f24765088 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -2053,7 +2053,9 @@ GLIBC_2.3.4 xdr_u_quad_t F
  GLIBC_2.30 getdents64 F
  GLIBC_2.30 gettid F
  GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_getperthreadids_np F
  GLIBC_2.30 pthread_attr_setperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadids_np F
  GLIBC_2.30 tgkill F
  GLIBC_2.30 twalk_r F
  GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 573fc2e01c..bc2ad25346 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2152,6 +2152,8 @@ GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
  GLIBC_2.30 getdents64 F
  GLIBC_2.30 gettid F
  GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_getperthreadids_np F
  GLIBC_2.30 pthread_attr_setperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadids_np F
  GLIBC_2.30 tgkill F
  GLIBC_2.30 twalk_r F



--
Cheers,
Carlos.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]