diff mbox series

Linux: Implement per-thread user and group IDs

Message ID 874l49mtgy.fsf@oldenburg2.str.redhat.com
State New
Headers show
Series Linux: Implement per-thread user and group IDs | expand

Commit Message

Florian Weimer June 28, 2019, 6:54 p.m. UTC
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.

(Build-tested on all architectures with build-many-glibcs.py.
Run-time-tested on x86-64, by running the new xtest as root.  I still
need to implement the inheritance test Carlos requested.  Due to the
similarities, I plan to write one separate test for both kinds of
properties.)

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.

Comments

Florian Weimer June 28, 2019, 7:06 p.m. UTC | #1
* Florian Weimer:

> 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.
>
> (Build-tested on all architectures with build-many-glibcs.py.
> Run-time-tested on x86-64, by running the new xtest as root.  I still
> need to implement the inheritance test Carlos requested.  Due to the
> similarities, I plan to write one separate test for both kinds of
> properties.)

I should have added that the core implementation is unchanged from the
one I posted in December 2018, only the interfaces were aligned with the
perthreadfs patch.

Thanks,
Florian
Carlos O'Donell June 28, 2019, 11:26 p.m. UTC | #2
On 6/28/19 2:54 PM, Florian Weimer wrote:
> 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.

OK.

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

OK.

> (Build-tested on all architectures with build-many-glibcs.py.
> Run-time-tested on x86-64, by running the new xtest as root.  I still
> need to implement the inheritance test Carlos requested.  Due to the
> similarities, I plan to write one separate test for both kinds of
> properties.)

Right, the test for per-process is missing, we have only test for
per-thread inheritance, and null attr default, but not per-process
inheritance. I assume you'll fix it like you are going to fix the
other-thread fs test case?

The other thing I was thinking about is: Should a thread created
with per-thread uid/gid block SIGSETXID permanently from startup?
This way there can be no accident that the function sighandler_setxid
is called? If you don't like that then at least some belt-and-suspenders
code in sighandler_setxid to assert that a thread per-thread uid/gid
will not have this handler ever called? Even better... do both! :-)

> 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..83bd54b5e8 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -45,6 +45,12 @@ Major new features:
>    pthread_attr_setperthreadfs_np and pthread_attr_getperthreadfs_np have
>    been added in support of that.
>  
> +* Likewise, on Linux, threads can now be created in such a way that they

s/Likewise, on/On/g

> +  retain per-thread user and group IDs.  (This has always been the way how
> +  the Linux kernel implements threads.)  The functions

Drop the parenthetical.

> +  pthread_attr_setperthreadids_np and pthread_attr_getperthreadids_np have
> +  been added in support of that.
> +
>  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 60c15a8d15..c3003c1f94 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_setperthreadid_np (pthread_attr_t *@var{attr}, int @var{scope})

Typo. s/pthread_attr_setperthreadid_np/pthread_attr_setperthreadids_np/g

> +@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)

OK.

> +
> +@item
> +@cindex per-thread group ID
> +@cindex thread-specific group ID
> +real, effective and saved group ID (as returned by the
> +@code{getresgid} function)

OK.

> +
> +@item
> +supplementary group list (as returned by the @code{getgroups}
> +function)

OK.

> +@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}.

OK.

> +
> +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.
> +

OK.

> +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.

OK.

> +
> +This function is a GNU extension and specific to Linux.
> +@end deftypefun

OK.

> +
> +@deftypefun int pthread_attr_getperthreadid_np (pthread_attr_t *restrict @var{attr}, int *restrict @var{scope})

Typo. s/pthread_attr_getperthreadid_np/pthread_attr_getperthreadids_np/g

> +@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

OK.

> +
>  @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

OK.

> +
>  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. Nice refactor.

> +
>  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))

OK.

>  	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))

OK.

>  	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))

OK.

>  	    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))

OK.

>  	    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))

OK.

>  	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))

OK.

>  	continue;
>  
>        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.

OK.

> +   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;

OK. Thanks for fixing this up to match the per-thread-fs changes!

> +  return 0;
> +}
> 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..c3d04f5156
> --- /dev/null
> +++ b/nptl/tst-pthread-perthreadids.c
> @@ -0,0 +1,510 @@
> +/* Test per-thread user and group IDs.
> +   Copyright (C) 2019 Free Software Foundation, Inc.

OK.

> +   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 IDs.  */
> +static bool
> +perthread_flag (pthread_t thr)
> +{
> +  pthread_attr_t attr;
> +  int ret = pthread_getattr_np (thr, &attr);

OK.

> +  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;

OK.

> +}
> +
> +/* Which set*id function to call.  */
> +enum operation
> +{
> + OP_NONE,
> + OP_SETUID,
> + OP_SETEUID,
> + OP_SETREUID,
> + OP_SETRESUID,
> + OP_SETGID,
> + OP_SETEGID,
> + OP_SETREGID,
> + OP_SETRESGID,
> + OP_SETGROUPS,
> +};

OK.

> +
> +/* Convert the operation to a descriptive string.  */
> +static const char *
> +operation_string (enum operation op)
> +{
> +  switch (op)

OK, implementation of *_SETXID_SYSCALL is:

sysdeps/unix/sysv/linux/setresgid.c
sysdeps/unix/sysv/linux/setgroups.c
sysdeps/unix/sysv/linux/setuid.c
sysdeps/unix/sysv/linux/setresuid.c
sysdeps/unix/sysv/linux/setreuid.c
sysdeps/unix/sysv/linux/setegid.c
sysdeps/unix/sysv/linux/setgid.c
sysdeps/unix/sysv/linux/setregid.c
sysdeps/unix/sysv/linux/seteuid.c

Confirmed all covered.

> +    {
> +    case OP_NONE:
> +      return "<none>";

OK.

> +    case OP_SETUID:
> +      return "OP_SETUID";

OK.

> +    case OP_SETEUID:
> +      return "OP_SETEUID";

OK.

> +    case OP_SETREUID:
> +      return "OP_SETREUID";

OK.

> +    case OP_SETRESUID:
> +      return "OP_SETRESUID";

OK.

> +    case OP_SETGID:
> +      return "OP_SETGID";

OK.

> +    case OP_SETEGID:
> +      return "OP_SETEGID";

OK.

> +    case OP_SETREGID:
> +      return "OP_SETREGID";

OK.

> +    case OP_SETRESGID:
> +      return "OP_SETRESGID";

OK.

> +    case OP_SETGROUPS:
> +      return "OP_SETGROUPS";

OK.

> +    }
> +
> +  FAIL_EXIT1 ("invalid operation: %d", (int) op);
> +}

OK.

> +
> +/* 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];
> +};

OK.

> +
> +static const struct test_case test_cases[] =
> +  {
> +   { OP_NONE, { 0, }, { 0, 0, 0}, {0, 0, 0} },
> +
> +   { OP_SETUID, { 1, }, { 1, 1, 1 }, { 0, } },
> +   { OP_SETEUID, { 2, }, { 0, 2, }, { 0, } },
> +   { OP_SETREUID, { 3, 4, }, { 3, 4, 4 }, { 0, } },
> +   { OP_SETRESUID, { 3, 4, 5 }, { 3, 4, 5 }, { 0, } },
> +
> +   { OP_SETGID, { 6, }, { 0, }, { 6, 6, 6 } },
> +   { OP_SETEGID, { 7, }, { 0, }, { 0, 7, } },
> +   { OP_SETREGID, { 8, 9, }, { 0, }, { 8, 9, 9 } },
> +   { OP_SETRESGID, { 10, 11, 12 }, { 0, }, { 10, 11, 12 } },
> +
> +   { OP_SETGROUPS, { -1, }, { 0, }, { 0, } },
> +   { OP_SETGROUPS, { 13, -1 }, { 0, }, { 0, } },
> +   { OP_SETGROUPS, { 13, 14, -1 }, { 0, }, { 0, } },
> +   { OP_SETGROUPS, { 13, 14, 15 }, { 0, }, { 0, } },
> +
> +   /* Final round of checks.  */
> +   { OP_NONE, { 0, }, { 0, }, {0, } },

I find it more difficult to read and edit when elements of the
struct are elided. Could you please fill them all in?

> +  };
> +
> +/* 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;
> +}

OK.

> +
> +/* 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;

OK.

> +
> +    case OP_SETGROUPS:
> +      {
> +        gid_t groups[] = { test->args[0], test->args[1], test->args[2] };
> +        ret = setgroups (supplemetary_count (test), groups);

OK.

> +      }
> +    }
> +
> +  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;

OK.

> +
> +/* 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;
> +};

OK.

> +
> +/* 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;
> +}

OK.

> +
> +/* 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.  */
> +      pthread_t thr = xpthread_create (NULL, threadfunc, arg);
> +      TEST_COMPARE (perthread_flag (thr), arg->perthread);
> +      return xpthread_join (thr);
> +    }
> +
> +  const struct test_case *expected = &test_cases[0];
> +  gid_t expected_supplementary[3] = { -1, -1, -1 };
> +  int expected_supplementary_count = 0;
> +
> +  xpthread_barrier_wait (&barrier);

All of these barrier waits should have notes about what they are
synchronizing with, and which point if possible, thta way a future
editor can know what they are wiating for. Perhaps numbering them
with comments might help here.

I realized late that I had the same problem while reviewing the
other test case and it only occurred to me that we should just
comment the phases.

> +
> +  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];
> +            }
> +        }
> +
> +      xpthread_barrier_wait (&barrier);
> +
> +      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);
> +
> +      xpthread_barrier_wait (&barrier);
> +    }
> +
> +  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 ()));
> +
> +  /* Main thread and another shared thread are extras.  */
> +  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_thread
> +    = xpthread_create (NULL, threadfunc,
> +                       create_thread_argument ((struct thread_argument) { }));
> +  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);
> +      TEST_VERIFY (perthread_flag (perthread_threads[i]));
> +    }
> +  /* Use thread index zero for no-op checking.  */
> +  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.  */

^^^ This comment needs clarification. Is it trying to rationalize why it's
array_length (test_cases) + 2?

> +  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.  */

What is 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.  */
> +    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));
> +      }
> +
> +  /* Use thread index zero for no-op 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);
> +  }
> +

This needs a block comment explaining what it is doing and why.

> +  for (int indirect = 0; indirect < 2; ++indirect)
> +    {
> +      check_perthread (indirect);
> +
> +      for (int broadcast_from_main = 0; broadcast_from_main < 2;
> +           ++broadcast_from_main)
> +        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 \

OK.

>    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.

OK.

> +   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");
> +}

OK.

> 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");
> +}

OK.

> 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 *);

OK.

>  
>  /* 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

OK.

>  
>  /* 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 37073e3ce7..9ea312cf41 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.  */
> +int pthread_attr_setperthreadids_np (pthread_attr_t *__attr, int __scope)
> +  __THROW __nonnull ((1));

OK.

> +
> +/* Set *SCOPE to PTHREAD_PER_PROCESS_NP or PTHREAD_PER_THREAD_NP,
> +   depending on the state of *ATTR.  */
> +int pthread_attr_getperthreadids_np (const pthread_attr_t *__restrict __attr,
> +				     int *__restrict __scope)

OK.

> +  __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 ())			\

OK.

>        {									\
>  	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 ())			\

OK.

>        {									\
>  	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 b80c84c0b7..d8ff0c58f3 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));

OK.

> +
> +/* Set *SCOPE to PTHREAD_PER_PROCESS_NP or PTHREAD_PER_THREAD_NP,
> +   depending on the state of *ATTR.  */

Wrong comment.

> +int pthread_attr_getperthreadids_np (const pthread_attr_t *__restrict __attr,
> +				     int *__restrict __scope)
> +  __THROW __nonnull ((1, 2));
> +

OK.

>  /* 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
> 

OK. Just version updates.
Florian Weimer June 30, 2019, 9:36 a.m. UTC | #3
* Carlos O'Donell:

>> (Build-tested on all architectures with build-many-glibcs.py.
>> Run-time-tested on x86-64, by running the new xtest as root.  I still
>> need to implement the inheritance test Carlos requested.  Due to the
>> similarities, I plan to write one separate test for both kinds of
>> properties.)
>
> Right, the test for per-process is missing, we have only test for
> per-thread inheritance, and null attr default, but not per-process
> inheritance. I assume you'll fix it like you are going to fix the
> other-thread fs test case?

You've already seen the new separate test.

> The other thing I was thinking about is: Should a thread created
> with per-thread uid/gid block SIGSETXID permanently from startup?
> This way there can be no accident that the function sighandler_setxid
> is called? If you don't like that then at least some belt-and-suspenders
> code in sighandler_setxid to assert that a thread per-thread uid/gid
> will not have this handler ever called? Even better... do both! :-)

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.

>> diff --git a/NEWS b/NEWS
>> index e63b69b930..83bd54b5e8 100644
>> --- a/NEWS
>> +++ b/NEWS
>> @@ -45,6 +45,12 @@ Major new features:
>>    pthread_attr_setperthreadfs_np and pthread_attr_getperthreadfs_np have
>>    been added in support of that.
>>  
>> +* Likewise, on Linux, threads can now be created in such a way that they
>
> s/Likewise, on/On/g

There's a preceding NEWS entry with similar wording, so I thought the
“Likewise” would make sense here.

>
>> +  retain per-thread user and group IDs.  (This has always been the way how
>> +  the Linux kernel implements threads.)  The functions
>
> Drop the parenthetical.

I will do that.

>> +
>> +@deftypefun int pthread_attr_getperthreadid_np (pthread_attr_t
> *restrict @var{attr}, int *restrict @var{scope})
>
> Typo. s/pthread_attr_getperthreadid_np/pthread_attr_getperthreadids_np/g

Will fix.

>> +static const struct test_case test_cases[] =
>> +  {
>> +   { OP_NONE, { 0, }, { 0, 0, 0}, {0, 0, 0} },
>> +
>> +   { OP_SETUID, { 1, }, { 1, 1, 1 }, { 0, } },
>> +   { OP_SETEUID, { 2, }, { 0, 2, }, { 0, } },
>> +   { OP_SETREUID, { 3, 4, }, { 3, 4, 4 }, { 0, } },
>> +   { OP_SETRESUID, { 3, 4, 5 }, { 3, 4, 5 }, { 0, } },
>> +
>> +   { OP_SETGID, { 6, }, { 0, }, { 6, 6, 6 } },
>> +   { OP_SETEGID, { 7, }, { 0, }, { 0, 7, } },
>> +   { OP_SETREGID, { 8, 9, }, { 0, }, { 8, 9, 9 } },
>> +   { OP_SETRESGID, { 10, 11, 12 }, { 0, }, { 10, 11, 12 } },
>> +
>> +   { OP_SETGROUPS, { -1, }, { 0, }, { 0, } },
>> +   { OP_SETGROUPS, { 13, -1 }, { 0, }, { 0, } },
>> +   { OP_SETGROUPS, { 13, 14, -1 }, { 0, }, { 0, } },
>> +   { OP_SETGROUPS, { 13, 14, 15 }, { 0, }, { 0, } },
>> +
>> +   /* Final round of checks.  */
>> +   { OP_NONE, { 0, }, { 0, }, {0, } },
>
> I find it more difficult to read and edit when elements of the
> struct are elided. Could you please fill them all in?

I can do this, although it will add quite a bit of cruft.

>> +/* 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.  */
>> +      pthread_t thr = xpthread_create (NULL, threadfunc, arg);
>> +      TEST_COMPARE (perthread_flag (thr), arg->perthread);
>> +      return xpthread_join (thr);
>> +    }
>> +
>> +  const struct test_case *expected = &test_cases[0];
>> +  gid_t expected_supplementary[3] = { -1, -1, -1 };
>> +  int expected_supplementary_count = 0;
>> +
>> +  xpthread_barrier_wait (&barrier);
>
> All of these barrier waits should have notes about what they are
> synchronizing with, and which point if possible, thta way a future
> editor can know what they are wiating for. Perhaps numbering them
> with comments might help here.
>
> I realized late that I had the same problem while reviewing the
> other test case and it only occurred to me that we should just
> comment the phases.

Hmm.  I'll see what I can do about this.

>> +  /* Main thread and two other shared threads are extras.  One
>> +     per-thread test is skipped, so there are two extra threads.  */
>
> ^^^ This comment needs clarification. Is it trying to rationalize why it's
> array_length (test_cases) + 2?

Yes, exactly.  I will expand the comment.

>> +  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.  */
>
> What is no-op checking?

It uses OP_NONE.  In general, this is when the thread observes the UID
change from another thread and verifies that.  I will try to make this
more explicit.

> This needs a block comment explaining what it is doing and why.

What about this?

  /* 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 with per-process
         scope).  */

>> +/* Set *SCOPE to PTHREAD_PER_PROCESS_NP or PTHREAD_PER_THREAD_NP,
>> +   depending on the state of *ATTR.  */
>
> Wrong comment.
>
>> +int pthread_attr_getperthreadids_np (const pthread_attr_t *__restrict __attr,
>> +				     int *__restrict __scope)
>> +  __THROW __nonnull ((1, 2));
>> +

Sorry, wrong comment in what way?
Carlos O'Donell July 1, 2019, 3:31 a.m. UTC | #4
On 6/30/19 5:36 AM, Florian Weimer wrote:
> * Carlos O'Donell:
> 
>>> (Build-tested on all architectures with build-many-glibcs.py.
>>> Run-time-tested on x86-64, by running the new xtest as root.  I still
>>> need to implement the inheritance test Carlos requested.  Due to the
>>> similarities, I plan to write one separate test for both kinds of
>>> properties.)
>>
>> Right, the test for per-process is missing, we have only test for
>> per-thread inheritance, and null attr default, but not per-process
>> inheritance. I assume you'll fix it like you are going to fix the
>> other-thread fs test case?
> 
> You've already seen the new separate test.

Yes I have. I've been reviewing these somewhat in parallel.

>> The other thing I was thinking about is: Should a thread created
>> with per-thread uid/gid block SIGSETXID permanently from startup?
>> This way there can be no accident that the function sighandler_setxid
>> is called? If you don't like that then at least some belt-and-suspenders
>> code in sighandler_setxid to assert that a thread per-thread uid/gid
>> will not have this handler ever called? Even better... do both! :-)
> 
> 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.

It seems dangerous to me to leave it unblocked or not to check the thread's
attribute flag to exclude it from ever participating.

Given that this is an internal implementation detail we could change it later.

>>> diff --git a/NEWS b/NEWS
>>> index e63b69b930..83bd54b5e8 100644
>>> --- a/NEWS
>>> +++ b/NEWS
>>> @@ -45,6 +45,12 @@ Major new features:
>>>    pthread_attr_setperthreadfs_np and pthread_attr_getperthreadfs_np have
>>>    been added in support of that.
>>>  
>>> +* Likewise, on Linux, threads can now be created in such a way that they
>>
>> s/Likewise, on/On/g
> 
> There's a preceding NEWS entry with similar wording, so I thought the
> “Likewise” would make sense here.

Each news entry should stand on its own so it can be quoted, or so it makes
sense when reading just one entry.

>>
>>> +  retain per-thread user and group IDs.  (This has always been the way how
>>> +  the Linux kernel implements threads.)  The functions
>>
>> Drop the parenthetical.
> 
> I will do that.
> 
>>> +
>>> +@deftypefun int pthread_attr_getperthreadid_np (pthread_attr_t
>> *restrict @var{attr}, int *restrict @var{scope})
>>
>> Typo. s/pthread_attr_getperthreadid_np/pthread_attr_getperthreadids_np/g
> 
> Will fix.
> 
>>> +static const struct test_case test_cases[] =
>>> +  {
>>> +   { OP_NONE, { 0, }, { 0, 0, 0}, {0, 0, 0} },
>>> +
>>> +   { OP_SETUID, { 1, }, { 1, 1, 1 }, { 0, } },
>>> +   { OP_SETEUID, { 2, }, { 0, 2, }, { 0, } },
>>> +   { OP_SETREUID, { 3, 4, }, { 3, 4, 4 }, { 0, } },
>>> +   { OP_SETRESUID, { 3, 4, 5 }, { 3, 4, 5 }, { 0, } },
>>> +
>>> +   { OP_SETGID, { 6, }, { 0, }, { 6, 6, 6 } },
>>> +   { OP_SETEGID, { 7, }, { 0, }, { 0, 7, } },
>>> +   { OP_SETREGID, { 8, 9, }, { 0, }, { 8, 9, 9 } },
>>> +   { OP_SETRESGID, { 10, 11, 12 }, { 0, }, { 10, 11, 12 } },
>>> +
>>> +   { OP_SETGROUPS, { -1, }, { 0, }, { 0, } },
>>> +   { OP_SETGROUPS, { 13, -1 }, { 0, }, { 0, } },
>>> +   { OP_SETGROUPS, { 13, 14, -1 }, { 0, }, { 0, } },
>>> +   { OP_SETGROUPS, { 13, 14, 15 }, { 0, }, { 0, } },
>>> +
>>> +   /* Final round of checks.  */
>>> +   { OP_NONE, { 0, }, { 0, }, {0, } },
>>
>> I find it more difficult to read and edit when elements of the
>> struct are elided. Could you please fill them all in?
> 
> I can do this, although it will add quite a bit of cruft.
> 

I disagree that this is cruft.

As they are now, the columns don't line up and reading the test
input at a glance is difficult.

>>> +/* 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.  */
>>> +      pthread_t thr = xpthread_create (NULL, threadfunc, arg);
>>> +      TEST_COMPARE (perthread_flag (thr), arg->perthread);
>>> +      return xpthread_join (thr);
>>> +    }
>>> +
>>> +  const struct test_case *expected = &test_cases[0];
>>> +  gid_t expected_supplementary[3] = { -1, -1, -1 };
>>> +  int expected_supplementary_count = 0;
>>> +
>>> +  xpthread_barrier_wait (&barrier);
>>
>> All of these barrier waits should have notes about what they are
>> synchronizing with, and which point if possible, thta way a future
>> editor can know what they are wiating for. Perhaps numbering them
>> with comments might help here.
>>
>> I realized late that I had the same problem while reviewing the
>> other test case and it only occurred to me that we should just
>> comment the phases.
> 
> Hmm.  I'll see what I can do about this.
> 
>>> +  /* Main thread and two other shared threads are extras.  One
>>> +     per-thread test is skipped, so there are two extra threads.  */
>>
>> ^^^ This comment needs clarification. Is it trying to rationalize why it's
>> array_length (test_cases) + 2?
> 
> Yes, exactly.  I will expand the comment.
> 
>>> +  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.  */
>>
>> What is no-op checking?
> 
> It uses OP_NONE.  In general, this is when the thread observes the UID
> change from another thread and verifies that.  I will try to make this
> more explicit.

Thanks, this wasn't clear to me when reading through the test the first
time.

> 
>> This needs a block comment explaining what it is doing and why.
> 
> What about this?
> 
>   /* 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.  */

OK.

> 
>>> +  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.  */

OK.

> 
>>> +      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 with per-process
>          scope).  */

OK.

> 
>>> +/* Set *SCOPE to PTHREAD_PER_PROCESS_NP or PTHREAD_PER_THREAD_NP,
>>> +   depending on the state of *ATTR.  */
>>
>> Wrong comment.
>>
>>> +int pthread_attr_getperthreadids_np (const pthread_attr_t *__restrict __attr,
>>> +				     int *__restrict __scope)
>>> +  __THROW __nonnull ((1, 2));
>>> +
> 
> Sorry, wrong comment in what way?

It says "Set ..." but this is the getter.

>
Florian Weimer July 1, 2019, 4:49 a.m. UTC | #5
* 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.

>>>> +* Likewise, on Linux, threads can now be created in such a way that they
>>>
>>> s/Likewise, on/On/g
>> 
>> There's a preceding NEWS entry with similar wording, so I thought the
>> “Likewise” would make sense here.
>
> Each news entry should stand on its own so it can be quoted, or so it makes
> sense when reading just one entry.

Okay, I'll change this.

>>>> +/* Set *SCOPE to PTHREAD_PER_PROCESS_NP or PTHREAD_PER_THREAD_NP,
>>>> +   depending on the state of *ATTR.  */
>>>
>>> Wrong comment.
>>>
>>>> +int pthread_attr_getperthreadids_np (const pthread_attr_t
>>>> *__restrict __attr,
>>>> +				     int *__restrict __scope)
>>>> +  __THROW __nonnull ((1, 2));
>>>> +
>> 
>> Sorry, wrong comment in what way?
>
> It says "Set ..." but this is the getter.

*SCOPE is an out parameter, so “Set” is correct?  I can use “Copy … to
 …” instead if that's clearer.
Carlos O'Donell July 1, 2019, 4:59 a.m. UTC | #6
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.

>>>>> +* Likewise, on Linux, threads can now be created in such a way that they
>>>>
>>>> s/Likewise, on/On/g
>>>
>>> There's a preceding NEWS entry with similar wording, so I thought the
>>> “Likewise” would make sense here.
>>
>> Each news entry should stand on its own so it can be quoted, or so it makes
>> sense when reading just one entry.
> 
> Okay, I'll change this.
> 
>>>>> +/* Set *SCOPE to PTHREAD_PER_PROCESS_NP or PTHREAD_PER_THREAD_NP,
>>>>> +   depending on the state of *ATTR.  */
>>>>
>>>> Wrong comment.
>>>>
>>>>> +int pthread_attr_getperthreadids_np (const pthread_attr_t
>>>>> *__restrict __attr,
>>>>> +				     int *__restrict __scope)
>>>>> +  __THROW __nonnull ((1, 2));
>>>>> +
>>>
>>> Sorry, wrong comment in what way?
>>
>> It says "Set ..." but this is the getter.
> 
> *SCOPE is an out parameter, so “Set” is correct?  I can use “Copy … to
>  …” instead if that's clearer.

Ah, I see what you were intending.

With setter/getter APIs it's wise to use the language of "set" and "get"
relative to the object being manipulated.

e.g.

/* Get the thread scope from *ATTR and store in *SCOPE.  */

vs.

/* Set the thread scope in *ATTR from the value *SCOPE.  */
Florian Weimer July 1, 2019, 4:35 p.m. UTC | #7
* 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.

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.
+
 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.
 
 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})
+@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})
+@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
 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;
   }
   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);
+}
+
 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 ());
 
   /* 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;
 
       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;
+}
+
 #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;
+}
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;
+    }
+}
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).  */
+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
+
+/* 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);
+
+  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);
+
+      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);
+    }
+
+  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)
 
 
 /* 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.  */
+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
Carlos O'Donell July 4, 2019, 4:06 a.m. UTC | #8
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
>
Rich Felker July 4, 2019, 9:38 p.m. UTC | #9
On Fri, Jun 28, 2019 at 08:54:05PM +0200, Florian Weimer wrote:
> 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.

From a security standpoint, this is a really bad idea. Given a set of
tasks sharing virtual address space, the individual privilege of each
is effectively the union of the privileges of them all, simply because
each has the capacity to undermine the execution integrity of all the
others.

For fsuid/fsgid, we already have per-thread behavior, and it's
somewhat reasonable because there's an understanding that this is
*not* restricting the privilege of the thread, just performing fs
access "as if" by another user/group (you always have the privilege to
revert fsuid/fsgid changes anyway). The useful part of the new
functionality your patch adds seems to just duplicate this, and the
remainder of the new functionality all seems actively dangerous,
creating a false impression that you can make isolated security
contexts as threads within a process.

Rich
Florian Weimer July 8, 2019, 8:26 a.m. UTC | #10
* Rich Felker:

> On Fri, Jun 28, 2019 at 08:54:05PM +0200, Florian Weimer wrote:
>> 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.
>
> From a security standpoint, this is a really bad idea. Given a set of
> tasks sharing virtual address space, the individual privilege of each
> is effectively the union of the privileges of them all, simply because
> each has the capacity to undermine the execution integrity of all the
> others.

If a task has the capability to call setresuid, it can already achieve
the same thing by calling the system call directly.  All system calls
operate on a per-task credential structure, not a process-wide one.

Even if tasks with different credentials share the same address space,
this does not make those credentials equivalent.  It is still necessary
to affect execution in such a way that one task performs an action on
behalf of another task which has different credentials.  Whether this is
more difficult or easier if a new process is spawned for each different
credential depends on many factors.

> For fsuid/fsgid, we already have per-thread behavior, and it's
> somewhat reasonable because there's an understanding that this is
> *not* restricting the privilege of the thread, just performing fs
> access "as if" by another user/group (you always have the privilege to
> revert fsuid/fsgid changes anyway). The useful part of the new
> functionality your patch adds seems to just duplicate this, and the
> remainder of the new functionality all seems actively dangerous,
> creating a false impression that you can make isolated security
> contexts as threads within a process.

setfsuid and setfsgid do not allow any error checking, so you have to
parse /proc to see if the kernel gave you the requested values.
Originally, they were introduced because at the time, the kernel would
allow sending signals to processes whose effective user ID was the same
as the current process.  That is obviously bad if a file server changes
effective IDs to get permission checking as that user from the kernel
because it allows the user to terminate the server process.  Today,
effective IDs are no longer used for signal permission checks, and
fsuid/fsgid is obsolete.

And on most systems, fsuid/fsgid alone allows full escalation to the
target user anyway, via dotfiles and similar mechanisms.

Thanks,
Florian
Florian Weimer July 8, 2019, 10:42 a.m. UTC | #11
* Carlos O'Donell:

> 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.

Do you mean like this?

/* 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).  */

Thanks,
Florian
Rich Felker July 8, 2019, 3:24 p.m. UTC | #12
On Mon, Jul 08, 2019 at 10:26:04AM +0200, Florian Weimer wrote:
> * Rich Felker:
> 
> > On Fri, Jun 28, 2019 at 08:54:05PM +0200, Florian Weimer wrote:
> >> 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.
> >
> > From a security standpoint, this is a really bad idea. Given a set of
> > tasks sharing virtual address space, the individual privilege of each
> > is effectively the union of the privileges of them all, simply because
> > each has the capacity to undermine the execution integrity of all the
> > others.
> 
> If a task has the capability to call setresuid, it can already achieve
> the same thing by calling the system call directly.  All system calls
> operate on a per-task credential structure, not a process-wide one.

Right. The questions are (1) whether such usage should be treated as
officially supported, as opposed to a hack producing invalid state,
and (2) whether the standard set*id functions should preserve such a
state rather than clearing it.

I'm strongly of the opinion that it's reasonable for application-level
code to assume that, after a successful setuid(getuid()) or similar
"privilege dropping" operation returns, privilges been fully dropped
and are not recoverable in the event that someone gains
(inaadvertently or intentionally) control of execution. If obscure
library code has left around threads marked not to be affected by
set*id(), this invariant is broken. For example, some awful PAM module
might make it so that ssh session processes end up keeping around root
privileges.

> Even if tasks with different credentials share the same address space,
> this does not make those credentials equivalent.  It is still necessary
> to affect execution in such a way that one task performs an action on
> behalf of another task which has different credentials.  Whether this is

Generally the whole point of "dropping suid" or "switching from root
to a user on whose behalf you're acting" is that you *assume* the user
can achieve code execution. Sometimes this is by design; other times
it's just a matter of the code being sufficiently complex that trying
to preclude it would be impractical.

> > For fsuid/fsgid, we already have per-thread behavior, and it's
> > somewhat reasonable because there's an understanding that this is
> > *not* restricting the privilege of the thread, just performing fs
> > access "as if" by another user/group (you always have the privilege to
> > revert fsuid/fsgid changes anyway). The useful part of the new
> > functionality your patch adds seems to just duplicate this, and the
> > remainder of the new functionality all seems actively dangerous,
> > creating a false impression that you can make isolated security
> > contexts as threads within a process.
> 
> setfsuid and setfsgid do not allow any error checking, so you have to
> parse /proc to see if the kernel gave you the requested values.

It seems like you can just call them a second time to determine
whether they succeeded, no? I agree this is an awkward interface
problem though and I wasn't aware of it.

> Originally, they were introduced because at the time, the kernel would
> allow sending signals to processes whose effective user ID was the same
> as the current process.  That is obviously bad if a file server changes
> effective IDs to get permission checking as that user from the kernel
> because it allows the user to terminate the server process.  Today,
> effective IDs are no longer used for signal permission checks, and
> fsuid/fsgid is obsolete.
> 
> And on most systems, fsuid/fsgid alone allows full escalation to the
> target user anyway, via dotfiles and similar mechanisms.

As I just said, the fs[ug]id operations are not privilege-restricting.
Rather they're a mechanism for still-fully-privileged code to perform
operations "as if" by another user/group. My understanding of what you
want to add is that it has effectively the same (lack of, in a
multithreaded setting) security properties as fs[ug]id, but with a
layer of added confusion and unsafety over what properties it
provides.

Rich
Carlos O'Donell July 8, 2019, 4:01 p.m. UTC | #13
On 7/8/19 6:42 AM, Florian Weimer wrote:
> * Carlos O'Donell:
> 
>> 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.
> 
> Do you mean like this?
> 
> /* 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).  */

Perfect.
Florian Weimer July 9, 2019, 9:48 a.m. UTC | #14
* Rich Felker:

>> If a task has the capability to call setresuid, it can already achieve
>> the same thing by calling the system call directly.  All system calls
>> operate on a per-task credential structure, not a process-wide one.
>
> Right. The questions are (1) whether such usage should be treated as
> officially supported, as opposed to a hack producing invalid state,

Major file servers use this today, so it's supported as far as glibc is
concerned.  Probably the kernel considers it supported, too.

> and (2) whether the standard set*id functions should preserve such a
> state rather than clearing it.

If the process in a mixed state and set*id is called from a thread that
doesn't have CAP_SYS_ADMIN and needs to change some IDs, and then
encounters IDs that it cannot change, it's not safe to keep the process
going because we've just run a partial set*id, with no way to
communicate that to the caller.  The glibc implementation intends to
abort the process in this case.

> I'm strongly of the opinion that it's reasonable for application-level
> code to assume that, after a successful setuid(getuid()) or similar
> "privilege dropping" operation returns, privilges been fully dropped
> and are not recoverable in the event that someone gains
> (inaadvertently or intentionally) control of execution. If obscure
> library code has left around threads marked not to be affected by
> set*id(), this invariant is broken. For example, some awful PAM module
> might make it so that ssh session processes end up keeping around root
> privileges.

But setuid (getuid ()) does not fully drop privileges even without this
change.  If there are open file descriptors, it is not verified that the
new IDs are still able to re-create these descriptors with the same
semantics.  Similarly, existing file mappings are not checked, either.
This means that a library already needs to be aware of the overall state
of the process if it wants to safely drop privileges.

>> Even if tasks with different credentials share the same address space,
>> this does not make those credentials equivalent.  It is still necessary
>> to affect execution in such a way that one task performs an action on
>> behalf of another task which has different credentials.  Whether this is
>
> Generally the whole point of "dropping suid" or "switching from root
> to a user on whose behalf you're acting" is that you *assume* the user
> can achieve code execution. Sometimes this is by design; other times
> it's just a matter of the code being sufficiently complex that trying
> to preclude it would be impractical.

I think there are two different scenarios:

* Dropping privileges to run user code.  For doing this to be safe in a
  multi-threaded process, it's necessary to give up all privileges and
  then call fork, set*id, and execve (with all the magic that is needed
  to disassociate the new subprocess: closing file descriptors, terminal
  and session management, etc.)  All other threads are gone at the
  set*id call, so there's no functionality change for this scenario.

* Temporarily switching privileges, to perform a set of carefully
  limited actions on behalf of a user with their permissions, instead of
  reimplementing the permission checks in userspace (which generally
  leads to more security bugs than it avoids).  This typically retains
  the privileged user ID in some fashion (via setresuid), so that the
  code can switch back, so there's no fundamental change, either.  (In
  fact, the main risk is doing this stuff in a multi-threaded process
  without libc support, so that other threads access files with the
  wrong permissions.)

>> > For fsuid/fsgid, we already have per-thread behavior, and it's
>> > somewhat reasonable because there's an understanding that this is
>> > *not* restricting the privilege of the thread, just performing fs
>> > access "as if" by another user/group (you always have the privilege to
>> > revert fsuid/fsgid changes anyway). The useful part of the new
>> > functionality your patch adds seems to just duplicate this, and the
>> > remainder of the new functionality all seems actively dangerous,
>> > creating a false impression that you can make isolated security
>> > contexts as threads within a process.
>> 
>> setfsuid and setfsgid do not allow any error checking, so you have to
>> parse /proc to see if the kernel gave you the requested values.
>
> It seems like you can just call them a second time to determine
> whether they succeeded, no? I agree this is an awkward interface
> problem though and I wasn't aware of it.

If the return value of the second call is not the expected one, then
what?  You still don't know whether the second call has succeeded.

> As I just said, the fs[ug]id operations are not privilege-restricting.
> Rather they're a mechanism for still-fully-privileged code to perform
> operations "as if" by another user/group. My understanding of what you
> want to add is that it has effectively the same (lack of, in a
> multithreaded setting) security properties as fs[ug]id, but with a
> layer of added confusion and unsafety over what properties it
> provides.

I think POSIX calls them effective IDs, and it just took Linux a while
to recognize that and adjust the signal delivery semantics accordingly.

Thanks,
Florian
diff mbox series

Patch

diff --git a/NEWS b/NEWS
index e63b69b930..83bd54b5e8 100644
--- a/NEWS
+++ b/NEWS
@@ -45,6 +45,12 @@  Major new features:
   pthread_attr_setperthreadfs_np and pthread_attr_getperthreadfs_np have
   been added in support of that.
 
+* Likewise, on Linux, threads can now be created in such a way that they
+  retain per-thread user and group IDs.  (This has always been the way how
+  the Linux kernel implements threads.)  The functions
+  pthread_attr_setperthreadids_np and pthread_attr_getperthreadids_np have
+  been added in support of that.
+
 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 60c15a8d15..c3003c1f94 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.
 
 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_setperthreadid_np (pthread_attr_t *@var{attr}, int @var{scope})
+@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_getperthreadid_np (pthread_attr_t *restrict @var{attr}, int *restrict @var{scope})
+@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
 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;
   }
   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);
+}
+
 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 ());
 
   /* 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;
 
       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;
+}
+
 #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;
+}
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;
+    }
+}
diff --git a/nptl/tst-pthread-perthreadids.c b/nptl/tst-pthread-perthreadids.c
new file mode 100644
index 0000000000..c3d04f5156
--- /dev/null
+++ b/nptl/tst-pthread-perthreadids.c
@@ -0,0 +1,510 @@ 
+/* 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 IDs.  */
+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
+{
+ 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];
+};
+
+static const struct test_case test_cases[] =
+  {
+   { OP_NONE, { 0, }, { 0, 0, 0}, {0, 0, 0} },
+
+   { OP_SETUID, { 1, }, { 1, 1, 1 }, { 0, } },
+   { OP_SETEUID, { 2, }, { 0, 2, }, { 0, } },
+   { OP_SETREUID, { 3, 4, }, { 3, 4, 4 }, { 0, } },
+   { OP_SETRESUID, { 3, 4, 5 }, { 3, 4, 5 }, { 0, } },
+
+   { OP_SETGID, { 6, }, { 0, }, { 6, 6, 6 } },
+   { OP_SETEGID, { 7, }, { 0, }, { 0, 7, } },
+   { OP_SETREGID, { 8, 9, }, { 0, }, { 8, 9, 9 } },
+   { OP_SETRESGID, { 10, 11, 12 }, { 0, }, { 10, 11, 12 } },
+
+   { OP_SETGROUPS, { -1, }, { 0, }, { 0, } },
+   { OP_SETGROUPS, { 13, -1 }, { 0, }, { 0, } },
+   { OP_SETGROUPS, { 13, 14, -1 }, { 0, }, { 0, } },
+   { OP_SETGROUPS, { 13, 14, 15 }, { 0, }, { 0, } },
+
+   /* Final round of checks.  */
+   { OP_NONE, { 0, }, { 0, }, {0, } },
+  };
+
+/* 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.  */
+      pthread_t thr = xpthread_create (NULL, threadfunc, arg);
+      TEST_COMPARE (perthread_flag (thr), arg->perthread);
+      return xpthread_join (thr);
+    }
+
+  const struct test_case *expected = &test_cases[0];
+  gid_t expected_supplementary[3] = { -1, -1, -1 };
+  int expected_supplementary_count = 0;
+
+  xpthread_barrier_wait (&barrier);
+
+  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];
+            }
+        }
+
+      xpthread_barrier_wait (&barrier);
+
+      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);
+
+      xpthread_barrier_wait (&barrier);
+    }
+
+  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 ()));
+
+  /* Main thread and another shared thread are extras.  */
+  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_thread
+    = xpthread_create (NULL, threadfunc,
+                       create_thread_argument ((struct thread_argument) { }));
+  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);
+      TEST_VERIFY (perthread_flag (perthread_threads[i]));
+    }
+  /* Use thread index zero for no-op checking.  */
+  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.  */
+    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));
+      }
+
+  /* Use thread index zero for no-op 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);
+  }
+
+  for (int indirect = 0; indirect < 2; ++indirect)
+    {
+      check_perthread (indirect);
+
+      for (int broadcast_from_main = 0; broadcast_from_main < 2;
+           ++broadcast_from_main)
+        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)
 
 
 /* Mutex attribute data structure.  */
diff --git a/sysdeps/nptl/pthread.h b/sysdeps/nptl/pthread.h
index 37073e3ce7..9ea312cf41 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.  */
+int pthread_attr_setperthreadids_np (pthread_attr_t *__attr, int __scope)
+  __THROW __nonnull ((1));
+
+/* Set *SCOPE to PTHREAD_PER_PROCESS_NP or PTHREAD_PER_THREAD_NP,
+   depending on the state of *ATTR.  */
+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 b80c84c0b7..d8ff0c58f3 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));
+
+/* Set *SCOPE to PTHREAD_PER_PROCESS_NP or PTHREAD_PER_THREAD_NP,
+   depending on the state of *ATTR.  */
+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