2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include "namespace.h"
30 #include <sys/param.h>
31 #include <sys/queue.h>
42 #include "un-namespace.h"
43 #include "libc_private.h"
45 struct __posix_spawnattr {
48 struct sched_param sa_schedparam;
50 sigset_t sa_sigdefault;
54 struct __posix_spawn_file_actions {
55 STAILQ_HEAD(, __posix_spawn_file_actions_entry) fa_list;
58 typedef struct __posix_spawn_file_actions_entry {
59 STAILQ_ENTRY(__posix_spawn_file_actions_entry) fae_list;
73 #define fae_path fae_data.open.path
75 #define fae_oflag fae_data.open.oflag
77 #define fae_mode fae_data.open.mode
81 #define fae_newfildes fae_data.dup2.newfildes
84 } posix_spawn_file_actions_entry_t;
91 process_spawnattr(const posix_spawnattr_t sa)
93 struct sigaction sigact = { .sa_flags = 0, .sa_handler = SIG_DFL };
97 * POSIX doesn't really describe in which order everything
98 * should be set. We'll just set them in the order in which they
102 /* Set process group */
103 if (sa->sa_flags & POSIX_SPAWN_SETPGROUP) {
104 if (setpgid(0, sa->sa_pgroup) != 0)
108 /* Set scheduler policy */
109 if (sa->sa_flags & POSIX_SPAWN_SETSCHEDULER) {
110 if (sched_setscheduler(0, sa->sa_schedpolicy,
111 &sa->sa_schedparam) != 0)
113 } else if (sa->sa_flags & POSIX_SPAWN_SETSCHEDPARAM) {
114 if (sched_setparam(0, &sa->sa_schedparam) != 0)
118 /* Reset user ID's */
119 if (sa->sa_flags & POSIX_SPAWN_RESETIDS) {
120 if (setegid(getgid()) != 0)
122 if (seteuid(getuid()) != 0)
127 * Set signal masks/defaults.
128 * Use unwrapped syscall, libthr is in undefined state after vfork().
130 if (sa->sa_flags & POSIX_SPAWN_SETSIGMASK) {
131 __sys_sigprocmask(SIG_SETMASK, &sa->sa_sigmask, NULL);
134 if (sa->sa_flags & POSIX_SPAWN_SETSIGDEF) {
135 for (i = 1; i <= _SIG_MAXSIG; i++) {
136 if (sigismember(&sa->sa_sigdefault, i))
137 if (__sys_sigaction(i, &sigact, NULL) != 0)
146 process_file_actions_entry(posix_spawn_file_actions_entry_t *fae)
150 switch (fae->fae_action) {
152 /* Perform an open(), make it use the right fd */
153 fd = _open(fae->fae_path, fae->fae_oflag, fae->fae_mode);
156 if (fd != fae->fae_fildes) {
157 if (_dup2(fd, fae->fae_fildes) == -1) {
160 return (saved_errno);
162 if (_close(fd) != 0) {
167 if (_fcntl(fae->fae_fildes, F_SETFD, 0) == -1)
171 /* Perform a dup2() */
172 if (_dup2(fae->fae_fildes, fae->fae_newfildes) == -1)
174 if (_fcntl(fae->fae_newfildes, F_SETFD, 0) == -1)
178 /* Perform a close(), do not fail if already closed */
179 (void)_close(fae->fae_fildes);
182 if (chdir(fae->fae_path) != 0)
186 if (fchdir(fae->fae_fildes) != 0)
190 closefrom(fae->fae_fildes);
197 process_file_actions(const posix_spawn_file_actions_t fa)
199 posix_spawn_file_actions_entry_t *fae;
202 /* Replay all file descriptor modifications */
203 STAILQ_FOREACH(fae, &fa->fa_list, fae_list) {
204 error = process_file_actions_entry(fae);
211 struct posix_spawn_args {
213 const posix_spawn_file_actions_t *fa;
214 const posix_spawnattr_t *sa;
221 #define PSPAWN_STACK_ALIGNMENT 16
222 #define PSPAWN_STACK_ALIGNBYTES (PSPAWN_STACK_ALIGNMENT - 1)
223 #define PSPAWN_STACK_ALIGN(sz) \
224 (((sz) + PSPAWN_STACK_ALIGNBYTES) & ~PSPAWN_STACK_ALIGNBYTES)
226 #if defined(__i386__) || defined(__amd64__)
228 * Below we'll assume that _RFORK_THREAD_STACK_SIZE is appropriately aligned for
229 * the posix_spawn() case where we do not end up calling execvpe and won't ever
230 * try to allocate space on the stack for argv[].
232 #define _RFORK_THREAD_STACK_SIZE 4096
233 _Static_assert((_RFORK_THREAD_STACK_SIZE % PSPAWN_STACK_ALIGNMENT) == 0,
234 "Inappropriate stack size alignment");
238 _posix_spawn_thr(void *data)
240 struct posix_spawn_args *psa;
244 if (psa->sa != NULL) {
245 psa->error = process_spawnattr(*psa->sa);
249 if (psa->fa != NULL) {
250 psa->error = process_file_actions(*psa->fa);
254 envp = psa->envp != NULL ? psa->envp : environ;
255 if (psa->use_env_path)
256 execvpe(psa->path, psa->argv, envp);
258 _execve(psa->path, psa->argv, envp);
261 /* This is called in such a way that it must not exit. */
266 do_posix_spawn(pid_t *pid, const char *path,
267 const posix_spawn_file_actions_t *fa,
268 const posix_spawnattr_t *sa,
269 char * const argv[], char * const envp[], int use_env_path)
271 struct posix_spawn_args psa;
273 #ifdef _RFORK_THREAD_STACK_SIZE
277 stacksz = _RFORK_THREAD_STACK_SIZE;
280 * We need to make sure we have enough room on the stack for the
281 * potential alloca() in execvPe if it gets kicked back an
282 * ENOEXEC from execve(2), plus the original buffer we gave
283 * ourselves; this protects us in the event that the caller
284 * intentionally or inadvertently supplies enough arguments to
285 * make us blow past the stack we've allocated from it.
287 for (cnt = 0; argv[cnt] != NULL; ++cnt)
289 stacksz += MAX(3, cnt + 2) * sizeof(char *);
290 stacksz = PSPAWN_STACK_ALIGN(stacksz);
294 * aligned_alloc is not safe to use here, because we can't guarantee
295 * that aligned_alloc and free will be provided by the same
296 * implementation. We've actively hit at least one application that
297 * will provide its own malloc/free but not aligned_alloc leading to
298 * a free by the wrong allocator.
300 stack = malloc(stacksz);
303 stacksz = (((uintptr_t)stack + stacksz) & ~PSPAWN_STACK_ALIGNBYTES) -
311 psa.use_env_path = use_env_path;
315 * Passing RFSPAWN to rfork(2) gives us effectively a vfork that drops
316 * non-ignored signal handlers. We'll fall back to the slightly less
317 * ideal vfork(2) if we get an EINVAL from rfork -- this should only
318 * happen with newer libc on older kernel that doesn't accept
321 #ifdef _RFORK_THREAD_STACK_SIZE
323 * x86 stores the return address on the stack, so rfork(2) cannot work
324 * as-is because the child would clobber the return address om the
325 * parent. Because of this, we must use rfork_thread instead while
326 * almost every other arch stores the return address in a register.
328 p = rfork_thread(RFSPAWN, stack + stacksz, _posix_spawn_thr, &psa);
333 /* _posix_spawn_thr does not return */
334 _posix_spawn_thr(&psa);
337 * The above block should leave us in a state where we've either
338 * succeeded and we're ready to process the results, or we need to
339 * fallback to vfork() if the kernel didn't like RFSPAWN.
342 if (p == -1 && errno == EINVAL) {
345 /* _posix_spawn_thr does not return */
346 _posix_spawn_thr(&psa);
351 /* Failed; ready to reap */
352 _waitpid(p, NULL, WNOHANG);
353 else if (pid != NULL)
360 posix_spawn(pid_t *pid, const char *path,
361 const posix_spawn_file_actions_t *fa,
362 const posix_spawnattr_t *sa,
363 char * const argv[], char * const envp[])
365 return (do_posix_spawn(pid, path, fa, sa, argv, envp, 0));
369 posix_spawnp(pid_t *pid, const char *path,
370 const posix_spawn_file_actions_t *fa,
371 const posix_spawnattr_t *sa,
372 char * const argv[], char * const envp[])
374 return (do_posix_spawn(pid, path, fa, sa, argv, envp, 1));
378 * File descriptor actions
382 posix_spawn_file_actions_init(posix_spawn_file_actions_t *ret)
384 posix_spawn_file_actions_t fa;
386 fa = malloc(sizeof(struct __posix_spawn_file_actions));
390 STAILQ_INIT(&fa->fa_list);
396 posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *fa)
398 posix_spawn_file_actions_entry_t *fae;
400 while ((fae = STAILQ_FIRST(&(*fa)->fa_list)) != NULL) {
401 /* Remove file action entry from the queue */
402 STAILQ_REMOVE_HEAD(&(*fa)->fa_list, fae_list);
404 /* Deallocate file action entry */
405 if (fae->fae_action == FAE_OPEN ||
406 fae->fae_action == FAE_CHDIR)
416 posix_spawn_file_actions_addopen(posix_spawn_file_actions_t * __restrict fa,
417 int fildes, const char * __restrict path, int oflag, mode_t mode)
419 posix_spawn_file_actions_entry_t *fae;
425 /* Allocate object */
426 fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
430 /* Set values and store in queue */
431 fae->fae_action = FAE_OPEN;
432 fae->fae_path = strdup(path);
433 if (fae->fae_path == NULL) {
438 fae->fae_fildes = fildes;
439 fae->fae_oflag = oflag;
440 fae->fae_mode = mode;
442 STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
447 posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *fa,
448 int fildes, int newfildes)
450 posix_spawn_file_actions_entry_t *fae;
452 if (fildes < 0 || newfildes < 0)
455 /* Allocate object */
456 fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
460 /* Set values and store in queue */
461 fae->fae_action = FAE_DUP2;
462 fae->fae_fildes = fildes;
463 fae->fae_newfildes = newfildes;
465 STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
470 posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *fa,
473 posix_spawn_file_actions_entry_t *fae;
478 /* Allocate object */
479 fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
483 /* Set values and store in queue */
484 fae->fae_action = FAE_CLOSE;
485 fae->fae_fildes = fildes;
487 STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
492 posix_spawn_file_actions_addchdir_np(posix_spawn_file_actions_t *
493 __restrict fa, const char *__restrict path)
495 posix_spawn_file_actions_entry_t *fae;
498 fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
502 fae->fae_action = FAE_CHDIR;
503 fae->fae_path = strdup(path);
504 if (fae->fae_path == NULL) {
510 STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
515 posix_spawn_file_actions_addfchdir_np(posix_spawn_file_actions_t *__restrict fa,
518 posix_spawn_file_actions_entry_t *fae;
523 /* Allocate object */
524 fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
528 fae->fae_action = FAE_FCHDIR;
529 fae->fae_fildes = fildes;
531 STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
536 posix_spawn_file_actions_addclosefrom_np (posix_spawn_file_actions_t *
537 __restrict fa, int from)
539 posix_spawn_file_actions_entry_t *fae;
544 /* Allocate object */
545 fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
549 fae->fae_action = FAE_CLOSEFROM;
550 fae->fae_fildes = from;
552 STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
561 posix_spawnattr_init(posix_spawnattr_t *ret)
563 posix_spawnattr_t sa;
565 sa = calloc(1, sizeof(struct __posix_spawnattr));
569 /* Set defaults as specified by POSIX, cleared above */
575 posix_spawnattr_destroy(posix_spawnattr_t *sa)
582 posix_spawnattr_getflags(const posix_spawnattr_t * __restrict sa,
583 short * __restrict flags)
585 *flags = (*sa)->sa_flags;
590 posix_spawnattr_getpgroup(const posix_spawnattr_t * __restrict sa,
591 pid_t * __restrict pgroup)
593 *pgroup = (*sa)->sa_pgroup;
598 posix_spawnattr_getschedparam(const posix_spawnattr_t * __restrict sa,
599 struct sched_param * __restrict schedparam)
601 *schedparam = (*sa)->sa_schedparam;
606 posix_spawnattr_getschedpolicy(const posix_spawnattr_t * __restrict sa,
607 int * __restrict schedpolicy)
609 *schedpolicy = (*sa)->sa_schedpolicy;
614 posix_spawnattr_getsigdefault(const posix_spawnattr_t * __restrict sa,
615 sigset_t * __restrict sigdefault)
617 *sigdefault = (*sa)->sa_sigdefault;
622 posix_spawnattr_getsigmask(const posix_spawnattr_t * __restrict sa,
623 sigset_t * __restrict sigmask)
625 *sigmask = (*sa)->sa_sigmask;
630 posix_spawnattr_setflags(posix_spawnattr_t *sa, short flags)
632 (*sa)->sa_flags = flags;
637 posix_spawnattr_setpgroup(posix_spawnattr_t *sa, pid_t pgroup)
639 (*sa)->sa_pgroup = pgroup;
644 posix_spawnattr_setschedparam(posix_spawnattr_t * __restrict sa,
645 const struct sched_param * __restrict schedparam)
647 (*sa)->sa_schedparam = *schedparam;
652 posix_spawnattr_setschedpolicy(posix_spawnattr_t *sa, int schedpolicy)
654 (*sa)->sa_schedpolicy = schedpolicy;
659 posix_spawnattr_setsigdefault(posix_spawnattr_t * __restrict sa,
660 const sigset_t * __restrict sigdefault)
662 (*sa)->sa_sigdefault = *sigdefault;
667 posix_spawnattr_setsigmask(posix_spawnattr_t * __restrict sa,
668 const sigset_t * __restrict sigmask)
670 (*sa)->sa_sigmask = *sigmask;