diff --git a/ChangeLog b/ChangeLog index 3ef2c69006..ce8bf046e6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,39 @@ 2016-03-07 Adhemerval Zanella + [BZ #14750] + [BZ #10354] + [BZ #18433] + * include/sched.h (__clone): Add hidden prototype. + (__clone2): Likewise. + * include/unistd.h (__dup): Likewise. + * posix/Makefile (tests): Add tst-spawn2. + * posix/tst-spawn2.c: New file. + * sysdeps/posix/dup.c (__dup): Add hidden definition. + * sysdeps/unix/sysv/linux/aarch64/clone.S (__clone): Likewise. + * sysdeps/unix/sysv/linux/alpha/clone.S (__clone): Likewise. + * sysdeps/unix/sysv/linux/arm/clone.S (__clone): Likewise. + * sysdeps/unix/sysv/linux/hppa/clone.S (__clone): Likewise. + * sysdeps/unix/sysv/linux/i386/clone.S (__clone): Likewise. + * sysdeps/unix/sysv/linux/ia64/clone2.S (__clone): Likewise. + * sysdeps/unix/sysv/linux/m68k/clone.S (__clone): Likewise. + * sysdeps/unix/sysv/linux/microblaze/clone.S (__clone): Likewise. + * sysdeps/unix/sysv/linux/mips/clone.S (__clone): Likewise. + * sysdeps/unix/sysv/linux/nios2/clone.S (__clone): Likewise. + * sysdeps/unix/sysv/linux/powerpc/powerpc32/clone.S (__clone): + Likewise. + * sysdeps/unix/sysv/linux/powerpc/powerpc64/clone.S (__clone): + Likewise. + * sysdeps/unix/sysv/linux/s390/s390-32/clone.S (__clone): Likewise. + * sysdeps/unix/sysv/linux/s390/s390-64/clone.S (__clone): Likewise. + * sysdeps/unix/sysv/linux/sh/clone.S (__clone): Likewise. + * sysdeps/unix/sysv/linux/sparc/sparc32/clone.S (__clone): Likewise. + * sysdeps/unix/sysv/linux/sparc/sparc64/clone.S (__clone): Likewise. + * sysdeps/unix/sysv/linux/tile/clone.S (__clone): Likewise. + * sysdeps/unix/sysv/linux/x86_64/clone.S (__clone): Likewise. + * sysdeps/unix/sysv/linux/nptl-signals.h + (____nptl_is_internal_signal): New function. + * sysdeps/unix/sysv/linux/spawni.c: New file. + * posix/execvpe.c (__execvpe): Remove dynamic allocation. * posix/Makefile (tests): Add tst-execvpe{1,2,3,4,5,6}. * posix/tst-execvp1.c (do_test): Use a macro to call execvp. diff --git a/include/sched.h b/include/sched.h index 4f59397090..b4d74064de 100644 --- a/include/sched.h +++ b/include/sched.h @@ -19,7 +19,9 @@ extern int __sched_rr_get_interval (__pid_t __pid, struct timespec *__t); /* These are Linux specific. */ extern int __clone (int (*__fn) (void *__arg), void *__child_stack, int __flags, void *__arg, ...); +libc_hidden_proto (__clone) extern int __clone2 (int (*__fn) (void *__arg), void *__child_stack_base, size_t __child_stack_size, int __flags, void *__arg, ...); +libc_hidden_proto (__clone2) #endif #endif diff --git a/include/unistd.h b/include/unistd.h index 5152f64f91..d2802b2b3e 100644 --- a/include/unistd.h +++ b/include/unistd.h @@ -81,6 +81,7 @@ char *__canonicalize_directory_name_internal (const char *__thisdir, size_t __size) attribute_hidden; extern int __dup (int __fd); +libc_hidden_proto (__dup) extern int __dup2 (int __fd, int __fd2); libc_hidden_proto (__dup2) extern int __dup3 (int __fd, int __fd2, int flags); diff --git a/posix/Makefile b/posix/Makefile index 7ec110e80c..5b0e298f75 100644 --- a/posix/Makefile +++ b/posix/Makefile @@ -94,7 +94,7 @@ tests := tstgetopt testfnm runtests runptests \ xtests := bug-ga2 ifeq (yes,$(build-shared)) test-srcs := globtest -tests += wordexp-test tst-exec tst-spawn +tests += wordexp-test tst-exec tst-spawn tst-spawn2 endif tests-static = tst-exec-static tst-spawn-static tests += $(tests-static) diff --git a/posix/tst-spawn2.c b/posix/tst-spawn2.c new file mode 100644 index 0000000000..149f3d5cfa --- /dev/null +++ b/posix/tst-spawn2.c @@ -0,0 +1,72 @@ +/* Further tests for spawn in case of invalid binary paths. + Copyright (C) 2016 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +int +posix_spawn_test (void) +{ + /* Check if posix_spawn correctly returns an error and an invalid pid + by trying to spawn an invalid binary. */ + + const char *program = "/path/to/invalid/binary"; + char * const args[] = { 0 }; + pid_t pid = -1; + + int ret = posix_spawn (&pid, program, 0, 0, args, environ); + if (ret != ENOENT) + error (EXIT_FAILURE, errno, "posix_spawn"); + + /* POSIX states the value returned on pid variable in case of an error + is not specified. GLIBC will update the value iff the child + execution is successful. */ + if (pid != -1) + error (EXIT_FAILURE, errno, "posix_spawn returned pid != -1"); + + /* Check if no child is actually created. */ + ret = waitpid (-1, NULL, 0); + if (ret != -1 || errno != ECHILD) + error (EXIT_FAILURE, errno, "waitpid"); + + /* Same as before, but with posix_spawnp. */ + char *args2[] = { (char*) program, 0 }; + + ret = posix_spawnp (&pid, args2[0], 0, 0, args2, environ); + if (ret != ENOENT) + error (EXIT_FAILURE, errno, "posix_spawnp"); + + if (pid != -1) + error (EXIT_FAILURE, errno, "posix_spawnp returned pid != -1"); + + ret = waitpid (-1, NULL, 0); + if (ret != -1 || errno != ECHILD) + error (EXIT_FAILURE, errno, "waitpid"); + + return 0; +} + +#define TEST_FUNCTION posix_spawn_test () +#include "../test-skeleton.c" diff --git a/sysdeps/posix/dup.c b/sysdeps/posix/dup.c index 9525e76749..abf3ff59dd 100644 --- a/sysdeps/posix/dup.c +++ b/sysdeps/posix/dup.c @@ -26,5 +26,5 @@ __dup (int fd) { return fcntl (fd, F_DUPFD, 0); } - +libc_hidden_def (__dup) weak_alias (__dup, dup) diff --git a/sysdeps/unix/sysv/linux/aarch64/clone.S b/sysdeps/unix/sysv/linux/aarch64/clone.S index 596fb9cc77..8f3ab40954 100644 --- a/sysdeps/unix/sysv/linux/aarch64/clone.S +++ b/sysdeps/unix/sysv/linux/aarch64/clone.S @@ -93,4 +93,5 @@ thread_start: cfi_endproc .size thread_start, .-thread_start +libc_hidden_def (__clone) weak_alias (__clone, clone) diff --git a/sysdeps/unix/sysv/linux/alpha/clone.S b/sysdeps/unix/sysv/linux/alpha/clone.S index ec9c06d9af..556aa63105 100644 --- a/sysdeps/unix/sysv/linux/alpha/clone.S +++ b/sysdeps/unix/sysv/linux/alpha/clone.S @@ -137,4 +137,5 @@ thread_start: cfi_endproc .end thread_start +libc_hidden_def (__clone) weak_alias (__clone, clone) diff --git a/sysdeps/unix/sysv/linux/arm/clone.S b/sysdeps/unix/sysv/linux/arm/clone.S index 460a8f5ae8..c103123965 100644 --- a/sysdeps/unix/sysv/linux/arm/clone.S +++ b/sysdeps/unix/sysv/linux/arm/clone.S @@ -93,4 +93,5 @@ PSEUDO_END (__clone) .fnend +libc_hidden_def (__clone) weak_alias (__clone, clone) diff --git a/sysdeps/unix/sysv/linux/hppa/clone.S b/sysdeps/unix/sysv/linux/hppa/clone.S index e80fd8d579..0a18137118 100644 --- a/sysdeps/unix/sysv/linux/hppa/clone.S +++ b/sysdeps/unix/sysv/linux/hppa/clone.S @@ -175,4 +175,5 @@ ENTRY(__clone) PSEUDO_END(__clone) +libc_hidden_def (__clone) weak_alias (__clone, clone) diff --git a/sysdeps/unix/sysv/linux/i386/clone.S b/sysdeps/unix/sysv/linux/i386/clone.S index 7d818c1e46..ef447d161d 100644 --- a/sysdeps/unix/sysv/linux/i386/clone.S +++ b/sysdeps/unix/sysv/linux/i386/clone.S @@ -139,4 +139,5 @@ L(nomoregetpid): cfi_startproc PSEUDO_END (__clone) +libc_hidden_def (__clone) weak_alias (__clone, clone) diff --git a/sysdeps/unix/sysv/linux/ia64/clone2.S b/sysdeps/unix/sysv/linux/ia64/clone2.S index 9b257b3c9b..fc046eb2a5 100644 --- a/sysdeps/unix/sysv/linux/ia64/clone2.S +++ b/sysdeps/unix/sysv/linux/ia64/clone2.S @@ -96,6 +96,8 @@ ENTRY(__clone2) ret /* Not reached. */ PSEUDO_END(__clone2) +libc_hidden_def (__clone2) + /* For now we leave __clone undefined. This is unlikely to be a */ /* problem, since at least the i386 __clone in glibc always failed */ /* with a 0 sp (eventhough the kernel explicitly handled it). */ diff --git a/sysdeps/unix/sysv/linux/m68k/clone.S b/sysdeps/unix/sysv/linux/m68k/clone.S index 8b40df27c4..33474cc12c 100644 --- a/sysdeps/unix/sysv/linux/m68k/clone.S +++ b/sysdeps/unix/sysv/linux/m68k/clone.S @@ -127,4 +127,5 @@ donepid: cfi_startproc PSEUDO_END (__clone) +libc_hidden_def (__clone) weak_alias (__clone, clone) diff --git a/sysdeps/unix/sysv/linux/microblaze/clone.S b/sysdeps/unix/sysv/linux/microblaze/clone.S index 035d88b29f..cbc95ce266 100644 --- a/sysdeps/unix/sysv/linux/microblaze/clone.S +++ b/sysdeps/unix/sysv/linux/microblaze/clone.S @@ -69,4 +69,5 @@ L(thread_start): nop PSEUDO_END(__clone) +libc_hidden_def (__clone) weak_alias (__clone,clone) diff --git a/sysdeps/unix/sysv/linux/mips/clone.S b/sysdeps/unix/sysv/linux/mips/clone.S index 755e8ccafe..feb8250eca 100644 --- a/sysdeps/unix/sysv/linux/mips/clone.S +++ b/sysdeps/unix/sysv/linux/mips/clone.S @@ -166,4 +166,5 @@ L(gotpid): END(__thread_start) +libc_hidden_def (__clone) weak_alias (__clone, clone) diff --git a/sysdeps/unix/sysv/linux/nios2/clone.S b/sysdeps/unix/sysv/linux/nios2/clone.S index 4da5c19a73..e4d3970983 100644 --- a/sysdeps/unix/sysv/linux/nios2/clone.S +++ b/sysdeps/unix/sysv/linux/nios2/clone.S @@ -104,4 +104,5 @@ thread_start: cfi_startproc PSEUDO_END (__clone) +libc_hidden_def (__clone) weak_alias (__clone, clone) diff --git a/sysdeps/unix/sysv/linux/nptl-signals.h b/sysdeps/unix/sysv/linux/nptl-signals.h index 7560a21825..01f34c27ec 100644 --- a/sysdeps/unix/sysv/linux/nptl-signals.h +++ b/sysdeps/unix/sysv/linux/nptl-signals.h @@ -16,6 +16,8 @@ License along with the GNU C Library; if not, see . */ +#include + /* The signal used for asynchronous cancelation. */ #define SIGCANCEL __SIGRTMIN @@ -29,5 +31,13 @@ /* Signal used to implement the setuid et.al. functions. */ #define SIGSETXID (__SIGRTMIN + 1) + +/* Return is sig is used internally. */ +static inline int +__nptl_is_internal_signal (int sig) +{ + return (sig == SIGCANCEL) || (sig == SIGTIMER) || (sig == SIGSETXID); +} + /* Used to communicate with signal handler. */ extern struct xid_command *__xidcmd attribute_hidden; diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/clone.S b/sysdeps/unix/sysv/linux/powerpc/powerpc32/clone.S index 28948eae84..eb973db0b8 100644 --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/clone.S +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/clone.S @@ -108,4 +108,5 @@ L(badargs): cfi_startproc END (__clone) +libc_hidden_def (__clone) weak_alias (__clone, clone) diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/clone.S b/sysdeps/unix/sysv/linux/powerpc/powerpc64/clone.S index c8c6de8229..959fdb7b76 100644 --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/clone.S +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/clone.S @@ -126,4 +126,5 @@ L(parent): END (__clone) +libc_hidden_def (__clone) weak_alias (__clone, clone) diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/clone.S b/sysdeps/unix/sysv/linux/s390/s390-32/clone.S index cb2afbb8d1..f774e909ee 100644 --- a/sysdeps/unix/sysv/linux/s390/s390-32/clone.S +++ b/sysdeps/unix/sysv/linux/s390/s390-32/clone.S @@ -70,4 +70,6 @@ thread_start: xc 0(4,%r15),0(%r15) basr %r14,%r1 /* jump to fn */ DO_CALL (exit, 1) + +libc_hidden_def (__clone) weak_alias (__clone, clone) diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/clone.S b/sysdeps/unix/sysv/linux/s390/s390-64/clone.S index eddab351dd..928a881268 100644 --- a/sysdeps/unix/sysv/linux/s390/s390-64/clone.S +++ b/sysdeps/unix/sysv/linux/s390/s390-64/clone.S @@ -73,4 +73,6 @@ thread_start: xc 0(8,%r15),0(%r15) basr %r14,%r1 /* jump to fn */ DO_CALL (exit, 1) + +libc_hidden_def (__clone) weak_alias (__clone, clone) diff --git a/sysdeps/unix/sysv/linux/sh/clone.S b/sysdeps/unix/sysv/linux/sh/clone.S index 53cc08b61e..a73dd7d9e3 100644 --- a/sysdeps/unix/sysv/linux/sh/clone.S +++ b/sysdeps/unix/sysv/linux/sh/clone.S @@ -123,4 +123,5 @@ ENTRY(__clone) .word TID - TLS_PRE_TCB_SIZE PSEUDO_END (__clone) +libc_hidden_def (__clone) weak_alias (__clone, clone) diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/clone.S b/sysdeps/unix/sysv/linux/sparc/sparc32/clone.S index 6f41f0f55a..68f8202e82 100644 --- a/sysdeps/unix/sysv/linux/sparc/sparc32/clone.S +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/clone.S @@ -100,4 +100,5 @@ __thread_start: .size __thread_start, .-__thread_start +libc_hidden_def (__clone) weak_alias (__clone, clone) diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/clone.S b/sysdeps/unix/sysv/linux/sparc/sparc64/clone.S index e2d3914358..cecffa7fb9 100644 --- a/sysdeps/unix/sysv/linux/sparc/sparc64/clone.S +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/clone.S @@ -96,4 +96,5 @@ __thread_start: .size __thread_start, .-__thread_start +libc_hidden_def (__clone) weak_alias (__clone, clone) diff --git a/sysdeps/unix/sysv/linux/spawni.c b/sysdeps/unix/sysv/linux/spawni.c new file mode 100644 index 0000000000..454462be47 --- /dev/null +++ b/sysdeps/unix/sysv/linux/spawni.c @@ -0,0 +1,404 @@ +/* POSIX spawn interface. Linux version. + Copyright (C) 2016 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "spawn_int.h" + +/* The Linux implementation of posix_spawn{p} uses the clone syscall directly + with CLONE_VM and CLONE_VFORK flags and an allocated stack. The new stack + and start function solves most the vfork limitation (possible parent + clobber due stack spilling). The remaining issue are: + + 1. That no signal handlers must run in child context, to avoid corrupting + parent's state. + 2. The parent must ensure child's stack freeing. + 3. Child must synchronize with parent to enforce 2. and to possible + return execv issues. + + The first issue is solved by blocking all signals in child, even the + NPTL-internal ones (SIGCANCEL and SIGSETXID). The second and third issue + is done by a stack allocation in parent and a synchronization with using + a pipe or waitpid (in case or error). The pipe has the advantage of + allowing the child the communicate an exec error. */ + + +/* The Unix standard contains a long explanation of the way to signal + an error after the fork() was successful. Since no new wait status + was wanted there is no way to signal an error using one of the + available methods. The committee chose to signal an error by a + normal program exit with the exit code 127. */ +#define SPAWN_ERROR 127 + +/* We need to block both SIGCANCEL and SIGSETXID. */ +#define SIGALL_SET \ + ((__sigset_t) { .__val = {[0 ... _SIGSET_NWORDS-1 ] = -1 } }) + +#ifdef __ia64__ +# define CLONE(__fn, __stack, __stacksize, __flags, __args) \ + __clone2 (__fn, __stack, __stacksize, __flags, __args, 0, 0, 0) +#else +# define CLONE(__fn, __stack, __stacksize, __flags, __args) \ + __clone (__fn, __stack, __flags, __args) +#endif + +#if _STACK_GROWS_DOWN +# define STACK(__stack, __stack_size) (__stack + __stack_size) +#elif _STACK_GROWS_UP +# define STACK(__stack, __stack_size) (__stack) +#endif + + +struct posix_spawn_args +{ + int pipe[2]; + sigset_t oldmask; + const char *file; + int (*exec) (const char *, char *const *, char *const *); + const posix_spawn_file_actions_t *fa; + const posix_spawnattr_t *restrict attr; + char *const *argv; + ptrdiff_t argc; + char *const *envp; + int xflags; +}; + +/* Older version requires that shell script without shebang definition + to be called explicitly using /bin/sh (_PATH_BSHELL). */ +static void +maybe_script_execute (struct posix_spawn_args *args) +{ + if (SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_15) + && (args->xflags & SPAWN_XFLAGS_TRY_SHELL) && errno == ENOEXEC) + { + char *const *argv = args->argv; + ptrdiff_t argc = args->argc; + + /* Construct an argument list for the shell. */ + char *new_argv[argc + 1]; + new_argv[0] = (char *) _PATH_BSHELL; + new_argv[1] = (char *) args->file; + if (argc > 1) + memcpy (new_argv + 2, argv + 1, argc * sizeof(char *)); + else + new_argv[2] = NULL; + + /* Execute the shell. */ + args->exec (new_argv[0], new_argv, args->envp); + } +} + +/* Function used in the clone call to setup the signals mask, posix_spawn + attributes, and file actions. It run on its own stack (provided by the + posix_spawn call). */ +static int +__spawni_child (void *arguments) +{ + struct posix_spawn_args *args = arguments; + const posix_spawnattr_t *restrict attr = args->attr; + const posix_spawn_file_actions_t *file_actions = args->fa; + int p = args->pipe[1]; + int ret; + + close_not_cancel (args->pipe[0]); + + /* The child must ensure that no signal handler are enabled because it shared + memory with parent, so the signal disposition must be either SIG_DFL or + SIG_IGN. It does by iterating over all signals and although it could + possibly be more optimized (by tracking which signal potentially have a + signal handler), it might requires system specific solutions (since the + sigset_t data type can be very different on different architectures). */ + struct sigaction sa; + memset (&sa, '\0', sizeof (sa)); + + sigset_t hset; + __sigprocmask (SIG_BLOCK, 0, &hset); + for (int sig = 1; sig < _NSIG; ++sig) + { + if ((attr->__flags & POSIX_SPAWN_SETSIGDEF) + && sigismember (&attr->__sd, sig)) + { + sa.sa_handler = SIG_DFL; + } + else if (sigismember (&hset, sig)) + { + if (__nptl_is_internal_signal (sig)) + sa.sa_handler = SIG_IGN; + else + { + __libc_sigaction (sig, 0, &sa); + if (sa.sa_handler == SIG_IGN) + continue; + sa.sa_handler = SIG_DFL; + } + } + else + continue; + + __libc_sigaction (sig, &sa, 0); + } + +#ifdef _POSIX_PRIORITY_SCHEDULING + /* Set the scheduling algorithm and parameters. */ + if ((attr->__flags & (POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER)) + == POSIX_SPAWN_SETSCHEDPARAM) + { + if ((ret = __sched_setparam (0, &attr->__sp)) == -1) + goto fail; + } + else if ((attr->__flags & POSIX_SPAWN_SETSCHEDULER) != 0) + { + if ((ret = __sched_setscheduler (0, attr->__policy, &attr->__sp)) == -1) + goto fail; + } +#endif + + /* Set the process group ID. */ + if ((attr->__flags & POSIX_SPAWN_SETPGROUP) != 0 + && (ret = __setpgid (0, attr->__pgrp)) != 0) + goto fail; + + /* Set the effective user and group IDs. */ + if ((attr->__flags & POSIX_SPAWN_RESETIDS) != 0 + && ((ret = local_seteuid (__getuid ())) != 0 + || (ret = local_setegid (__getgid ())) != 0)) + goto fail; + + /* Execute the file actions. */ + if (file_actions != 0) + { + int cnt; + struct rlimit64 fdlimit; + bool have_fdlimit = false; + + for (cnt = 0; cnt < file_actions->__used; ++cnt) + { + struct __spawn_action *action = &file_actions->__actions[cnt]; + + /* Dup the pipe fd onto an unoccupied one to avoid any file + operation to clobber it. */ + if ((action->action.close_action.fd == p) + || (action->action.open_action.fd == p) + || (action->action.dup2_action.fd == p)) + { + if ((ret = __dup (p)) < 0) + goto fail; + p = ret; + } + + switch (action->tag) + { + case spawn_do_close: + if ((ret = + close_not_cancel (action->action.close_action.fd)) != 0) + { + if (!have_fdlimit) + { + __getrlimit64 (RLIMIT_NOFILE, &fdlimit); + have_fdlimit = true; + } + + /* Signal errors only for file descriptors out of range. */ + if (action->action.close_action.fd < 0 + || action->action.close_action.fd >= fdlimit.rlim_cur) + goto fail; + } + break; + + case spawn_do_open: + { + ret = open_not_cancel (action->action.open_action.path, + action->action. + open_action.oflag | O_LARGEFILE, + action->action.open_action.mode); + + if (ret == -1) + goto fail; + + int new_fd = ret; + + /* Make sure the desired file descriptor is used. */ + if (ret != action->action.open_action.fd) + { + if ((ret = __dup2 (new_fd, action->action.open_action.fd)) + != action->action.open_action.fd) + goto fail; + + if ((ret = close_not_cancel (new_fd)) != 0) + goto fail; + } + } + break; + + case spawn_do_dup2: + if ((ret = __dup2 (action->action.dup2_action.fd, + action->action.dup2_action.newfd)) + != action->action.dup2_action.newfd) + goto fail; + break; + } + } + } + + /* Set the initial signal mask of the child if POSIX_SPAWN_SETSIGMASK + is set, otherwise restore the previous one. */ + __sigprocmask (SIG_SETMASK, (attr->__flags & POSIX_SPAWN_SETSIGMASK) + ? &attr->__ss : &args->oldmask, 0); + + args->exec (args->file, args->argv, args->envp); + + /* This is compatibility function required to enable posix_spawn run + script without shebang definition for older posix_spawn versions + (2.15). */ + maybe_script_execute (args); + + ret = -errno; + +fail: + /* Since sizeof errno < PIPE_BUF, the write is atomic. */ + ret = -ret; + if (ret) + while (write_not_cancel (p, &ret, sizeof ret) < 0) + continue; + exit (SPAWN_ERROR); +} + +/* Spawn a new process executing PATH with the attributes describes in *ATTRP. + Before running the process perform the actions described in FILE-ACTIONS. */ +static int +__spawnix (pid_t * pid, const char *file, + const posix_spawn_file_actions_t * file_actions, + const posix_spawnattr_t * attrp, char *const argv[], + char *const envp[], int xflags, + int (*exec) (const char *, char *const *, char *const *)) +{ + pid_t new_pid; + struct posix_spawn_args args; + int ec; + + if (__pipe2 (args.pipe, O_CLOEXEC)) + return errno; + + /* To avoid imposing hard limits on posix_spawn{p} the total number of + arguments is first calculated to allocate a mmap to hold all possible + values. */ + ptrdiff_t argc = 0; + /* Linux allows at most max (0x7FFFFFFF, 1/4 stack size) arguments + to be used in a execve call. We limit to INT_MAX minus one due the + compatiblity code that may execute a shell script (maybe_script_execute) + where it will construct another argument list with an additional + argument. */ + ptrdiff_t limit = INT_MAX - 1; + while (argv[argc++] != NULL) + if (argc == limit) + { + errno = E2BIG; + return errno; + } + + int prot = (PROT_READ | PROT_WRITE + | ((GL (dl_stack_flags) & PF_X) ? PROT_EXEC : 0)); + + /* Add a slack area for child's stack. */ + size_t argv_size = (argc * sizeof (void *)) + 512; + size_t stack_size = ALIGN_UP (argv_size, GLRO(dl_pagesize)); + void *stack = __mmap (NULL, stack_size, prot, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); + if (__glibc_unlikely (stack == MAP_FAILED)) + { + close_not_cancel (args.pipe[0]); + close_not_cancel (args.pipe[1]); + return errno; + } + + /* Disable asynchronous cancellation. */ + int cs = LIBC_CANCEL_ASYNC (); + + args.file = file; + args.exec = exec; + args.fa = file_actions; + args.attr = attrp ? attrp : &(const posix_spawnattr_t) { 0 }; + args.argv = argv; + args.argc = argc; + args.envp = envp; + args.xflags = xflags; + + __sigprocmask (SIG_BLOCK, &SIGALL_SET, &args.oldmask); + + /* The clone flags used will create a new child that will run in the same + memory space (CLONE_VM) and the execution of calling thread will be + suspend until the child calls execve or _exit. These condition as + signal below either by pipe write (_exit with SPAWN_ERROR) or + a successful execve. + Also since the calling thread execution will be suspend, there is not + need for CLONE_SETTLS. Although parent and child share the same TLS + namespace, there will be no concurrent access for TLS variables (errno + for instance). */ + new_pid = CLONE (__spawni_child, STACK (stack, stack_size), stack_size, + CLONE_VM | CLONE_VFORK | SIGCHLD, &args); + + close_not_cancel (args.pipe[1]); + + if (new_pid > 0) + { + if (__read (args.pipe[0], &ec, sizeof ec) != sizeof ec) + ec = 0; + else + __waitpid (new_pid, NULL, 0); + } + else + ec = -new_pid; + + __munmap (stack, stack_size); + + close_not_cancel (args.pipe[0]); + + if (!ec && new_pid) + *pid = new_pid; + + __sigprocmask (SIG_SETMASK, &args.oldmask, 0); + + LIBC_CANCEL_RESET (cs); + + return ec; +} + +/* Spawn a new process executing PATH with the attributes describes in *ATTRP. + Before running the process perform the actions described in FILE-ACTIONS. */ +int +__spawni (pid_t * pid, const char *file, + const posix_spawn_file_actions_t * acts, + const posix_spawnattr_t * attrp, char *const argv[], + char *const envp[], int xflags) +{ + return __spawnix (pid, file, acts, attrp, argv, envp, xflags, + xflags & SPAWN_XFLAGS_USE_PATH ? __execvpe : __execve); +} diff --git a/sysdeps/unix/sysv/linux/tile/clone.S b/sysdeps/unix/sysv/linux/tile/clone.S index 02fe3a8670..a300eb59ff 100644 --- a/sysdeps/unix/sysv/linux/tile/clone.S +++ b/sysdeps/unix/sysv/linux/tile/clone.S @@ -219,4 +219,5 @@ ENTRY (__clone) } PSEUDO_END (__clone) +libc_hidden_def (__clone) weak_alias (__clone, clone) diff --git a/sysdeps/unix/sysv/linux/x86_64/clone.S b/sysdeps/unix/sysv/linux/x86_64/clone.S index 382568a3e9..1a50957df3 100644 --- a/sysdeps/unix/sysv/linux/x86_64/clone.S +++ b/sysdeps/unix/sysv/linux/x86_64/clone.S @@ -115,4 +115,5 @@ L(thread_start): cfi_startproc; PSEUDO_END (__clone) +libc_hidden_def (__clone) weak_alias (__clone, clone)