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