xref: /freebsd/lib/libc/gen/posix_spawn.c (revision 7029da5c36f2d3cf6bb6c81bf551229f416399e8)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
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.
15  *
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
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include "namespace.h"
33 #include <sys/queue.h>
34 #include <sys/wait.h>
35 
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <sched.h>
39 #include <spawn.h>
40 #include <signal.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include "un-namespace.h"
45 #include "libc_private.h"
46 
47 extern char **environ;
48 
49 struct __posix_spawnattr {
50 	short			sa_flags;
51 	pid_t			sa_pgroup;
52 	struct sched_param	sa_schedparam;
53 	int			sa_schedpolicy;
54 	sigset_t		sa_sigdefault;
55 	sigset_t		sa_sigmask;
56 };
57 
58 struct __posix_spawn_file_actions {
59 	STAILQ_HEAD(, __posix_spawn_file_actions_entry) fa_list;
60 };
61 
62 typedef struct __posix_spawn_file_actions_entry {
63 	STAILQ_ENTRY(__posix_spawn_file_actions_entry) fae_list;
64 	enum { FAE_OPEN, FAE_DUP2, FAE_CLOSE } fae_action;
65 
66 	int fae_fildes;
67 	union {
68 		struct {
69 			char *path;
70 #define fae_path	fae_data.open.path
71 			int oflag;
72 #define fae_oflag	fae_data.open.oflag
73 			mode_t mode;
74 #define fae_mode	fae_data.open.mode
75 		} open;
76 		struct {
77 			int newfildes;
78 #define fae_newfildes	fae_data.dup2.newfildes
79 		} dup2;
80 	} fae_data;
81 } posix_spawn_file_actions_entry_t;
82 
83 /*
84  * Spawn routines
85  */
86 
87 static int
88 process_spawnattr(const posix_spawnattr_t sa)
89 {
90 	struct sigaction sigact = { .sa_flags = 0, .sa_handler = SIG_DFL };
91 	int i;
92 
93 	/*
94 	 * POSIX doesn't really describe in which order everything
95 	 * should be set. We'll just set them in the order in which they
96 	 * are mentioned.
97 	 */
98 
99 	/* Set process group */
100 	if (sa->sa_flags & POSIX_SPAWN_SETPGROUP) {
101 		if (setpgid(0, sa->sa_pgroup) != 0)
102 			return (errno);
103 	}
104 
105 	/* Set scheduler policy */
106 	if (sa->sa_flags & POSIX_SPAWN_SETSCHEDULER) {
107 		if (sched_setscheduler(0, sa->sa_schedpolicy,
108 		    &sa->sa_schedparam) != 0)
109 			return (errno);
110 	} else if (sa->sa_flags & POSIX_SPAWN_SETSCHEDPARAM) {
111 		if (sched_setparam(0, &sa->sa_schedparam) != 0)
112 			return (errno);
113 	}
114 
115 	/* Reset user ID's */
116 	if (sa->sa_flags & POSIX_SPAWN_RESETIDS) {
117 		if (setegid(getgid()) != 0)
118 			return (errno);
119 		if (seteuid(getuid()) != 0)
120 			return (errno);
121 	}
122 
123 	/*
124 	 * Set signal masks/defaults.
125 	 * Use unwrapped syscall, libthr is in undefined state after vfork().
126 	 */
127 	if (sa->sa_flags & POSIX_SPAWN_SETSIGMASK) {
128 		__sys_sigprocmask(SIG_SETMASK, &sa->sa_sigmask, NULL);
129 	}
130 
131 	if (sa->sa_flags & POSIX_SPAWN_SETSIGDEF) {
132 		for (i = 1; i <= _SIG_MAXSIG; i++) {
133 			if (sigismember(&sa->sa_sigdefault, i))
134 				if (__sys_sigaction(i, &sigact, NULL) != 0)
135 					return (errno);
136 		}
137 	}
138 
139 	return (0);
140 }
141 
142 static int
143 process_file_actions_entry(posix_spawn_file_actions_entry_t *fae)
144 {
145 	int fd, saved_errno;
146 
147 	switch (fae->fae_action) {
148 	case FAE_OPEN:
149 		/* Perform an open(), make it use the right fd */
150 		fd = _open(fae->fae_path, fae->fae_oflag, fae->fae_mode);
151 		if (fd < 0)
152 			return (errno);
153 		if (fd != fae->fae_fildes) {
154 			if (_dup2(fd, fae->fae_fildes) == -1) {
155 				saved_errno = errno;
156 				(void)_close(fd);
157 				return (saved_errno);
158 			}
159 			if (_close(fd) != 0) {
160 				if (errno == EBADF)
161 					return (EBADF);
162 			}
163 		}
164 		if (_fcntl(fae->fae_fildes, F_SETFD, 0) == -1)
165 			return (errno);
166 		break;
167 	case FAE_DUP2:
168 		/* Perform a dup2() */
169 		if (_dup2(fae->fae_fildes, fae->fae_newfildes) == -1)
170 			return (errno);
171 		if (_fcntl(fae->fae_newfildes, F_SETFD, 0) == -1)
172 			return (errno);
173 		break;
174 	case FAE_CLOSE:
175 		/* Perform a close(), do not fail if already closed */
176 		(void)_close(fae->fae_fildes);
177 		break;
178 	}
179 	return (0);
180 }
181 
182 static int
183 process_file_actions(const posix_spawn_file_actions_t fa)
184 {
185 	posix_spawn_file_actions_entry_t *fae;
186 	int error;
187 
188 	/* Replay all file descriptor modifications */
189 	STAILQ_FOREACH(fae, &fa->fa_list, fae_list) {
190 		error = process_file_actions_entry(fae);
191 		if (error)
192 			return (error);
193 	}
194 	return (0);
195 }
196 
197 struct posix_spawn_args {
198 	const char *path;
199 	const posix_spawn_file_actions_t *fa;
200 	const posix_spawnattr_t *sa;
201 	char * const * argv;
202 	char * const * envp;
203 	int use_env_path;
204 	volatile int error;
205 };
206 
207 #if defined(__i386__) || defined(__amd64__)
208 #define	_RFORK_THREAD_STACK_SIZE	4096
209 #endif
210 
211 static int
212 _posix_spawn_thr(void *data)
213 {
214 	struct posix_spawn_args *psa;
215 	char * const *envp;
216 
217 	psa = data;
218 	if (psa->sa != NULL) {
219 		psa->error = process_spawnattr(*psa->sa);
220 		if (psa->error)
221 			_exit(127);
222 	}
223 	if (psa->fa != NULL) {
224 		psa->error = process_file_actions(*psa->fa);
225 		if (psa->error)
226 			_exit(127);
227 	}
228 	envp = psa->envp != NULL ? psa->envp : environ;
229 	if (psa->use_env_path)
230 		_execvpe(psa->path, psa->argv, envp);
231 	else
232 		_execve(psa->path, psa->argv, envp);
233 	psa->error = errno;
234 
235 	/* This is called in such a way that it must not exit. */
236 	_exit(127);
237 }
238 
239 static int
240 do_posix_spawn(pid_t *pid, const char *path,
241     const posix_spawn_file_actions_t *fa,
242     const posix_spawnattr_t *sa,
243     char * const argv[], char * const envp[], int use_env_path)
244 {
245 	struct posix_spawn_args psa;
246 	pid_t p;
247 #ifdef _RFORK_THREAD_STACK_SIZE
248 	char *stack;
249 
250 	stack = malloc(_RFORK_THREAD_STACK_SIZE);
251 	if (stack == NULL)
252 		return (ENOMEM);
253 #endif
254 	psa.path = path;
255 	psa.fa = fa;
256 	psa.sa = sa;
257 	psa.argv = argv;
258 	psa.envp = envp;
259 	psa.use_env_path = use_env_path;
260 	psa.error = 0;
261 
262 	/*
263 	 * Passing RFSPAWN to rfork(2) gives us effectively a vfork that drops
264 	 * non-ignored signal handlers.  We'll fall back to the slightly less
265 	 * ideal vfork(2) if we get an EINVAL from rfork -- this should only
266 	 * happen with newer libc on older kernel that doesn't accept
267 	 * RFSPAWN.
268 	 */
269 #ifdef _RFORK_THREAD_STACK_SIZE
270 	/*
271 	 * x86 stores the return address on the stack, so rfork(2) cannot work
272 	 * as-is because the child would clobber the return address om the
273 	 * parent.  Because of this, we must use rfork_thread instead while
274 	 * almost every other arch stores the return address in a register.
275 	 */
276 	p = rfork_thread(RFSPAWN, stack + _RFORK_THREAD_STACK_SIZE,
277 	    _posix_spawn_thr, &psa);
278 	free(stack);
279 #else
280 	p = rfork(RFSPAWN);
281 	if (p == 0)
282 		/* _posix_spawn_thr does not return */
283 		_posix_spawn_thr(&psa);
284 #endif
285 	/*
286 	 * The above block should leave us in a state where we've either
287 	 * succeeded and we're ready to process the results, or we need to
288 	 * fallback to vfork() if the kernel didn't like RFSPAWN.
289 	 */
290 
291 	if (p == -1 && errno == EINVAL) {
292 		p = vfork();
293 		if (p == 0)
294 			/* _posix_spawn_thr does not return */
295 			_posix_spawn_thr(&psa);
296 	}
297 	if (p == -1)
298 		return (errno);
299 	if (psa.error != 0)
300 		/* Failed; ready to reap */
301 		_waitpid(p, NULL, WNOHANG);
302 	else if (pid != NULL)
303 		/* exec succeeded */
304 		*pid = p;
305 	return (psa.error);
306 }
307 
308 int
309 posix_spawn(pid_t *pid, const char *path,
310     const posix_spawn_file_actions_t *fa,
311     const posix_spawnattr_t *sa,
312     char * const argv[], char * const envp[])
313 {
314 	return do_posix_spawn(pid, path, fa, sa, argv, envp, 0);
315 }
316 
317 int
318 posix_spawnp(pid_t *pid, const char *path,
319     const posix_spawn_file_actions_t *fa,
320     const posix_spawnattr_t *sa,
321     char * const argv[], char * const envp[])
322 {
323 	return do_posix_spawn(pid, path, fa, sa, argv, envp, 1);
324 }
325 
326 /*
327  * File descriptor actions
328  */
329 
330 int
331 posix_spawn_file_actions_init(posix_spawn_file_actions_t *ret)
332 {
333 	posix_spawn_file_actions_t fa;
334 
335 	fa = malloc(sizeof(struct __posix_spawn_file_actions));
336 	if (fa == NULL)
337 		return (-1);
338 
339 	STAILQ_INIT(&fa->fa_list);
340 	*ret = fa;
341 	return (0);
342 }
343 
344 int
345 posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *fa)
346 {
347 	posix_spawn_file_actions_entry_t *fae;
348 
349 	while ((fae = STAILQ_FIRST(&(*fa)->fa_list)) != NULL) {
350 		/* Remove file action entry from the queue */
351 		STAILQ_REMOVE_HEAD(&(*fa)->fa_list, fae_list);
352 
353 		/* Deallocate file action entry */
354 		if (fae->fae_action == FAE_OPEN)
355 			free(fae->fae_path);
356 		free(fae);
357 	}
358 
359 	free(*fa);
360 	return (0);
361 }
362 
363 int
364 posix_spawn_file_actions_addopen(posix_spawn_file_actions_t * __restrict fa,
365     int fildes, const char * __restrict path, int oflag, mode_t mode)
366 {
367 	posix_spawn_file_actions_entry_t *fae;
368 	int error;
369 
370 	if (fildes < 0)
371 		return (EBADF);
372 
373 	/* Allocate object */
374 	fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
375 	if (fae == NULL)
376 		return (errno);
377 
378 	/* Set values and store in queue */
379 	fae->fae_action = FAE_OPEN;
380 	fae->fae_path = strdup(path);
381 	if (fae->fae_path == NULL) {
382 		error = errno;
383 		free(fae);
384 		return (error);
385 	}
386 	fae->fae_fildes = fildes;
387 	fae->fae_oflag = oflag;
388 	fae->fae_mode = mode;
389 
390 	STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
391 	return (0);
392 }
393 
394 int
395 posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *fa,
396     int fildes, int newfildes)
397 {
398 	posix_spawn_file_actions_entry_t *fae;
399 
400 	if (fildes < 0 || newfildes < 0)
401 		return (EBADF);
402 
403 	/* Allocate object */
404 	fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
405 	if (fae == NULL)
406 		return (errno);
407 
408 	/* Set values and store in queue */
409 	fae->fae_action = FAE_DUP2;
410 	fae->fae_fildes = fildes;
411 	fae->fae_newfildes = newfildes;
412 
413 	STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
414 	return (0);
415 }
416 
417 int
418 posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *fa,
419     int fildes)
420 {
421 	posix_spawn_file_actions_entry_t *fae;
422 
423 	if (fildes < 0)
424 		return (EBADF);
425 
426 	/* Allocate object */
427 	fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
428 	if (fae == NULL)
429 		return (errno);
430 
431 	/* Set values and store in queue */
432 	fae->fae_action = FAE_CLOSE;
433 	fae->fae_fildes = fildes;
434 
435 	STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
436 	return (0);
437 }
438 
439 /*
440  * Spawn attributes
441  */
442 
443 int
444 posix_spawnattr_init(posix_spawnattr_t *ret)
445 {
446 	posix_spawnattr_t sa;
447 
448 	sa = calloc(1, sizeof(struct __posix_spawnattr));
449 	if (sa == NULL)
450 		return (errno);
451 
452 	/* Set defaults as specified by POSIX, cleared above */
453 	*ret = sa;
454 	return (0);
455 }
456 
457 int
458 posix_spawnattr_destroy(posix_spawnattr_t *sa)
459 {
460 	free(*sa);
461 	return (0);
462 }
463 
464 int
465 posix_spawnattr_getflags(const posix_spawnattr_t * __restrict sa,
466     short * __restrict flags)
467 {
468 	*flags = (*sa)->sa_flags;
469 	return (0);
470 }
471 
472 int
473 posix_spawnattr_getpgroup(const posix_spawnattr_t * __restrict sa,
474     pid_t * __restrict pgroup)
475 {
476 	*pgroup = (*sa)->sa_pgroup;
477 	return (0);
478 }
479 
480 int
481 posix_spawnattr_getschedparam(const posix_spawnattr_t * __restrict sa,
482     struct sched_param * __restrict schedparam)
483 {
484 	*schedparam = (*sa)->sa_schedparam;
485 	return (0);
486 }
487 
488 int
489 posix_spawnattr_getschedpolicy(const posix_spawnattr_t * __restrict sa,
490     int * __restrict schedpolicy)
491 {
492 	*schedpolicy = (*sa)->sa_schedpolicy;
493 	return (0);
494 }
495 
496 int
497 posix_spawnattr_getsigdefault(const posix_spawnattr_t * __restrict sa,
498     sigset_t * __restrict sigdefault)
499 {
500 	*sigdefault = (*sa)->sa_sigdefault;
501 	return (0);
502 }
503 
504 int
505 posix_spawnattr_getsigmask(const posix_spawnattr_t * __restrict sa,
506     sigset_t * __restrict sigmask)
507 {
508 	*sigmask = (*sa)->sa_sigmask;
509 	return (0);
510 }
511 
512 int
513 posix_spawnattr_setflags(posix_spawnattr_t *sa, short flags)
514 {
515 	(*sa)->sa_flags = flags;
516 	return (0);
517 }
518 
519 int
520 posix_spawnattr_setpgroup(posix_spawnattr_t *sa, pid_t pgroup)
521 {
522 	(*sa)->sa_pgroup = pgroup;
523 	return (0);
524 }
525 
526 int
527 posix_spawnattr_setschedparam(posix_spawnattr_t * __restrict sa,
528     const struct sched_param * __restrict schedparam)
529 {
530 	(*sa)->sa_schedparam = *schedparam;
531 	return (0);
532 }
533 
534 int
535 posix_spawnattr_setschedpolicy(posix_spawnattr_t *sa, int schedpolicy)
536 {
537 	(*sa)->sa_schedpolicy = schedpolicy;
538 	return (0);
539 }
540 
541 int
542 posix_spawnattr_setsigdefault(posix_spawnattr_t * __restrict sa,
543     const sigset_t * __restrict sigdefault)
544 {
545 	(*sa)->sa_sigdefault = *sigdefault;
546 	return (0);
547 }
548 
549 int
550 posix_spawnattr_setsigmask(posix_spawnattr_t * __restrict sa,
551     const sigset_t * __restrict sigmask)
552 {
553 	(*sa)->sa_sigmask = *sigmask;
554 	return (0);
555 }
556