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