1 /* Copyright (C) 2014-2022 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3 
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8 
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, see
16    <https://www.gnu.org/licenses/>.  */
17 
18 #include <errno.h>
19 #include <pthread.h>
20 #include <signal.h>
21 #include <stdbool.h>
22 #include <stdio.h>
23 #include <sys/syscall.h>
24 #include <unistd.h>
25 
26 /* Check that a partial setuid failure aborts the process. */
27 
28 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
29 static pthread_cond_t cond_send;
30 static void (*func_sent) (void);
31 static pthread_cond_t cond_recv;
32 
33 #define FAIL(fmt, ...) \
34   do { printf ("FAIL: " fmt "\n", __VA_ARGS__); _exit (1); } while (0)
35 
36 static void *
thread_func(void * ctx)37 thread_func (void *ctx __attribute__ ((unused)))
38 {
39   int ret = pthread_mutex_lock (&mutex);
40   if (ret != 0)
41     FAIL ("pthread_mutex_lock (thread): %d", ret);
42 
43   while (true)
44     {
45       if (func_sent != NULL)
46 	{
47 	  void (*func) (void) = func_sent;
48 	  ret = pthread_mutex_unlock (&mutex);
49 	  if (ret != 0)
50 	    FAIL ("pthread_mutex_unlock (thread): %d", ret);
51 	  func ();
52 	  ret = pthread_mutex_lock (&mutex);
53 	  if (ret != 0)
54 	    FAIL ("pthread_mutex_lock (thread): %d", ret);
55 	  func_sent = NULL;
56 	  ret = pthread_cond_signal (&cond_recv);
57 	  if (ret != 0)
58 	    FAIL ("pthread_cond_signal (recv): %d", ret);
59 	}
60       ret = pthread_cond_wait (&cond_send, &mutex);
61       if (ret != 0)
62 	FAIL ("pthread_cond_wait (send): %d", ret);
63     }
64   return NULL;
65 }
66 
67 static void
run_on_thread(void (* func)(void))68 run_on_thread (void (*func) (void))
69 {
70   int ret = pthread_mutex_lock (&mutex);
71   if (ret != 0)
72     FAIL ("pthread_mutex_lock (%s): %d", __func__, ret);
73   func_sent = func;
74   ret = pthread_mutex_unlock (&mutex);
75   if (ret != 0)
76     FAIL ("pthread_mutex_unlock (%s): %d", __func__, ret);
77 
78   ret = pthread_cond_signal (&cond_send);
79   if (ret != 0)
80     FAIL ("pthread_mutex_lock (%s): %d", __func__, ret);
81 
82   ret = pthread_mutex_lock (&mutex);
83   if (ret != 0)
84     FAIL ("pthread_mutex_lock (%s): %d", __func__, ret);
85 
86   while (func_sent != NULL)
87     {
88       ret = pthread_cond_wait (&cond_recv, &mutex);
89       if (ret != 0)
90 	FAIL ("pthread_mutex_wait (%s): %d", __func__, ret);
91     }
92   ret = pthread_mutex_unlock (&mutex);
93   if (ret != 0)
94     FAIL ("pthread_mutex_unlock (%s): %d", __func__, ret);
95 }
96 
97 static void
change_thread_ids(void)98 change_thread_ids (void)
99 {
100   long ret = syscall (__NR_setresuid, 2001, 2002, 2003);
101   if (ret != 0)
102     FAIL ("setresuid (2001, 2002, 2003): %ld", ret);
103 }
104 
105 static uid_t ruid, euid, suid;
106 
107 static void
get_thread_ids(void)108 get_thread_ids (void)
109 {
110   if (getresuid (&ruid, &euid, &suid) < 0)
111     FAIL ("getresuid: %m (%d)", errno);
112 }
113 
114 static void
abort_expected(int signal)115 abort_expected (int signal __attribute__ ((unused)))
116 {
117   _exit (0);
118 }
119 
120 static int
do_test(void)121 do_test (void)
122 {
123   pthread_t thread;
124   int ret = pthread_create (&thread, NULL, thread_func, NULL);
125   if (ret != 0)
126     FAIL ("pthread_create: %d", ret);
127 
128   run_on_thread (change_thread_ids);
129 
130   signal (SIGABRT, &abort_expected);
131   /* This should abort the process.  */
132   if (setresuid (1001, 1002, 1003) < 0)
133     FAIL ("setresuid: %m (%d)", errno);
134   signal (SIGABRT, SIG_DFL);
135 
136   /* If we get here, check that the kernel did the right thing. */
137   run_on_thread (get_thread_ids);
138   if (ruid != 1001 || euid != 1002 || suid != 1003)
139     FAIL ("unexpected UIDs after setuid: %ld, %ld, %ld",
140 	  (long) ruid, (long) euid, (long) suid);
141   return 0;
142 }
143 
144 #define TEST_FUNCTION do_test ()
145 #include "../test-skeleton.c"
146