]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - sysdeps/unix/sysv/linux/spawni.c
Update copyright dates with scripts/update-copyrights.
[thirdparty/glibc.git] / sysdeps / unix / sysv / linux / spawni.c
index 2daf0c5ef040032aac015dee9f86028c3d2a58ee..c497869a747b4ddb95777686f256ef6876bf1da5 100644 (file)
@@ -1,5 +1,5 @@
 /* POSIX spawn interface.  Linux version.
-   Copyright (C) 2016-2017 Free Software Foundation, Inc.
+   Copyright (C) 2016-2019 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
@@ -17,7 +17,6 @@
    <http://www.gnu.org/licenses/>.  */
 
 #include <spawn.h>
-#include <assert.h>
 #include <fcntl.h>
 #include <paths.h>
 #include <string.h>
@@ -30,8 +29,8 @@
 #include <shlib-compat.h>
 #include <nptl/pthreadP.h>
 #include <dl-sysdep.h>
+#include <libc-pointer-arith.h>
 #include <ldsodefs.h>
-#include <libc-internal.h>
 #include "spawn_int.h"
 
 /* The Linux implementation of posix_spawn{p} uses the clone syscall directly
 #define SPAWN_ERROR    127
 
 #ifdef __ia64__
-# define CLONE(__fn, __stack, __stacksize, __flags, __args) \
-  __clone2 (__fn, __stack, __stacksize, __flags, __args, 0, 0, 0)
+# define CLONE(__fn, __stackbase, __stacksize, __flags, __args) \
+  __clone2 (__fn, __stackbase, __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
+/* Since ia64 wants the stackbase w/clone2, re-use the grows-up macro.  */
+#if _STACK_GROWS_UP || defined (__ia64__)
 # define STACK(__stack, __stack_size) (__stack)
+#elif _STACK_GROWS_DOWN
+# define STACK(__stack, __stack_size) (__stack + __stack_size)
 #endif
 
 
@@ -101,7 +101,7 @@ maybe_script_execute (struct posix_spawn_args *args)
       ptrdiff_t argc = args->argc;
 
       /* Construct an argument list for the shell.  */
-      char *new_argv[argc + 1];
+      char *new_argv[argc + 2];
       new_argv[0] = (char *) _PATH_BSHELL;
       new_argv[1] = (char *) args->file;
       if (argc > 1)
@@ -123,7 +123,6 @@ __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 ret;
 
   /* 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
@@ -139,13 +138,13 @@ __spawni_child (void *arguments)
   for (int sig = 1; sig < _NSIG; ++sig)
     {
       if ((attr->__flags & POSIX_SPAWN_SETSIGDEF)
-         && sigismember (&attr->__sd, sig))
+         && __sigismember (&attr->__sd, sig))
        {
          sa.sa_handler = SIG_DFL;
        }
-      else if (sigismember (&hset, sig))
+      else if (__sigismember (&hset, sig))
        {
-         if (__nptl_is_internal_signal (sig))
+         if (__is_internal_signal (sig))
            sa.sa_handler = SIG_IGN;
          else
            {
@@ -166,25 +165,29 @@ __spawni_child (void *arguments)
   if ((attr->__flags & (POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER))
       == POSIX_SPAWN_SETSCHEDPARAM)
     {
-      if ((ret = __sched_setparam (0, &attr->__sp)) == -1)
+      if (__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)
+      if (__sched_setscheduler (0, attr->__policy, &attr->__sp) == -1)
        goto fail;
     }
 #endif
 
+  if ((attr->__flags & POSIX_SPAWN_SETSID) != 0
+      && __setsid () < 0)
+    goto fail;
+
   /* Set the process group ID.  */
   if ((attr->__flags & POSIX_SPAWN_SETPGROUP) != 0
-      && (ret = __setpgid (0, attr->__pgrp)) != 0)
+      && __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))
+      && (local_seteuid (__getuid ()) != 0
+         || local_setegid (__getgid ()) != 0))
     goto fail;
 
   /* Execute the file actions.  */
@@ -201,8 +204,7 @@ __spawni_child (void *arguments)
          switch (action->tag)
            {
            case spawn_do_close:
-             if ((ret =
-                  close_not_cancel (action->action.close_action.fd)) != 0)
+             if (__close_nocancel (action->action.close_action.fd) != 0)
                {
                  if (!have_fdlimit)
                    {
@@ -225,12 +227,12 @@ __spawni_child (void *arguments)
                   with the process already at maximum number of file descriptor
                   opened and also for multiple actions on single-open special
                   paths (like /dev/watchdog).  */
-               close_not_cancel (action->action.open_action.fd);
+               __close_nocancel (action->action.open_action.fd);
 
-               ret = open_not_cancel (action->action.open_action.path,
-                                      action->action.
-                                      open_action.oflag | O_LARGEFILE,
-                                      action->action.open_action.mode);
+               int ret = __open_nocancel (action->action.open_action.path,
+                                          action->action.
+                                          open_action.oflag | O_LARGEFILE,
+                                          action->action.open_action.mode);
 
                if (ret == -1)
                  goto fail;
@@ -240,22 +242,32 @@ __spawni_child (void *arguments)
                /* 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))
+                   if (__dup2 (new_fd, action->action.open_action.fd)
                        != action->action.open_action.fd)
                      goto fail;
 
-                   if ((ret = close_not_cancel (new_fd)) != 0)
+                   if (__close_nocancel (new_fd) != 0)
                      goto fail;
                  }
              }
              break;
 
            case spawn_do_dup2:
-             if ((ret = __dup2 (action->action.dup2_action.fd,
-                                action->action.dup2_action.newfd))
+             if (__dup2 (action->action.dup2_action.fd,
+                         action->action.dup2_action.newfd)
                  != action->action.dup2_action.newfd)
                goto fail;
              break;
+
+           case spawn_do_chdir:
+             if (__chdir (action->action.chdir_action.path) != 0)
+               goto fail;
+             break;
+
+           case spawn_do_fchdir:
+             if (__fchdir (action->action.fchdir_action.fd) != 0)
+               goto fail;
+             break;
            }
        }
     }
@@ -265,7 +277,6 @@ __spawni_child (void *arguments)
   __sigprocmask (SIG_SETMASK, (attr->__flags & POSIX_SPAWN_SETSIGMASK)
                 ? &attr->__ss : &args->oldmask, 0);
 
-  args->err = 0;
   args->exec (args->file, args->argv, args->envp);
 
   /* This is compatibility function required to enable posix_spawn run
@@ -318,6 +329,11 @@ __spawnix (pid_t * pid, const char *file,
 
   /* Add a slack area for child's stack.  */
   size_t argv_size = (argc * sizeof (void *)) + 512;
+  /* We need at least a few pages in case the compiler's stack checking is
+     enabled.  In some configs, it is known to use at least 24KiB.  We use
+     32KiB to be "safe" from anything the compiler might do.  Besides, the
+     extra pages won't actually be allocated unless they get used.  */
+  argv_size += (32 * 1024);
   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);
@@ -331,7 +347,7 @@ __spawnix (pid_t * pid, const char *file,
 
   /* Child must set args.err to something non-negative - we rely on
      the parent and child sharing VM.  */
-  args.err = -1;
+  args.err = 0;
   args.file = file;
   args.exec = exec;
   args.fa = file_actions;
@@ -354,12 +370,26 @@ __spawnix (pid_t * pid, const char *file,
   new_pid = CLONE (__spawni_child, STACK (stack, stack_size), stack_size,
                   CLONE_VM | CLONE_VFORK | SIGCHLD, &args);
 
+  /* It needs to collect the case where the auxiliary process was created
+     but failed to execute the file (due either any preparation step or
+     for execve itself).  */
   if (new_pid > 0)
     {
+      /* Also, it handles the unlikely case where the auxiliary process was
+        terminated before calling execve as if it was successfully.  The
+        args.err is set to 0 as default and changed to a positive value
+        only in case of failure, so in case of premature termination
+        due a signal args.err will remain zeroed and it will be up to
+        caller to actually collect it.  */
       ec = args.err;
-      assert (ec >= 0);
-      if (ec != 0)
-         __waitpid (new_pid, NULL, 0);
+      if (ec > 0)
+       /* There still an unlikely case where the child is cancelled after
+          setting args.err, due to a positive error value.  Also there is
+          possible pid reuse race (where the kernel allocated the same pid
+          to an unrelated process).  Unfortunately due synchronization
+          issues where the kernel might not have the process collected
+          the waitpid below can not use WNOHANG.  */
+       __waitpid (new_pid, NULL, 0);
     }
   else
     ec = -new_pid;
@@ -384,6 +414,8 @@ __spawni (pid_t * pid, const char *file,
          const posix_spawnattr_t * attrp, char *const argv[],
          char *const envp[], int xflags)
 {
+  /* It uses __execvpex to avoid run ENOEXEC in non compatibility mode (it
+     will be handled by maybe_script_execute).  */
   return __spawnix (pid, file, acts, attrp, argv, envp, xflags,
-                   xflags & SPAWN_XFLAGS_USE_PATH ? __execvpe : __execve);
+                   xflags & SPAWN_XFLAGS_USE_PATH ? __execvpex :__execve);
 }