| /* Copyright (C) 2014-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 <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 || suid != 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" |