xref: /freebsd/lib/libc/gen/posix_spawn.c (revision a259b98fa211ed87bfee58c575de4e2de94ee0fa)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
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 "namespace.h"
30 #include <sys/param.h>
31 #include <sys/mman.h>
32 #include <sys/procctl.h>
33 #include <sys/procdesc.h>
34 #include <sys/queue.h>
35 #include <sys/wait.h>
36 
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <sched.h>
40 #include <spawn.h>
41 #include <signal.h>
42 #include <stdbool.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include "un-namespace.h"
47 #include "libc_private.h"
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 	int			sa_execfd;
57 	int			*sa_pdrfork_fdp;
58 	int			sa_pdflags;
59 };
60 
61 struct __posix_spawn_file_actions {
62 	STAILQ_HEAD(, __posix_spawn_file_actions_entry) fa_list;
63 };
64 
65 typedef struct __posix_spawn_file_actions_entry {
66 	STAILQ_ENTRY(__posix_spawn_file_actions_entry) fae_list;
67 	enum {
68 		FAE_OPEN,
69 		FAE_DUP2,
70 		FAE_CLOSE,
71 		FAE_CHDIR,
72 		FAE_FCHDIR,
73 		FAE_CLOSEFROM,
74 	} fae_action;
75 
76 	int fae_fildes;
77 	union {
78 		struct {
79 			char *path;
80 #define fae_path	fae_data.open.path
81 			int oflag;
82 #define fae_oflag	fae_data.open.oflag
83 			mode_t mode;
84 #define fae_mode	fae_data.open.mode
85 		} open;
86 		struct {
87 			int newfildes;
88 #define fae_newfildes	fae_data.dup2.newfildes
89 		} dup2;
90 	} fae_data;
91 } posix_spawn_file_actions_entry_t;
92 
93 /*
94  * Spawn routines
95  */
96 
97 static int
98 process_spawnattr(const posix_spawnattr_t sa)
99 {
100 	struct sigaction sigact = { .sa_flags = 0, .sa_handler = SIG_DFL };
101 	int aslr, i;
102 
103 	/*
104 	 * POSIX doesn't really describe in which order everything
105 	 * should be set. We'll just set them in the order in which they
106 	 * are mentioned.
107 	 */
108 
109 	/* Set process group */
110 	if (sa->sa_flags & POSIX_SPAWN_SETPGROUP) {
111 		if (setpgid(0, sa->sa_pgroup) != 0)
112 			return (errno);
113 	}
114 
115 	/* Set scheduler policy */
116 	if (sa->sa_flags & POSIX_SPAWN_SETSCHEDULER) {
117 		if (sched_setscheduler(0, sa->sa_schedpolicy,
118 		    &sa->sa_schedparam) != 0)
119 			return (errno);
120 	} else if (sa->sa_flags & POSIX_SPAWN_SETSCHEDPARAM) {
121 		if (sched_setparam(0, &sa->sa_schedparam) != 0)
122 			return (errno);
123 	}
124 
125 	/* Reset user ID's */
126 	if (sa->sa_flags & POSIX_SPAWN_RESETIDS) {
127 		if (setegid(getgid()) != 0)
128 			return (errno);
129 		if (seteuid(getuid()) != 0)
130 			return (errno);
131 	}
132 
133 	/*
134 	 * Set signal masks/defaults.
135 	 * Use unwrapped syscall, libthr is in undefined state after vfork().
136 	 */
137 	if (sa->sa_flags & POSIX_SPAWN_SETSIGMASK) {
138 		__sys_sigprocmask(SIG_SETMASK, &sa->sa_sigmask, NULL);
139 	}
140 
141 	if (sa->sa_flags & POSIX_SPAWN_SETSIGDEF) {
142 		for (i = 1; i <= _SIG_MAXSIG; i++) {
143 			if (sigismember(&sa->sa_sigdefault, i))
144 				if (__sys_sigaction(i, &sigact, NULL) != 0)
145 					return (errno);
146 		}
147 	}
148 
149 	/* Disable ASLR. */
150 	if ((sa->sa_flags & POSIX_SPAWN_DISABLE_ASLR_NP) != 0) {
151 		aslr = PROC_ASLR_FORCE_DISABLE;
152 		if (procctl(P_PID, 0, PROC_ASLR_CTL, &aslr) != 0)
153 			return (errno);
154 	}
155 
156 	return (0);
157 }
158 
159 static int
160 process_file_actions_entry(posix_spawn_file_actions_entry_t *fae)
161 {
162 	int fd, saved_errno;
163 
164 	switch (fae->fae_action) {
165 	case FAE_OPEN:
166 		/* Perform an open(), make it use the right fd */
167 		fd = _open(fae->fae_path, fae->fae_oflag, fae->fae_mode);
168 		if (fd < 0)
169 			return (errno);
170 		if (fd != fae->fae_fildes) {
171 			if (_dup2(fd, fae->fae_fildes) == -1) {
172 				saved_errno = errno;
173 				(void)_close(fd);
174 				return (saved_errno);
175 			}
176 			if (_close(fd) != 0) {
177 				if (errno == EBADF)
178 					return (EBADF);
179 			}
180 		}
181 		if (_fcntl(fae->fae_fildes, F_SETFD, 0) == -1)
182 			return (errno);
183 		break;
184 	case FAE_DUP2:
185 		/* Perform a dup2() */
186 		if (_dup2(fae->fae_fildes, fae->fae_newfildes) == -1)
187 			return (errno);
188 		if (_fcntl(fae->fae_newfildes, F_SETFD, 0) == -1)
189 			return (errno);
190 		break;
191 	case FAE_CLOSE:
192 		/* Perform a close(), do not fail if already closed */
193 		(void)_close(fae->fae_fildes);
194 		break;
195 	case FAE_CHDIR:
196 		if (chdir(fae->fae_path) != 0)
197 			return (errno);
198 		break;
199 	case FAE_FCHDIR:
200 		if (fchdir(fae->fae_fildes) != 0)
201 			return (errno);
202 		break;
203 	case FAE_CLOSEFROM:
204 		closefrom(fae->fae_fildes);
205 		break;
206 	}
207 	return (0);
208 }
209 
210 static int
211 process_file_actions(const posix_spawn_file_actions_t fa)
212 {
213 	posix_spawn_file_actions_entry_t *fae;
214 	int error;
215 
216 	/* Replay all file descriptor modifications */
217 	STAILQ_FOREACH(fae, &fa->fa_list, fae_list) {
218 		error = process_file_actions_entry(fae);
219 		if (error)
220 			return (error);
221 	}
222 	return (0);
223 }
224 
225 struct posix_spawn_args {
226 	const char *path;
227 	const posix_spawn_file_actions_t *fa;
228 	const posix_spawnattr_t *sa;
229 	char * const * argv;
230 	char * const * envp;
231 	int use_env_path;
232 	volatile int error;
233 };
234 
235 #define	_RFORK_THREAD_STACK_SIZE	4096
236 
237 static int
238 _posix_spawn_thr(void *data)
239 {
240 	struct posix_spawn_args *psa;
241 	char * const *envp;
242 
243 	psa = data;
244 	if (psa->sa != NULL) {
245 		psa->error = process_spawnattr(*psa->sa);
246 		if (psa->error)
247 			_exit(127);
248 	}
249 	if (psa->fa != NULL) {
250 		psa->error = process_file_actions(*psa->fa);
251 		if (psa->error)
252 			_exit(127);
253 	}
254 	envp = psa->envp != NULL ? psa->envp : environ;
255 	if (psa->sa != NULL && (*(psa->sa))->sa_execfd != -1)
256 		fexecve((*(psa->sa))->sa_execfd, psa->argv, envp);
257 	else if (psa->use_env_path)
258 		__libc_execvpe(psa->path, psa->argv, envp);
259 	else
260 		_execve(psa->path, psa->argv, envp);
261 	psa->error = errno;
262 
263 	/* This is called in such a way that it must not exit. */
264 	_exit(127);
265 }
266 
267 static int
268 do_posix_spawn(pid_t *pid, const char *path,
269     const posix_spawn_file_actions_t *fa,
270     const posix_spawnattr_t *sa,
271     char * const argv[], char * const envp[], bool use_env_path)
272 {
273 	struct posix_spawn_args psa;
274 	pid_t p;
275 	int pfd;
276 	bool do_pfd;
277 	char *guard, *stack;
278 	size_t stacksz, pagesz;
279 
280 #if defined(__i386__) || defined(__amd64__)
281 	stacksz = _RFORK_THREAD_STACK_SIZE;
282 	pagesz = getpagesize();
283 
284 	if (use_env_path) {
285 		size_t cnt;
286 
287 		/*
288 		 * We need to make sure we have enough room on the stack for the
289 		 * potential alloca() in execvPe if it gets kicked back an
290 		 * ENOEXEC from execve(2), plus the original buffer we gave
291 		 * ourselves; this protects us in the event that the caller
292 		 * intentionally or inadvertently supplies enough arguments to
293 		 * make us blow past the stack we've allocated from it.
294 		 */
295 		for (cnt = 0; argv[cnt] != NULL; ++cnt)
296 			;
297 		stacksz += MAX(3, cnt + 2) * sizeof(char *);
298 	}
299 
300 	stacksz = round_page(stacksz);
301 	guard = mmap(NULL, stacksz + pagesz, 0, MAP_GUARD, -1, 0);
302 	if (guard == MAP_FAILED)
303 		return (ENOMEM);
304 	stack = mmap(guard + pagesz, stacksz, PROT_READ | PROT_WRITE,
305 	    MAP_PRIVATE | MAP_FIXED | MAP_ANON, -1, 0);
306 	if (stack == MAP_FAILED) {
307 		munmap(guard, stacksz + pagesz);
308 		return (ENOMEM);
309 	}
310 #else
311 	guard = stack = NULL;
312 	pagesz = stacksz = 0;
313 #endif
314 	psa.path = path;
315 	psa.fa = fa;
316 	psa.sa = sa;
317 	psa.argv = argv;
318 	psa.envp = envp;
319 	psa.use_env_path = use_env_path;
320 	psa.error = 0;
321 
322 	do_pfd = sa != NULL && (*sa)->sa_pdrfork_fdp != NULL;
323 
324 	/*
325 	 * Passing RFSPAWN to rfork(2) gives us effectively a vfork that drops
326 	 * non-ignored signal handlers.  We'll fall back to the slightly less
327 	 * ideal vfork(2) if we get an EINVAL from rfork -- this should only
328 	 * happen with newer libc on older kernel that doesn't accept
329 	 * RFSPAWN.
330 	 *
331 	 * Combination of vfork() (or its equivalent rfork() form) and
332 	 * a special property of the libthr rtld locks ensure that
333 	 * rtld is operational in the child.  In particular, libthr
334 	 * rtld locks do not store owner' tid into the lock word.
335 	 *
336 	 * x86 stores the return address on the stack, so rfork(2)
337 	 * cannot work as-is because the child would clobber the
338 	 * return address of the parent.  Because of this, we must use
339 	 * rfork_thread instead.
340 	 *
341 	 * Every other architecture stores the return address in a
342 	 * register, the trivial rfork_thread() wrapper is provided
343 	 * for them.  The only minor drawback is that the stack is
344 	 * temporarily allocated.
345 	 */
346 	if (do_pfd) {
347 		p = pdrfork_thread(&pfd, PD_CLOEXEC | (*sa)->sa_pdflags,
348 		    RFSPAWN, stack + stacksz, _posix_spawn_thr, &psa);
349 	} else {
350 		p = rfork_thread(RFSPAWN, stack + stacksz, _posix_spawn_thr,
351 		    &psa);
352 	}
353 	if (stack != NULL)
354 		munmap(guard, stacksz + pagesz);
355 
356 	/*
357 	 * The above block should leave us in a state where we've either
358 	 * succeeded and we're ready to process the results, or we need to
359 	 * fallback to vfork() if the kernel didn't like RFSPAWN.
360 	 */
361 
362 	if (p == -1 && errno == EINVAL) {
363 		if (do_pfd)
364 			return (EOPNOTSUPP);
365 		p = vfork();
366 		if (p == 0)
367 			/* _posix_spawn_thr does not return */
368 			_posix_spawn_thr(&psa);
369 	}
370 	if (p == -1)
371 		return (errno);
372 	if (psa.error != 0) {
373 		/* Failed; ready to reap */
374 		if (do_pfd)
375 			(void)_close(pfd);
376 		else
377 			_waitpid(p, NULL, WNOHANG);
378 	} else if (pid != NULL) {
379 		/* exec succeeded */
380 		*pid = p;
381 		if (do_pfd)
382 			*((*sa)->sa_pdrfork_fdp) = pfd;
383 	}
384 	return (psa.error);
385 }
386 
387 int
388 posix_spawn(pid_t *pid, const char *path,
389     const posix_spawn_file_actions_t *fa,
390     const posix_spawnattr_t *sa,
391     char * const argv[], char * const envp[])
392 {
393 	return (do_posix_spawn(pid, path, fa, sa, argv, envp, false));
394 }
395 
396 int
397 posix_spawnp(pid_t *pid, const char *path,
398     const posix_spawn_file_actions_t *fa,
399     const posix_spawnattr_t *sa,
400     char * const argv[], char * const envp[])
401 {
402 	return (do_posix_spawn(pid, path, fa, sa, argv, envp, true));
403 }
404 
405 /*
406  * File descriptor actions
407  */
408 
409 int
410 posix_spawn_file_actions_init(posix_spawn_file_actions_t *ret)
411 {
412 	posix_spawn_file_actions_t fa;
413 
414 	fa = malloc(sizeof(struct __posix_spawn_file_actions));
415 	if (fa == NULL)
416 		return (errno);
417 
418 	STAILQ_INIT(&fa->fa_list);
419 	*ret = fa;
420 	return (0);
421 }
422 
423 int
424 posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *fa)
425 {
426 	posix_spawn_file_actions_entry_t *fae;
427 
428 	while ((fae = STAILQ_FIRST(&(*fa)->fa_list)) != NULL) {
429 		/* Remove file action entry from the queue */
430 		STAILQ_REMOVE_HEAD(&(*fa)->fa_list, fae_list);
431 
432 		/* Deallocate file action entry */
433 		if (fae->fae_action == FAE_OPEN ||
434 		    fae->fae_action == FAE_CHDIR)
435 			free(fae->fae_path);
436 		free(fae);
437 	}
438 
439 	free(*fa);
440 	return (0);
441 }
442 
443 int
444 posix_spawn_file_actions_addopen(posix_spawn_file_actions_t * __restrict fa,
445     int fildes, const char * __restrict path, int oflag, mode_t mode)
446 {
447 	posix_spawn_file_actions_entry_t *fae;
448 	int error;
449 
450 	if (fildes < 0)
451 		return (EBADF);
452 
453 	/* Allocate object */
454 	fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
455 	if (fae == NULL)
456 		return (errno);
457 
458 	/* Set values and store in queue */
459 	fae->fae_action = FAE_OPEN;
460 	fae->fae_path = strdup(path);
461 	if (fae->fae_path == NULL) {
462 		error = errno;
463 		free(fae);
464 		return (error);
465 	}
466 	fae->fae_fildes = fildes;
467 	fae->fae_oflag = oflag;
468 	fae->fae_mode = mode;
469 
470 	STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
471 	return (0);
472 }
473 
474 int
475 posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *fa,
476     int fildes, int newfildes)
477 {
478 	posix_spawn_file_actions_entry_t *fae;
479 
480 	if (fildes < 0 || newfildes < 0)
481 		return (EBADF);
482 
483 	/* Allocate object */
484 	fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
485 	if (fae == NULL)
486 		return (errno);
487 
488 	/* Set values and store in queue */
489 	fae->fae_action = FAE_DUP2;
490 	fae->fae_fildes = fildes;
491 	fae->fae_newfildes = newfildes;
492 
493 	STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
494 	return (0);
495 }
496 
497 int
498 posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *fa,
499     int fildes)
500 {
501 	posix_spawn_file_actions_entry_t *fae;
502 
503 	if (fildes < 0)
504 		return (EBADF);
505 
506 	/* Allocate object */
507 	fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
508 	if (fae == NULL)
509 		return (errno);
510 
511 	/* Set values and store in queue */
512 	fae->fae_action = FAE_CLOSE;
513 	fae->fae_fildes = fildes;
514 
515 	STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
516 	return (0);
517 }
518 
519 int
520 posix_spawn_file_actions_addchdir_np(posix_spawn_file_actions_t *
521     __restrict fa, const char *__restrict path)
522 {
523 	posix_spawn_file_actions_entry_t *fae;
524 	int error;
525 
526 	fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
527 	if (fae == NULL)
528 		return (errno);
529 
530 	fae->fae_action = FAE_CHDIR;
531 	fae->fae_path = strdup(path);
532 	if (fae->fae_path == NULL) {
533 		error = errno;
534 		free(fae);
535 		return (error);
536 	}
537 
538 	STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
539 	return (0);
540 }
541 __weak_reference(posix_spawn_file_actions_addchdir_np,
542     posix_spawn_file_actions_addchdir);
543 
544 int
545 posix_spawn_file_actions_addfchdir_np(posix_spawn_file_actions_t *__restrict fa,
546     int fildes)
547 {
548 	posix_spawn_file_actions_entry_t *fae;
549 
550 	if (fildes < 0)
551 		return (EBADF);
552 
553 	/* Allocate object */
554 	fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
555 	if (fae == NULL)
556 		return (errno);
557 
558 	fae->fae_action = FAE_FCHDIR;
559 	fae->fae_fildes = fildes;
560 
561 	STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
562 	return (0);
563 }
564 
565 __weak_reference(posix_spawn_file_actions_addfchdir_np,
566     posix_spawn_file_actions_addfchdir);
567 
568 int
569 posix_spawn_file_actions_addclosefrom_np (posix_spawn_file_actions_t *
570     __restrict fa, int from)
571 {
572 	posix_spawn_file_actions_entry_t *fae;
573 
574 	if (from < 0)
575 		return (EBADF);
576 
577 	/* Allocate object */
578 	fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
579 	if (fae == NULL)
580 		return (errno);
581 
582 	fae->fae_action = FAE_CLOSEFROM;
583 	fae->fae_fildes = from;
584 
585 	STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
586 	return (0);
587 }
588 
589 /*
590  * Spawn attributes
591  */
592 
593 int
594 posix_spawnattr_init(posix_spawnattr_t *ret)
595 {
596 	posix_spawnattr_t sa;
597 
598 	sa = calloc(1, sizeof(struct __posix_spawnattr));
599 	if (sa == NULL)
600 		return (errno);
601 	sa->sa_execfd = -1;
602 
603 	/* Set defaults as specified by POSIX, cleared above */
604 	*ret = sa;
605 	return (0);
606 }
607 
608 int
609 posix_spawnattr_destroy(posix_spawnattr_t *sa)
610 {
611 	free(*sa);
612 	return (0);
613 }
614 
615 int
616 posix_spawnattr_getflags(const posix_spawnattr_t * __restrict sa,
617     short * __restrict flags)
618 {
619 	*flags = (*sa)->sa_flags;
620 	return (0);
621 }
622 
623 int
624 posix_spawnattr_getpgroup(const posix_spawnattr_t * __restrict sa,
625     pid_t * __restrict pgroup)
626 {
627 	*pgroup = (*sa)->sa_pgroup;
628 	return (0);
629 }
630 
631 int
632 posix_spawnattr_getschedparam(const posix_spawnattr_t * __restrict sa,
633     struct sched_param * __restrict schedparam)
634 {
635 	*schedparam = (*sa)->sa_schedparam;
636 	return (0);
637 }
638 
639 int
640 posix_spawnattr_getschedpolicy(const posix_spawnattr_t * __restrict sa,
641     int * __restrict schedpolicy)
642 {
643 	*schedpolicy = (*sa)->sa_schedpolicy;
644 	return (0);
645 }
646 
647 int
648 posix_spawnattr_getsigdefault(const posix_spawnattr_t * __restrict sa,
649     sigset_t * __restrict sigdefault)
650 {
651 	*sigdefault = (*sa)->sa_sigdefault;
652 	return (0);
653 }
654 
655 int
656 posix_spawnattr_getsigmask(const posix_spawnattr_t * __restrict sa,
657     sigset_t * __restrict sigmask)
658 {
659 	*sigmask = (*sa)->sa_sigmask;
660 	return (0);
661 }
662 
663 int
664 posix_spawnattr_getexecfd_np(const posix_spawnattr_t * __restrict sa,
665     int * __restrict fdp)
666 {
667 	*fdp = (*sa)->sa_execfd;
668 	return (0);
669 }
670 
671 int
672 posix_spawnattr_getprocdescp_np(const posix_spawnattr_t * __restrict sa,
673     int ** __restrict fdpp, int * __restrict pdrflagsp)
674 {
675 	*fdpp = (*sa)->sa_pdrfork_fdp;
676 	*pdrflagsp = (*sa)->sa_pdflags;
677 	return (0);
678 }
679 
680 int
681 posix_spawnattr_setflags(posix_spawnattr_t *sa, short flags)
682 {
683 	if ((flags & ~(POSIX_SPAWN_RESETIDS | POSIX_SPAWN_SETPGROUP |
684 	    POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER |
685 	    POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK |
686 	    POSIX_SPAWN_DISABLE_ASLR_NP)) != 0)
687 		return (EINVAL);
688 	(*sa)->sa_flags = flags;
689 	return (0);
690 }
691 
692 int
693 posix_spawnattr_setpgroup(posix_spawnattr_t *sa, pid_t pgroup)
694 {
695 	(*sa)->sa_pgroup = pgroup;
696 	return (0);
697 }
698 
699 int
700 posix_spawnattr_setschedparam(posix_spawnattr_t * __restrict sa,
701     const struct sched_param * __restrict schedparam)
702 {
703 	(*sa)->sa_schedparam = *schedparam;
704 	return (0);
705 }
706 
707 int
708 posix_spawnattr_setschedpolicy(posix_spawnattr_t *sa, int schedpolicy)
709 {
710 	(*sa)->sa_schedpolicy = schedpolicy;
711 	return (0);
712 }
713 
714 int
715 posix_spawnattr_setsigdefault(posix_spawnattr_t * __restrict sa,
716     const sigset_t * __restrict sigdefault)
717 {
718 	(*sa)->sa_sigdefault = *sigdefault;
719 	return (0);
720 }
721 
722 int
723 posix_spawnattr_setsigmask(posix_spawnattr_t * __restrict sa,
724     const sigset_t * __restrict sigmask)
725 {
726 	(*sa)->sa_sigmask = *sigmask;
727 	return (0);
728 }
729 
730 int
731 posix_spawnattr_setexecfd_np(posix_spawnattr_t * __restrict sa,
732     int execfd)
733 {
734 	(*sa)->sa_execfd = execfd;
735 	return (0);
736 }
737 
738 int
739 posix_spawnattr_setprocdescp_np(const posix_spawnattr_t * __restrict sa,
740     int * __restrict fdp, int pdrflags)
741 {
742 	(*sa)->sa_pdrfork_fdp = fdp;
743 	(*sa)->sa_pdflags = pdrflags;
744 	return (0);
745 }
746