Linux: Implement per-thread user and group IDs
Commit Message
* 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.
Comments
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
>
* 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
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.
@@ -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
@@ -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
@@ -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
@@ -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;
@@ -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);
@@ -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)
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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;
+ }
+}
new file mode 100644
@@ -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>
@@ -94,6 +94,8 @@ libsupport-routines = \
xfopen \
xfork \
xftruncate \
+ xgetresgid \
+ xgetresuid \
xgetsockname \
xlisten \
xlseek \
new file mode 100644
@@ -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");
+}
new file mode 100644
@@ -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");
+}
@@ -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);
@@ -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. */
@@ -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));
@@ -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; \
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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));
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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