diff options
-rw-r--r-- | ChangeLog | 7 | ||||
-rw-r--r-- | nptl/Makefile | 3 | ||||
-rw-r--r-- | nptl/nptl-init.c | 9 | ||||
-rw-r--r-- | nptl/tst-setuid2.c | 145 |
4 files changed, 161 insertions, 3 deletions
@@ -1,3 +1,10 @@ +2014-04-01 Florian Weimer <fweimer@redhat.com> + + [BZ #13347] + * nptl/nptl-init.c (sighandler_setxid): Check system call result. + * nptl/tst-setuid2.c: New file. + * nptl/Makefile (xtests): Add tst-setuid2. + 2014-04-01 Alan Modra <amodra@gmail.com> [BZ #16786] diff --git a/nptl/Makefile b/nptl/Makefile index 897ac9669c..287622475d 100644 --- a/nptl/Makefile +++ b/nptl/Makefile @@ -270,7 +270,8 @@ tests = tst-typesizes \ tst-vfork1 tst-vfork2 tst-vfork1x tst-vfork2x \ tst-getpid1 tst-getpid2 tst-getpid3 \ tst-initializers1 $(patsubst %,tst-initializers1-%,c89 gnu89 c99 gnu99) -xtests = tst-setuid1 tst-setuid1-static tst-mutexpp1 tst-mutexpp6 tst-mutexpp10 +xtests = tst-setuid1 tst-setuid1-static tst-setuid2 \ + tst-mutexpp1 tst-mutexpp6 tst-mutexpp10 test-srcs = tst-oddstacklimit # Files which must not be linked with libpthread. diff --git a/nptl/nptl-init.c b/nptl/nptl-init.c index 794156ba8e..2796dc5182 100644 --- a/nptl/nptl-init.c +++ b/nptl/nptl-init.c @@ -232,6 +232,7 @@ sighandler_setxid (int sig, siginfo_t *si, void *ctx) /* Determine the process ID. It might be negative if the thread is in the middle of a fork() call. */ pid_t pid = THREAD_GETMEM (THREAD_SELF, pid); + int result; if (__glibc_unlikely (pid < 0)) pid = -pid; @@ -245,8 +246,12 @@ sighandler_setxid (int sig, siginfo_t *si, void *ctx) return; INTERNAL_SYSCALL_DECL (err); - INTERNAL_SYSCALL_NCS (__xidcmd->syscall_no, err, 3, __xidcmd->id[0], - __xidcmd->id[1], __xidcmd->id[2]); + result = INTERNAL_SYSCALL_NCS (__xidcmd->syscall_no, err, 3, __xidcmd->id[0], + __xidcmd->id[1], __xidcmd->id[2]); + if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (result, err))) + /* Safety check. This should never happen if the setxid system + calls are only ever called through their glibc wrappers. */ + abort (); /* Reset the SETXID flag. */ struct pthread *self = THREAD_SELF; diff --git a/nptl/tst-setuid2.c b/nptl/tst-setuid2.c new file mode 100644 index 0000000000..951aeccac5 --- /dev/null +++ b/nptl/tst-setuid2.c @@ -0,0 +1,145 @@ +/* Copyright (C) 2014 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 <errno.h> +#include <pthread.h> +#include <signal.h> +#include <stdbool.h> +#include <stdio.h> +#include <sys/syscall.h> +#include <unistd.h> + +/* Check that a partial setuid failure aborts the process. */ + +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t cond_send; +static void (*func_sent) (void); +static pthread_cond_t cond_recv; + +#define FAIL(fmt, ...) \ + do { printf ("FAIL: " fmt "\n", __VA_ARGS__); _exit (1); } while (0) + +static void * +thread_func (void *ctx __attribute__ ((unused))) +{ + int ret = pthread_mutex_lock (&mutex); + if (ret != 0) + FAIL ("pthread_mutex_lock (thread): %d", ret); + + while (true) + { + if (func_sent != NULL) + { + void (*func) (void) = func_sent; + ret = pthread_mutex_unlock (&mutex); + if (ret != 0) + FAIL ("pthread_mutex_unlock (thread): %d", ret); + func (); + ret = pthread_mutex_lock (&mutex); + if (ret != 0) + FAIL ("pthread_mutex_lock (thread): %d", ret); + func_sent = NULL; + ret = pthread_cond_signal (&cond_recv); + if (ret != 0) + FAIL ("pthread_cond_signal (recv): %d", ret); + } + ret = pthread_cond_wait (&cond_send, &mutex); + if (ret != 0) + FAIL ("pthread_cond_wait (send): %d", ret); + } + return NULL; +} + +static void +run_on_thread (void (*func) (void)) +{ + int ret = pthread_mutex_lock (&mutex); + if (ret != 0) + FAIL ("pthread_mutex_lock (%s): %d", __func__, ret); + func_sent = func; + ret = pthread_mutex_unlock (&mutex); + if (ret != 0) + FAIL ("pthread_mutex_unlock (%s): %d", __func__, ret); + + ret = pthread_cond_signal (&cond_send); + if (ret != 0) + FAIL ("pthread_mutex_lock (%s): %d", __func__, ret); + + ret = pthread_mutex_lock (&mutex); + if (ret != 0) + FAIL ("pthread_mutex_lock (%s): %d", __func__, ret); + + while (func_sent != NULL) + { + ret = pthread_cond_wait (&cond_recv, &mutex); + if (ret != 0) + FAIL ("pthread_mutex_wait (%s): %d", __func__, ret); + } + ret = pthread_mutex_unlock (&mutex); + if (ret != 0) + FAIL ("pthread_mutex_unlock (%s): %d", __func__, ret); +} + +static void +change_thread_ids (void) +{ + long ret = syscall (__NR_setresuid, 2001, 2002, 2003); + if (ret != 0) + FAIL ("setresuid (2001, 2002, 2003): %ld", ret); +} + +static uid_t ruid, euid, suid; + +static void +get_thread_ids (void) +{ + if (getresuid (&ruid, &euid, &suid) < 0) + FAIL ("getresuid: %m (%d)", errno); +} + +static void +abort_expected (int signal __attribute__ ((unused))) +{ + _exit (0); +} + +static int +do_test (void) +{ + pthread_t thread; + int ret = pthread_create (&thread, NULL, thread_func, NULL); + if (ret != 0) + FAIL ("pthread_create: %d", ret); + + run_on_thread (change_thread_ids); + + signal (SIGABRT, &abort_expected); + /* This should abort the process. */ + if (setresuid (1001, 1002, 1003) < 0) + FAIL ("setresuid: %m (%d)", errno); + signal (SIGABRT, SIG_DFL); + + /* If we get here, check that the kernel did the right thing. */ + run_on_thread (get_thread_ids); + if (ruid != 1001 || euid != 1002 || euid != 1003) + FAIL ("unexpected UIDs after setuid: %ld, %ld, %ld", + (long) ruid, (long) euid, (long) suid); + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" |