1 | /* Copyright (C) 2002-2023 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 |
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 |
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_fetch_add_relaxed (&xidcmd->cntr, -1) == 1) |
92 | futex_wake ((unsigned int *) &xidcmd->cntr, 1, FUTEX_PRIVATE); |
93 | } |
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 |
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 |
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_fetch_add_relaxed (&cmdp->cntr, 1); |
167 | return 1; |
168 | } |
169 | else |
170 | return 0; |
171 | } |
172 | |
173 | int |
174 | attribute_hidden |
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 | |