1 /* Copyright (C) 2002-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 <futex-internal.h>
19 #include <ldsodefs.h>
20 #include <list.h>
21 #include <lowlevellock.h>
22 #include <pthreadP.h>
23 #include <unistd.h>
24 
25 /* Check for consistency across set*id system call results.  The abort
26    should not happen as long as all privileges changes happen through
27    the glibc wrappers.  ERROR must be 0 (no error) or an errno
28    code.  */
29 static void
setxid_error(struct xid_command * cmdp,int error)30 setxid_error (struct xid_command *cmdp, int error)
31 {
32   do
33     {
34       int olderror = cmdp->error;
35       if (olderror == error)
36         break;
37       if (olderror != -1)
38         {
39           /* Mismatch between current and previous results.  Save the
40              error value to memory so that is not clobbered by the
41              abort function and preserved in coredumps.  */
42           volatile int xid_err __attribute__ ((unused)) = error;
43           abort ();
44         }
45     }
46   while (atomic_compare_and_exchange_bool_acq (&cmdp->error, error, -1));
47 }
48 
49 /* Set by __nptl_setxid and used by __nptl_setxid_sighandler.  */
50 static struct xid_command *xidcmd;
51 
52 /* We use the SIGSETXID signal in the setuid, setgid, etc. implementations to
53    tell each thread to call the respective setxid syscall on itself.  This is
54    the handler.  */
55 void
__nptl_setxid_sighandler(int sig,siginfo_t * si,void * ctx)56 __nptl_setxid_sighandler (int sig, siginfo_t *si, void *ctx)
57 {
58   int result;
59 
60   /* Safety check.  It would be possible to call this function for
61      other signals and send a signal from another process.  This is not
62      correct and might even be a security problem.  Try to catch as
63      many incorrect invocations as possible.  */
64   if (sig != SIGSETXID
65       || si->si_pid != __getpid ()
66       || si->si_code != SI_TKILL)
67     return;
68 
69   result = INTERNAL_SYSCALL_NCS (xidcmd->syscall_no, 3, xidcmd->id[0],
70 				 xidcmd->id[1], xidcmd->id[2]);
71   int error = 0;
72   if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (result)))
73     error = INTERNAL_SYSCALL_ERRNO (result);
74   setxid_error (xidcmd, error);
75 
76   /* Reset the SETXID flag.  */
77   struct pthread *self = THREAD_SELF;
78   int flags, newval;
79   do
80     {
81       flags = THREAD_GETMEM (self, cancelhandling);
82       newval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling,
83 					  flags & ~SETXID_BITMASK, flags);
84     }
85   while (flags != newval);
86 
87   /* And release the futex.  */
88   self->setxid_futex = 1;
89   futex_wake (&self->setxid_futex, 1, FUTEX_PRIVATE);
90 
91   if (atomic_decrement_val (&xidcmd->cntr) == 0)
92     futex_wake ((unsigned int *) &xidcmd->cntr, 1, FUTEX_PRIVATE);
93 }
libc_hidden_def(__nptl_setxid_sighandler)94 libc_hidden_def (__nptl_setxid_sighandler)
95 
96 static void
97 setxid_mark_thread (struct xid_command *cmdp, struct pthread *t)
98 {
99   int ch;
100 
101   /* Wait until this thread is cloned.  */
102   if (t->setxid_futex == -1
103       && ! atomic_compare_and_exchange_bool_acq (&t->setxid_futex, -2, -1))
104     do
105       futex_wait_simple (&t->setxid_futex, -2, FUTEX_PRIVATE);
106     while (t->setxid_futex == -2);
107 
108   /* Don't let the thread exit before the setxid handler runs.  */
109   t->setxid_futex = 0;
110 
111   do
112     {
113       ch = t->cancelhandling;
114 
115       /* If the thread is exiting right now, ignore it.  */
116       if ((ch & EXITING_BITMASK) != 0)
117         {
118           /* Release the futex if there is no other setxid in
119              progress.  */
120           if ((ch & SETXID_BITMASK) == 0)
121             {
122               t->setxid_futex = 1;
123               futex_wake (&t->setxid_futex, 1, FUTEX_PRIVATE);
124             }
125           return;
126         }
127     }
128   while (atomic_compare_and_exchange_bool_acq (&t->cancelhandling,
129                                                ch | SETXID_BITMASK, ch));
130 }
131 
132 
133 static void
setxid_unmark_thread(struct xid_command * cmdp,struct pthread * t)134 setxid_unmark_thread (struct xid_command *cmdp, struct pthread *t)
135 {
136   int ch;
137 
138   do
139     {
140       ch = t->cancelhandling;
141       if ((ch & SETXID_BITMASK) == 0)
142         return;
143     }
144   while (atomic_compare_and_exchange_bool_acq (&t->cancelhandling,
145                                                ch & ~SETXID_BITMASK, ch));
146 
147   /* Release the futex just in case.  */
148   t->setxid_futex = 1;
149   futex_wake (&t->setxid_futex, 1, FUTEX_PRIVATE);
150 }
151 
152 
153 static int
setxid_signal_thread(struct xid_command * cmdp,struct pthread * t)154 setxid_signal_thread (struct xid_command *cmdp, struct pthread *t)
155 {
156   if ((t->cancelhandling & SETXID_BITMASK) == 0)
157     return 0;
158 
159   int val;
160   pid_t pid = __getpid ();
161   val = INTERNAL_SYSCALL_CALL (tgkill, pid, t->tid, SIGSETXID);
162 
163   /* If this failed, it must have had not started yet or else exited.  */
164   if (!INTERNAL_SYSCALL_ERROR_P (val))
165     {
166       atomic_increment (&cmdp->cntr);
167       return 1;
168     }
169   else
170     return 0;
171 }
172 
173 int
174 attribute_hidden
__nptl_setxid(struct xid_command * cmdp)175 __nptl_setxid (struct xid_command *cmdp)
176 {
177   int signalled;
178   int result;
179   lll_lock (GL (dl_stack_cache_lock), LLL_PRIVATE);
180 
181   xidcmd = cmdp;
182   cmdp->cntr = 0;
183   cmdp->error = -1;
184 
185   struct pthread *self = THREAD_SELF;
186 
187   /* Iterate over the list with system-allocated threads first.  */
188   list_t *runp;
189   list_for_each (runp, &GL (dl_stack_used))
190     {
191       struct pthread *t = list_entry (runp, struct pthread, list);
192       if (t == self)
193         continue;
194 
195       setxid_mark_thread (cmdp, t);
196     }
197 
198   /* Now the list with threads using user-allocated stacks.  */
199   list_for_each (runp, &GL (dl_stack_user))
200     {
201       struct pthread *t = list_entry (runp, struct pthread, list);
202       if (t == self)
203         continue;
204 
205       setxid_mark_thread (cmdp, t);
206     }
207 
208   /* Iterate until we don't succeed in signalling anyone.  That means
209      we have gotten all running threads, and their children will be
210      automatically correct once started.  */
211   do
212     {
213       signalled = 0;
214 
215       list_for_each (runp, &GL (dl_stack_used))
216         {
217           struct pthread *t = list_entry (runp, struct pthread, list);
218           if (t == self)
219             continue;
220 
221           signalled += setxid_signal_thread (cmdp, t);
222         }
223 
224       list_for_each (runp, &GL (dl_stack_user))
225         {
226           struct pthread *t = list_entry (runp, struct pthread, list);
227           if (t == self)
228             continue;
229 
230           signalled += setxid_signal_thread (cmdp, t);
231         }
232 
233       int cur = cmdp->cntr;
234       while (cur != 0)
235         {
236           futex_wait_simple ((unsigned int *) &cmdp->cntr, cur,
237                              FUTEX_PRIVATE);
238           cur = cmdp->cntr;
239         }
240     }
241   while (signalled != 0);
242 
243   /* Clean up flags, so that no thread blocks during exit waiting
244      for a signal which will never come.  */
245   list_for_each (runp, &GL (dl_stack_used))
246     {
247       struct pthread *t = list_entry (runp, struct pthread, list);
248       if (t == self)
249         continue;
250 
251       setxid_unmark_thread (cmdp, t);
252     }
253 
254   list_for_each (runp, &GL (dl_stack_user))
255     {
256       struct pthread *t = list_entry (runp, struct pthread, list);
257       if (t == self)
258         continue;
259 
260       setxid_unmark_thread (cmdp, t);
261     }
262 
263   /* This must be last, otherwise the current thread might not have
264      permissions to send SIGSETXID syscall to the other threads.  */
265   result = INTERNAL_SYSCALL_NCS (cmdp->syscall_no, 3,
266                                  cmdp->id[0], cmdp->id[1], cmdp->id[2]);
267   int error = 0;
268   if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (result)))
269     {
270       error = INTERNAL_SYSCALL_ERRNO (result);
271       __set_errno (error);
272       result = -1;
273     }
274   setxid_error (cmdp, error);
275 
276   lll_unlock (GL (dl_stack_cache_lock), LLL_PRIVATE);
277   return result;
278 }
279