@@ -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
@@ -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);
@@ -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)
new file mode 100644
@@ -0,0 +1,73 @@
+/* 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
+ <http://www.gnu.org/licenses/>. */
+
+#include <spawn.h>
+#include <error.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include <stdio.h>
+
+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"
+
@@ -26,5 +26,5 @@ __dup (int fd)
{
return fcntl (fd, F_DUPFD, 0);
}
-
+libc_hidden_def (__dup)
weak_alias (__dup, dup)
@@ -93,4 +93,5 @@ thread_start:
cfi_endproc
.size thread_start, .-thread_start
+libc_hidden_def (__clone)
weak_alias (__clone, clone)
@@ -137,4 +137,5 @@ thread_start:
cfi_endproc
.end thread_start
+libc_hidden_def (__clone)
weak_alias (__clone, clone)
@@ -93,4 +93,5 @@ PSEUDO_END (__clone)
.fnend
+libc_hidden_def (__clone)
weak_alias (__clone, clone)
@@ -175,4 +175,5 @@ ENTRY(__clone)
PSEUDO_END(__clone)
+libc_hidden_def (__clone)
weak_alias (__clone, clone)
@@ -139,4 +139,5 @@ L(nomoregetpid):
cfi_startproc
PSEUDO_END (__clone)
+libc_hidden_def (__clone)
weak_alias (__clone, clone)
@@ -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). */
@@ -127,4 +127,5 @@ donepid:
cfi_startproc
PSEUDO_END (__clone)
+libc_hidden_def (__clone)
weak_alias (__clone, clone)
@@ -69,4 +69,5 @@ L(thread_start):
nop
PSEUDO_END(__clone)
+libc_hidden_def (__clone)
weak_alias (__clone,clone)
@@ -166,4 +166,5 @@ L(gotpid):
END(__thread_start)
+libc_hidden_def (__clone)
weak_alias (__clone, clone)
@@ -104,4 +104,5 @@ thread_start:
cfi_startproc
PSEUDO_END (__clone)
+libc_hidden_def (__clone)
weak_alias (__clone, clone)
@@ -16,6 +16,8 @@
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
+#include <signal.h>
+
/* 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;
@@ -108,4 +108,5 @@ L(badargs):
cfi_startproc
END (__clone)
+libc_hidden_def (__clone)
weak_alias (__clone, clone)
@@ -126,4 +126,5 @@ L(parent):
END (__clone)
+libc_hidden_def (__clone)
weak_alias (__clone, clone)
@@ -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)
@@ -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)
@@ -123,4 +123,5 @@ ENTRY(__clone)
.word TID - TLS_PRE_TCB_SIZE
PSEUDO_END (__clone)
+libc_hidden_def (__clone)
weak_alias (__clone, clone)
@@ -100,4 +100,5 @@ __thread_start:
.size __thread_start, .-__thread_start
+libc_hidden_def (__clone)
weak_alias (__clone, clone)
@@ -96,4 +96,5 @@ __thread_start:
.size __thread_start, .-__thread_start
+libc_hidden_def (__clone)
weak_alias (__clone, clone)
new file mode 100644
@@ -0,0 +1,401 @@
+/* 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
+ <http://www.gnu.org/licenses/>. */
+
+#include <spawn.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <not-cancel.h>
+#include <local-setxid.h>
+#include <shlib-compat.h>
+#include <nptl/pthreadP.h>
+#include <dl-sysdep.h>
+#include <ldsodefs.h>
+#include <libc-internal.h>
+#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;
+ memcpy (new_argv + 2, argv + 1, argc * sizeof(char *));
+
+ /* 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);
+}
@@ -219,4 +219,5 @@ ENTRY (__clone)
}
PSEUDO_END (__clone)
+libc_hidden_def (__clone)
weak_alias (__clone, clone)
@@ -115,4 +115,5 @@ L(thread_start):
cfi_startproc;
PSEUDO_END (__clone)
+libc_hidden_def (__clone)
weak_alias (__clone, clone)