From patchwork Thu Dec 13 09:18:32 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 30660 Received: (qmail 70898 invoked by alias); 13 Dec 2018 09:18:52 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 70393 invoked by uid 89); 13 Dec 2018 09:18:51 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-24.9 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT, LIKELY_SPAM_BODY, SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=sk:operati, Closure, Final X-HELO: mx1.redhat.com From: Florian Weimer To: libc-alpha@sourceware.org Cc: Jeremy Allison , Daniel Gryniewicz Subject: [PATCH] Linux: Implement per-thread user and group IDs Date: Thu, 13 Dec 2018 10:18:32 +0100 Message-ID: <87mup9okd3.fsf@oldenburg2.str.redhat.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/26.1 (gnu/linux) MIME-Version: 1.0 This functionality is needed by many file servers, including Samba and nfs-ganesha. The patch context depends on the earlier patch for per-thread file system attributes: (Although there is no functional dependency.) The abilist diffs are not included in this patch because maintaining them causes too much churn. Thanks, Florian ----- 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. 2018-12-12 Florian Weimer * 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.29): Add pthread_attr_setperthreadids_np, pthread_attr_getperthreadids_np. * sysdeps/unix/sysv/linux/alpha/libc.abilist (GLIBC_2.29): Likewise. * sysdeps/unix/sysv/linux/arm/libc.abilist (GLIBC_2.29): Likewise. * sysdeps/unix/sysv/linux/hppa/libc.abilist (GLIBC_2.29): Likewise. * sysdeps/unix/sysv/linux/i386/libc.abilist (GLIBC_2.29): Likewise. * sysdeps/unix/sysv/linux/ia64/libc.abilist (GLIBC_2.29): Likewise. * sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist (GLIBC_2.29): Likewise. * sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist (GLIBC_2.29): Likewise. * sysdeps/unix/sysv/linux/microblaze/libc.abilist (GLIBC_2.29): Likewise. * sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist (GLIBC_2.29): Likewise. * sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist (GLIBC_2.29): Likewise. * sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist (GLIBC_2.29): Likewise. * sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist (GLIBC_2.29): Likewise. * sysdeps/unix/sysv/linux/nios2/libc.abilist (GLIBC_2.29): Likewise. * sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist (GLIBC_2.29): Likewise. * sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist (GLIBC_2.29): Likewise. * sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist (GLIBC_2.29): Likewise. * sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist (GLIBC_2.29): Likewise. * sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist (GLIBC_2.29): Likewise. * sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist (GLIBC_2.29): Likewise. * sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist (GLIBC_2.29): Likewise. * sysdeps/unix/sysv/linux/sh/libc.abilist (GLIBC_2.29): Likewise. * sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist (GLIBC_2.29): Likewise. * sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist (GLIBC_2.29): Likewise. * sysdeps/unix/sysv/linux/x86_64/64/libc.abilist (GLIBC_2.29): Likewise. * sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist (GLIBC_2.29): Likewise. diff --git a/NEWS b/NEWS index 190b0ddb57..1d1c193743 100644 --- a/NEWS +++ b/NEWS @@ -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 diff --git a/nptl/Makefile b/nptl/Makefile index 8a071af5c7..7e82f0bdbf 100644 --- a/nptl/Makefile +++ b/nptl/Makefile @@ -31,7 +31,8 @@ routines = alloca_cutoff forward libc-lowlevellock libc-cancellation \ libc-cleanup libc_pthread_init libc_multiple_threads \ register-atfork pthread_atfork pthread_self thrd_current \ thrd_equal thrd_sleep thrd_yield \ - pthread_attr_setperthreadfs_np pthread_attr_getperthreadfs_np + pthread_attr_setperthreadfs_np pthread_attr_getperthreadfs_np \ + pthread_attr_setperthreadids_np pthread_attr_getperthreadids_np shared-only-routines = forward static-only-routines = pthread_atfork @@ -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 diff --git a/nptl/Versions b/nptl/Versions index ba71f85411..95490322d6 100644 --- a/nptl/Versions +++ b/nptl/Versions @@ -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; diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c index 04e3f08465..d4c25a951f 100644 --- a/nptl/allocatestack.c +++ b/nptl/allocatestack.c @@ -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); diff --git a/nptl/pthreadP.h b/nptl/pthreadP.h index 7f16ba9800..7a1f965d9e 100644 --- a/nptl/pthreadP.h +++ b/nptl/pthreadP.h @@ -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) diff --git a/nptl/pthread_attr_getperthreadids_np.c b/nptl/pthread_attr_getperthreadids_np.c new file mode 100644 index 0000000000..cfadfb3bed --- /dev/null +++ b/nptl/pthread_attr_getperthreadids_np.c @@ -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 + . */ + +#include +#include + +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; +} diff --git a/nptl/pthread_attr_setperthreadids_np.c b/nptl/pthread_attr_setperthreadids_np.c new file mode 100644 index 0000000000..84d68e0e4f --- /dev/null +++ b/nptl/pthread_attr_setperthreadids_np.c @@ -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 + . */ + +#include +#include + +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; +} diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c index fe75d04113..cf9dae201b 100644 --- a/nptl/pthread_create.c +++ b/nptl/pthread_create.c @@ -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 diff --git a/nptl/tst-pthread-perthreadids.c b/nptl/tst-pthread-perthreadids.c new file mode 100644 index 0000000000..ad021e1255 --- /dev/null +++ b/nptl/tst-pthread-perthreadids.c @@ -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 + . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 ""; + 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 diff --git a/support/Makefile b/support/Makefile index 2ef8c019b1..230532d728 100644 --- a/support/Makefile +++ b/support/Makefile @@ -88,6 +88,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..2e7b413934 --- /dev/null +++ b/support/xgetresgid.c @@ -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 + . */ + +#include +#include + +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..0081d1fa78 --- /dev/null +++ b/support/xgetresuid.c @@ -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 + . */ + +#include +#include + +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 13dfa2c0e5..2233aa0386 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 30396cc448..cde8f40503 100644 --- a/sysdeps/nptl/internaltypes.h +++ b/sysdeps/nptl/internaltypes.h @@ -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 diff --git a/sysdeps/nptl/pthread.h b/sysdeps/nptl/pthread.h index 692d694bbd..ac65069976 100644 --- a/sysdeps/nptl/pthread.h +++ b/sysdeps/nptl/pthread.h @@ -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)); diff --git a/sysdeps/nptl/setxid.h b/sysdeps/nptl/setxid.h index de39efc723..09af829d3f 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; \