@@ -51,6 +51,12 @@ 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. (This has always been the way how the
+ Linux kernel implements threads.) The functions
+ pthread_attr_setperthreadids_np and pthread_attr_getperthreadids_np have
+ been added in support of that.
+
Deprecated and removed features, and other changes affecting compatibility:
* The glibc.tune tunable namespace has been renamed to glibc.cpu and the
@@ -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
@@ -328,7 +329,8 @@ tests-internal := tst-rwlock19 tst-rwlock20 \
tst-mutexpi8 tst-mutexpi8-static
xtests = tst-setuid1 tst-setuid1-static tst-setuid2 \
- tst-mutexpp1 tst-mutexpp6 tst-mutexpp10
+ tst-mutexpp1 tst-mutexpp6 tst-mutexpp10 \
+ 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.29 {
pthread_attr_setperthreadfs_np;
pthread_attr_getperthreadfs_np;
+ pthread_attr_setperthreadids_np;
+ pthread_attr_getperthreadids_np;
}
GLIBC_PRIVATE {
__libc_alloca_cutoff;
@@ -1114,6 +1114,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)
@@ -1127,13 +1135,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);
@@ -1143,7 +1152,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);
@@ -1159,7 +1168,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);
@@ -1168,7 +1177,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);
@@ -1189,7 +1198,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);
@@ -1198,7 +1207,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);
@@ -655,6 +655,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,27 @@
+/* Read the per-thread user/group IDs flag in thread attributes.
+ Copyright (C) 2018 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 *attr)
+{
+ struct pthread_attr *iattr = (struct pthread_attr *) attr;
+ return (iattr->flags & ATTR_FLAG_PERTHREADIDS) != 0;
+}
new file mode 100644
@@ -0,0 +1,30 @@
+/* Change the per-thread user/group IDs flag in thread attributes.
+ Copyright (C) 2018 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>
+
+void
+pthread_attr_setperthreadids_np (pthread_attr_t *attr, int enabled)
+{
+ struct pthread_attr *iattr = (struct pthread_attr *) attr;
+ if (enabled)
+ iattr->flags |= ATTR_FLAG_PERTHREADIDS;
+ else
+ iattr->flags &= ~ATTR_FLAG_PERTHREADIDS;
+}
@@ -700,8 +700,8 @@ __pthread_create_2_1 (pthread_t *newthread, const pthread_attr_t *attr,
/* Copy the thread attribute flags. */
struct pthread *self = THREAD_SELF;
- pd->flags = ((iattr->flags & ~(ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET))
- | (self->flags & (ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET)));
+ pd->flags = ((iattr->flags & ~ATTR_FLAGS_IGNORED_ATTR)
+ | (self->flags & ATTR_FLAGS_INHERITED));
/* Initialize the field for the ID of the thread which is waiting
for us. This is a self-reference in case the thread is created
new file mode 100644
@@ -0,0 +1,426 @@
+/* Test per-thread user and group IDs.
+ Copyright (C) 2018 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>
+
+enum operation
+{
+ OP_NONE,
+ OP_SETUID,
+ OP_SETEUID,
+ OP_SETREUID,
+ OP_SETRESUID,
+ OP_SETGID,
+ OP_SETEGID,
+ OP_SETREGID,
+ OP_SETRESGID,
+ OP_SETGROUPS,
+};
+
+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);
+}
+
+struct test_case
+{
+ enum operation op;
+ int args[3];
+
+ /* Expected UIDs and GIDs are only used if the current thread has
+ made changes. */
+ uid_t expected_uid[3];
+ gid_t expected_gid[3];
+};
+
+static const struct test_case test_cases[] =
+ {
+ { OP_NONE, { 0, }, { 0, 0, 0}, {0, 0, 0} },
+
+ { OP_SETUID, { 1, }, { 1, 1, 1 }, { 0, } },
+ { OP_SETEUID, { 2, }, { 0, 2, }, { 0, } },
+ { OP_SETREUID, { 3, 4, }, { 3, 4, 4 }, { 0, } },
+ { OP_SETRESUID, { 3, 4, 5 }, { 3, 4, 5 }, { 0, } },
+
+ { OP_SETGID, { 6, }, { 0, }, { 6, 6, 6 } },
+ { OP_SETEGID, { 7, }, { 0, }, { 0, 7, } },
+ { OP_SETREGID, { 8, 9, }, { 0, }, { 8, 9, 9 } },
+ { OP_SETRESGID, { 10, 11, 12 }, { 0, }, { 10, 11, 12 } },
+
+ { OP_SETGROUPS, { -1, }, { 0, }, { 0, } },
+ { OP_SETGROUPS, { 13, -1 }, { 0, }, { 0, } },
+ { OP_SETGROUPS, { 13, 14, -1 }, { 0, }, { 0, } },
+ { OP_SETGROUPS, { 13, 14, 15 }, { 0, }, { 0, } },
+
+ /* Final round of checks. */
+ { OP_NONE, { 0, }, { 0, }, {0, } },
+ };
+
+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;
+}
+
+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;
+};
+
+static struct thread_argument *
+create_thread_argument (size_t index, bool suppress, bool indirect)
+{
+ struct thread_argument *result = xmalloc (sizeof (*result));
+ result->thread_index = index;
+ result->suppress_operation = suppress;
+ return result;
+}
+
+static void *
+threadfunc (void *closure)
+{
+ struct thread_argument *arg = closure;
+
+ if (arg->indirect)
+ {
+ arg->indirect = false;
+ xpthread_join (xpthread_create (NULL, threadfunc, arg));
+ return NULL;
+ }
+
+ const struct test_case *expected = &test_cases[0];
+ gid_t expected_supplementary[3] = { -1, -1, -1 };
+ int expected_supplementary_count = 0;
+
+ xpthread_barrier_wait (&barrier);
+
+ for (size_t test_index = 0; test_index < array_length (test_cases);
+ ++test_index)
+ {
+ if (test_index == arg->thread_index)
+ {
+ if (test_verbose > 0)
+ {
+ if (arg->suppress_operation)
+ printf ("info: thread %zu suppressing operation\n",
+ test_index);
+ else
+ printf ("info: thread %zu performing operation\n",
+ test_index);
+ }
+ if (!arg->suppress_operation)
+ test_case_run (&test_cases[test_index]);
+
+ expected = &test_cases[test_index];
+ if (test_cases[test_index].op == OP_SETGROUPS)
+ {
+ expected_supplementary_count
+ = supplemetary_count (&test_cases[test_index]);
+ for (int i = 0; i < expected_supplementary_count; ++i)
+ expected_supplementary[i] = test_cases[test_index].args[i];
+ }
+ }
+
+ xpthread_barrier_wait (&barrier);
+
+ xpthread_mutex_lock (&mutex);
+
+ if (test_verbose > 0)
+ printf ("info: checking phase for thread %zu, test %zu\n",
+ arg->thread_index, test_index);
+ uid_t actual_uid[3];
+ xgetresuid (&actual_uid[0], &actual_uid[1], &actual_uid[2]);
+ TEST_COMPARE (actual_uid[0], expected->expected_uid[0]);
+ TEST_COMPARE (actual_uid[1], expected->expected_uid[1]);
+ TEST_COMPARE (actual_uid[2], expected->expected_uid[2]);
+ gid_t actual_gid[3];
+ xgetresgid (&actual_gid[0], &actual_gid[1], &actual_gid[2]);
+ TEST_COMPARE (actual_gid[0], expected->expected_gid[0]);
+ TEST_COMPARE (actual_gid[1], expected->expected_gid[1]);
+ TEST_COMPARE (actual_gid[2], expected->expected_gid[2]);
+
+ gid_t actual_supplementary[3];
+ gid_t actual_supplementary_count
+ = getgroups (array_length (actual_supplementary),
+ actual_supplementary);
+ TEST_COMPARE (actual_supplementary_count, expected_supplementary_count);
+ if (actual_supplementary_count > 0)
+ TEST_COMPARE (actual_supplementary[0], expected_supplementary[0]);
+ if (actual_supplementary_count > 1)
+ TEST_COMPARE (actual_supplementary[1], expected_supplementary[1]);
+ if (actual_supplementary_count > 2)
+ TEST_COMPARE (actual_supplementary[2], expected_supplementary[2]);
+
+ xpthread_mutex_unlock (&mutex);
+
+ xpthread_barrier_wait (&barrier);
+ }
+
+ free (arg);
+ return NULL;
+}
+
+/* Used to create threads with per-thread user/group IDs. */
+static pthread_attr_t attr_perthreadids;
+
+/* Test which verifies that per-thread IDs are thread-specific. */
+static void
+check_perthread (bool indirect)
+{
+ if (test_verbose > 0)
+ printf ("info: testing per-thread IDs, %s indirection\n",
+ indirect ? "with" : "without");
+ /* Main thread and another shared thread are extras. */
+ xpthread_barrier_init (&barrier, NULL, array_length (test_cases) + 2);
+
+ pthread_t perthread_threads[array_length (test_cases)];
+ /* Use thread index zero for no-op checking. */
+ pthread_t shared_thread
+ = xpthread_create (NULL, threadfunc,
+ create_thread_argument (0, false, false));
+ for (size_t i = 0; i < array_length (test_cases); ++i)
+ {
+ struct thread_argument *arg
+ = create_thread_argument (i, false, indirect);
+ perthread_threads[i] = xpthread_create (&attr_perthreadids, threadfunc,
+ arg);
+ }
+ /* Use thread index zero for no-op checking. */
+ threadfunc (create_thread_argument (0, false, false));
+ 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];
+ shared_threads[0]
+ = xpthread_create (NULL, threadfunc,
+ create_thread_argument (arg->broadcast_test_index,
+ arg->broadcast_from_main,
+ false));
+ shared_threads[1]
+ = xpthread_create (NULL, threadfunc,
+ create_thread_argument (arg->broadcast_test_index,
+ true, false));
+
+ for (size_t i = 0; i < array_length (test_cases); ++i)
+ /* Skip the test which uses broadcasting. */
+ if (i != arg->broadcast_test_index)
+ perthread_threads[i]
+ = xpthread_create (&attr_perthreadids, threadfunc,
+ create_thread_argument (i, false, arg->indirect));
+ /* Use thread index zero for no-op checking. */
+ threadfunc (create_thread_argument (arg->broadcast_test_index,
+ !arg->broadcast_from_main, false));
+ 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_COMPARE (pthread_attr_getperthreadids_np (&attr_perthreadids), 0);
+ pthread_attr_setperthreadids_np (&attr_perthreadids, true);
+ TEST_COMPARE (pthread_attr_getperthreadids_np (&attr_perthreadids), 1);
+ /* The flag is normalized to 1. */
+ pthread_attr_setperthreadids_np (&attr_perthreadids, 2);
+ TEST_COMPARE (pthread_attr_getperthreadids_np (&attr_perthreadids), 1);
+ pthread_attr_setperthreadids_np (&attr_perthreadids, 0);
+ TEST_COMPARE (pthread_attr_getperthreadids_np (&attr_perthreadids), 0);
+ pthread_attr_setperthreadids_np (&attr_perthreadids, true);
+ TEST_COMPARE (pthread_attr_getperthreadids_np (&attr_perthreadids), 1);
+
+ for (int indirect = 0; indirect < 2; ++indirect)
+ {
+ check_perthread (indirect);
+
+ for (int broadcast_from_main = 0; broadcast_from_main < 2;
+ ++broadcast_from_main)
+ for (size_t broadcast_test_index = 0;
+ broadcast_test_index < array_length (test_cases);
+ ++broadcast_test_index)
+ {
+ struct subprocess_argument arg =
+ {
+ .broadcast_test_index = broadcast_test_index,
+ .broadcast_from_main = broadcast_from_main,
+ .indirect = indirect,
+ };
+ support_isolate_in_subprocess (subprocess, &arg);
+ }
+ }
+
+ xpthread_attr_destroy (&attr_perthreadids);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
@@ -88,6 +88,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) 2018 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) 2018 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,7 +49,18 @@ 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. */
+#define ATTR_FLAGS_IGNORED_ATTR \
+ (ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET)
+
+/* These flags are inherited from the current thread during
+ pthread_create even if they are not specified in the thread
+ attribute. */
+#define ATTR_FLAGS_INHERITED \
+ (ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET | ATTR_FLAG_PERTHREADIDS)
/* Mutex attribute data structure. */
struct pthread_mutexattr
@@ -418,6 +418,23 @@ void pthread_attr_setperthreadfs_np (pthread_attr_t *__attr, int __enabled)
int pthread_attr_getperthreadfs_np (const pthread_attr_t *__attr)
__THROW __nonnull ((1));
+/* 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 __enabled)
+ __THROW __nonnull ((1));
+
+/* Return 1 if ATTR enables per-thread user and group IDs, or 0 if
+ not. See pthread_attr_setperthreadids_np. */
+int pthread_attr_getperthreadids_np (const pthread_attr_t *__attr)
+ __THROW __nonnull ((1));
+
/* 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; \