]> git.ipfire.org Git - thirdparty/glibc.git/blame - hurd/hurdexec.c
Update copyright dates with scripts/update-copyrights.
[thirdparty/glibc.git] / hurd / hurdexec.c
CommitLineData
d614a753 1/* Copyright (C) 1991-2020 Free Software Foundation, Inc.
c84142e8 2 This file is part of the GNU C Library.
28f540f4 3
c84142e8 4 The GNU C Library is free software; you can redistribute it and/or
41bdb6e2
AJ
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.
28f540f4 8
c84142e8
UD
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
41bdb6e2 12 Lesser General Public License for more details.
28f540f4 13
41bdb6e2 14 You should have received a copy of the GNU Lesser General Public
59ba27a6 15 License along with the GNU C Library; if not, see
5a82c748 16 <https://www.gnu.org/licenses/>. */
28f540f4
RM
17
18#include <errno.h>
19#include <unistd.h>
20#include <fcntl.h>
21#include <limits.h>
22#include <stdlib.h>
23#include <string.h>
24#include <hurd.h>
25#include <hurd/fd.h>
26#include <hurd/signal.h>
98e1c93a 27#include <hurd/id.h>
8f0c527e 28#include <assert.h>
75cd5204 29#include <argz.h>
28f540f4
RM
30
31/* Overlay TASK, executing FILE with arguments ARGV and environment ENVP.
32 If TASK == mach_task_self (), some ports are dealloc'd by the exec server.
311ba8dc
ST
33 ARGV and ENVP are terminated by NULL pointers.
34 Deprecated: use _hurd_exec_paths instead. */
28f540f4 35error_t
75914335 36_hurd_exec (task_t task, file_t file,
28f540f4 37 char *const argv[], char *const envp[])
311ba8dc
ST
38{
39 return _hurd_exec_paths (task, file, NULL, NULL, argv, envp);
40}
41
42link_warning (_hurd_exec,
43 "_hurd_exec is deprecated, use _hurd_exec_paths instead");
44
45/* Overlay TASK, executing FILE with arguments ARGV and environment ENVP.
46 If TASK == mach_task_self (), some ports are dealloc'd by the exec server.
47 ARGV and ENVP are terminated by NULL pointers. PATH is the relative path to
48 FILE and ABSPATH is the absolute path to FILE. Passing NULL, though possible,
49 should be avoided, since then the exec server may not know the path to
50 FILE if FILE is a script, and will then pass /dev/fd/N to the
51 interpreter. */
52error_t
53_hurd_exec_paths (task_t task, file_t file,
54 const char *path, const char *abspath,
55 char *const argv[], char *const envp[])
28f540f4
RM
56{
57 error_t err;
8f0c527e 58 char *args, *env;
28f540f4
RM
59 size_t argslen, envlen;
60 int ints[INIT_INT_MAX];
61 mach_port_t ports[_hurd_nports];
62 struct hurd_userlink ulink_ports[_hurd_nports];
98e1c93a
RM
63 inline void free_port (unsigned int i)
64 {
65 _hurd_port_free (&_hurd_ports[i], &ulink_ports[i], ports[i]);
66 }
28f540f4 67 file_t *dtable;
1e9dc039 68 unsigned int dtablesize, i;
28f540f4
RM
69 struct hurd_port **dtable_cells;
70 struct hurd_userlink *ulink_dtable;
28f540f4
RM
71 struct hurd_sigstate *ss;
72 mach_port_t *please_dealloc, *pdp;
98e1c93a 73 int reauth = 0;
28f540f4 74
8f0c527e 75 /* XXX needs to be hurdmalloc XXX */
8c4b8cbc
RM
76 if (argv == NULL)
77 args = NULL, argslen = 0;
78 else if (err = __argz_create (argv, &args, &argslen))
75cd5204 79 return err;
8c4b8cbc
RM
80 if (envp == NULL)
81 env = NULL, envlen = 0;
1d88de3d 82 else if (err = __argz_create (envp, &env, &envlen))
75cd5204 83 goto outargs;
28f540f4
RM
84
85 /* Load up the ports to give to the new program. */
86 for (i = 0; i < _hurd_nports; ++i)
87 if (i == INIT_PORT_PROC && task != __mach_task_self ())
88 {
89 /* This is another task, so we need to ask the proc server
90 for the right proc server port for it. */
91 if (err = __USEPORT (PROC, __proc_task2proc (port, task, &ports[i])))
92 {
93 while (--i > 0)
98e1c93a 94 free_port (i);
75cd5204 95 goto outenv;
28f540f4
RM
96 }
97 }
98 else
99 ports[i] = _hurd_port_get (&_hurd_ports[i], &ulink_ports[i]);
100
101
102 /* Load up the ints to give the new program. */
103 for (i = 0; i < INIT_INT_MAX; ++i)
104 switch (i)
105 {
106 case INIT_UMASK:
107 ints[i] = _hurd_umask;
108 break;
109
110 case INIT_SIGMASK:
111 case INIT_SIGIGN:
112 case INIT_SIGPENDING:
113 /* We will set these all below. */
114 break;
115
fe4d0ab7
MB
116 case INIT_TRACEMASK:
117 ints[i] = _hurdsig_traced;
118 break;
119
28f540f4
RM
120 default:
121 ints[i] = 0;
122 }
123
124 ss = _hurd_self_sigstate ();
8f0c527e
RM
125
126 assert (! __spin_lock_locked (&ss->critical_section_lock));
127 __spin_lock (&ss->critical_section_lock);
128
653d74f1
JK
129 _hurd_sigstate_lock (ss);
130 struct sigaction *actions = _hurd_sigstate_actions (ss);
28f540f4 131 ints[INIT_SIGMASK] = ss->blocked;
653d74f1 132 ints[INIT_SIGPENDING] = _hurd_sigstate_pending (ss);
28f540f4
RM
133 ints[INIT_SIGIGN] = 0;
134 for (i = 1; i < NSIG; ++i)
653d74f1 135 if (actions[i].sa_handler == SIG_IGN)
28f540f4
RM
136 ints[INIT_SIGIGN] |= __sigmask (i);
137
138 /* We hold the sigstate lock until the exec has failed so that no signal
139 can arrive between when we pack the blocked and ignored signals, and
140 when the exec actually happens. A signal handler could change what
141 signals are blocked and ignored. Either the change will be reflected
142 in the exec, or the signal will never be delivered. Setting the
143 critical section flag avoids anything we call trying to acquire the
144 sigstate lock. */
75914335 145
653d74f1 146 _hurd_sigstate_unlock (ss);
28f540f4
RM
147
148 /* Pack up the descriptor table to give the new program. */
149 __mutex_lock (&_hurd_dtable_lock);
150
151 dtablesize = _hurd_dtable ? _hurd_dtablesize : _hurd_init_dtablesize;
152
153 if (task == __mach_task_self ())
154 /* Request the exec server to deallocate some ports from us if the exec
155 succeeds. The init ports and descriptor ports will arrive in the
156 new program's exec_startup message. If we failed to deallocate
157 them, the new program would have duplicate user references for them.
158 But we cannot deallocate them ourselves, because we must still have
159 them after a failed exec call. */
98e1c93a 160 please_dealloc = __alloca ((_hurd_nports + 3 + (3 * dtablesize))
28f540f4
RM
161 * sizeof (mach_port_t));
162 else
163 please_dealloc = NULL;
164 pdp = please_dealloc;
165
166 if (_hurd_dtable != NULL)
167 {
168 dtable = __alloca (dtablesize * sizeof (dtable[0]));
169 ulink_dtable = __alloca (dtablesize * sizeof (ulink_dtable[0]));
170 dtable_cells = __alloca (dtablesize * sizeof (dtable_cells[0]));
171 for (i = 0; i < dtablesize; ++i)
172 {
173 struct hurd_fd *const d = _hurd_dtable[i];
174 if (d == NULL)
175 {
176 dtable[i] = MACH_PORT_NULL;
177 continue;
178 }
179 __spin_lock (&d->port.lock);
180 if (d->flags & FD_CLOEXEC)
181 {
182 /* This descriptor is marked to be closed on exec.
183 So don't pass it to the new program. */
184 dtable[i] = MACH_PORT_NULL;
185 if (pdp && d->port.port != MACH_PORT_NULL)
186 {
187 /* We still need to deallocate the ports. */
188 *pdp++ = d->port.port;
189 if (d->ctty.port != MACH_PORT_NULL)
190 *pdp++ = d->ctty.port;
191 }
192 __spin_unlock (&d->port.lock);
193 }
194 else
195 {
196 if (pdp && d->ctty.port != MACH_PORT_NULL)
197 /* All the elements of DTABLE are added to PLEASE_DEALLOC
198 below, so we needn't add the port itself.
199 But we must deallocate the ctty port as well as
200 the normal port that got installed in DTABLE[I]. */
201 *pdp++ = d->ctty.port;
202 dtable[i] = _hurd_port_locked_get (&d->port, &ulink_dtable[i]);
203 dtable_cells[i] = &d->port;
204 }
205 }
206 }
207 else
208 {
209 dtable = _hurd_init_dtable;
210 ulink_dtable = NULL;
211 dtable_cells = NULL;
212 }
213
57d58860 214 /* Prune trailing null ports from the descriptor table. */
21bc421f 215 while (dtablesize > 0 && dtable[dtablesize - 1] == MACH_PORT_NULL)
57d58860
RM
216 --dtablesize;
217
98e1c93a
RM
218 /* See if we need to diddle the auth port of the new program.
219 The purpose of this is to get the effect setting the saved-set UID and
220 GID to the respective effective IDs after the exec, as POSIX.1 requires.
221 Note that we don't reauthenticate with the proc server; that would be a
222 no-op since it only keeps track of the effective UIDs, and if it did
223 keep track of the available IDs we would have the problem that we'd be
224 changing the IDs before the exec and have to change them back after a
225 failure. Arguably we could skip all the reauthentications because the
226 available IDs have no bearing on any filesystem. But the conservative
227 approach is to reauthenticate all the io ports so that no state anywhere
228 reflects that our whole ID set differs from what we've set it to. */
229 __mutex_lock (&_hurd_id.lock);
230 err = _hurd_check_ids ();
231 if (err == 0 && ((_hurd_id.aux.nuids >= 2 && _hurd_id.gen.nuids >= 1
232 && _hurd_id.aux.uids[1] != _hurd_id.gen.uids[0])
233 || (_hurd_id.aux.ngids >= 2 && _hurd_id.gen.ngids >= 1
234 && _hurd_id.aux.gids[1] != _hurd_id.gen.gids[0])))
235 {
236 /* We have euid != svuid or egid != svgid. POSIX.1 says that exec
237 sets svuid = euid and svgid = egid. So we must get a new auth
238 port and reauthenticate everything with it. We'll pass the new
311ba8dc 239 ports in file_exec_paths instead of our own ports. */
28f540f4 240
98e1c93a 241 auth_t newauth;
20d13812 242
98e1c93a
RM
243 _hurd_id.aux.uids[1] = _hurd_id.gen.uids[0];
244 _hurd_id.aux.gids[1] = _hurd_id.gen.gids[0];
245 _hurd_id.valid = 0;
246 if (_hurd_id.rid_auth != MACH_PORT_NULL)
247 {
248 __mach_port_deallocate (__mach_task_self (), _hurd_id.rid_auth);
249 _hurd_id.rid_auth = MACH_PORT_NULL;
250 }
251
252 err = __auth_makeauth (ports[INIT_PORT_AUTH],
253 NULL, MACH_MSG_TYPE_COPY_SEND, 0,
254 _hurd_id.gen.uids, _hurd_id.gen.nuids,
255 _hurd_id.aux.uids, _hurd_id.aux.nuids,
256 _hurd_id.gen.gids, _hurd_id.gen.ngids,
257 _hurd_id.aux.gids, _hurd_id.aux.ngids,
258 &newauth);
259 if (err == 0)
260 {
261 /* Now we have to reauthenticate the ports with this new ID.
262 */
263
264 inline error_t reauth_io (io_t port, io_t *newport)
265 {
266 mach_port_t ref = __mach_reply_port ();
267 *newport = MACH_PORT_NULL;
268 error_t err = __io_reauthenticate (port,
269 ref, MACH_MSG_TYPE_MAKE_SEND);
270 if (!err)
271 err = __auth_user_authenticate (newauth,
272 ref, MACH_MSG_TYPE_MAKE_SEND,
273 newport);
274 __mach_port_destroy (__mach_task_self (), ref);
275 return err;
276 }
277 inline void reauth_port (unsigned int idx)
278 {
279 io_t newport;
280 err = reauth_io (ports[idx], &newport) ?: err;
281 if (pdp)
282 *pdp++ = ports[idx]; /* XXX presumed still in _hurd_ports */
283 free_port (idx);
284 ports[idx] = newport;
285 }
286
287 if (pdp)
288 *pdp++ = ports[INIT_PORT_AUTH];
289 free_port (INIT_PORT_AUTH);
290 ports[INIT_PORT_AUTH] = newauth;
291
292 reauth_port (INIT_PORT_CRDIR);
293 reauth_port (INIT_PORT_CWDIR);
294
295 if (!err)
296 {
297 /* Now we'll reauthenticate each file descriptor. */
298 if (ulink_dtable == NULL)
299 {
300 assert (dtable == _hurd_init_dtable);
301 dtable = __alloca (dtablesize * sizeof (dtable[0]));
302 for (i = 0; i < dtablesize; ++i)
303 if (_hurd_init_dtable[i] != MACH_PORT_NULL)
304 {
305 if (pdp)
306 *pdp++ = _hurd_init_dtable[i];
307 err = reauth_io (_hurd_init_dtable[i], &dtable[i]);
308 if (err)
309 {
310 while (++i < dtablesize)
311 dtable[i] = MACH_PORT_NULL;
312 break;
313 }
314 }
315 else
316 dtable[i] = MACH_PORT_NULL;
317 }
318 else
319 {
320 if (pdp)
321 {
322 /* Ask to deallocate all the old fd ports,
323 since we will have new ones in DTABLE. */
324 memcpy (pdp, dtable, dtablesize * sizeof pdp[0]);
325 pdp += dtablesize;
326 }
327 for (i = 0; i < dtablesize; ++i)
328 if (dtable[i] != MACH_PORT_NULL)
329 {
330 io_t newport;
331 err = reauth_io (dtable[i], &newport);
332 _hurd_port_free (dtable_cells[i], &ulink_dtable[i],
333 dtable[i]);
334 dtable[i] = newport;
335 if (err)
336 {
337 while (++i < dtablesize)
338 _hurd_port_free (dtable_cells[i],
339 &ulink_dtable[i], dtable[i]);
340 break;
341 }
342 }
343 ulink_dtable = NULL;
344 dtable_cells = NULL;
345 }
346 }
347 }
348
349 reauth = 1;
350 }
351 __mutex_unlock (&_hurd_id.lock);
352
353 /* The information is all set up now. Try to exec the file. */
354 if (!err)
355 {
356 int flags;
28f540f4 357
98e1c93a
RM
358 if (pdp)
359 {
360 /* Request the exec server to deallocate some ports from us if
361 the exec succeeds. The init ports and descriptor ports will
362 arrive in the new program's exec_startup message. If we
363 failed to deallocate them, the new program would have
364 duplicate user references for them. But we cannot deallocate
365 them ourselves, because we must still have them after a failed
366 exec call. */
367
368 for (i = 0; i < _hurd_nports; ++i)
369 *pdp++ = ports[i];
370 for (i = 0; i < dtablesize; ++i)
371 *pdp++ = dtable[i];
372 }
373
374 flags = 0;
20d13812 375#ifdef EXEC_SIGTRAP
98e1c93a
RM
376 /* PTRACE_TRACEME sets all bits in _hurdsig_traced, which is
377 propagated through exec by INIT_TRACEMASK, so this checks if
378 PTRACE_TRACEME has been called in this process in any of its
379 current or prior lives. */
380 if (__sigismember (&_hurdsig_traced, SIGKILL))
381 flags |= EXEC_SIGTRAP;
20d13812 382#endif
311ba8dc
ST
383 err = __file_exec_paths (file, task, flags,
384 path ? path : "",
385 abspath ? abspath : "",
386 args, argslen, env, envlen,
387 dtable, MACH_MSG_TYPE_COPY_SEND, dtablesize,
388 ports, MACH_MSG_TYPE_COPY_SEND,
389 _hurd_nports,
390 ints, INIT_INT_MAX,
391 please_dealloc, pdp - please_dealloc,
392 &_hurd_msgport,
393 task == __mach_task_self () ? 1 : 0);
394 /* Fall back for backwards compatibility. This can just be removed
395 when __file_exec goes away. */
396 if (err == MIG_BAD_ID)
397 err = __file_exec (file, task, flags,
398 args, argslen, env, envlen,
399 dtable, MACH_MSG_TYPE_COPY_SEND, dtablesize,
400 ports, MACH_MSG_TYPE_COPY_SEND, _hurd_nports,
401 ints, INIT_INT_MAX,
402 please_dealloc, pdp - please_dealloc,
403 &_hurd_msgport,
404 task == __mach_task_self () ? 1 : 0);
98e1c93a 405 }
28f540f4
RM
406
407 /* Release references to the standard ports. */
408 for (i = 0; i < _hurd_nports; ++i)
98e1c93a
RM
409 if ((i == INIT_PORT_PROC && task != __mach_task_self ())
410 || (reauth && (i == INIT_PORT_AUTH
411 || i == INIT_PORT_CRDIR || i == INIT_PORT_CWDIR)))
28f540f4
RM
412 __mach_port_deallocate (__mach_task_self (), ports[i]);
413 else
98e1c93a 414 free_port (i);
28f540f4 415
98e1c93a 416 /* Release references to the file descriptor ports. */
28f540f4 417 if (ulink_dtable != NULL)
98e1c93a
RM
418 {
419 for (i = 0; i < dtablesize; ++i)
420 if (dtable[i] != MACH_PORT_NULL)
421 _hurd_port_free (dtable_cells[i], &ulink_dtable[i], dtable[i]);
422 }
423 else if (dtable && dtable != _hurd_init_dtable)
28f540f4 424 for (i = 0; i < dtablesize; ++i)
98e1c93a 425 __mach_port_deallocate (__mach_task_self (), dtable[i]);
28f540f4
RM
426
427 /* Release lock on the file descriptor table. */
428 __mutex_unlock (&_hurd_dtable_lock);
429
430 /* Safe to let signals happen now. */
8f0c527e 431 _hurd_critical_section_unlock (ss);
28f540f4 432
75cd5204
RM
433 outargs:
434 free (args);
435 outenv:
436 free (env);
28f540f4
RM
437 return err;
438}
7a8f45e3 439libc_hidden_def (_hurd_exec_paths)