]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
posix: New Linux posix_spawn{p} implementation
authorAdhemerval Zanella <adhemerval.zanella@linaro.org>
Tue, 19 Jan 2016 19:33:32 +0000 (17:33 -0200)
committerAdhemerval Zanella <adhemerval.zanella@linaro.com>
Mon, 7 Mar 2016 04:53:47 +0000 (11:53 +0700)
This patch implements a new posix_spawn{p} implementation for Linux.  The main
difference is it uses the clone syscall directly with CLONE_VM and CLONE_VFORK
flags and a direct allocated stack.  The new stack and start function solves
most the vfork limitation (possible parent clobber due stack spilling).  The
remaning issue are related to signal handling:

  1. That no signal handlers must run in child context, to avoid corrupt
     parent's state.
  2. Child must synchronize with parent to enforce stack deallocation and
     to possible return execv issues.

The first one is solved by blocking all signals in child, even NPTL-internal
ones (SIGCANCEL and SIGSETXID).  The second 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 signal an exec error (checked
with new tst-spawn2 test).

There is an inherent race condition in pipe2 usage for architectures that do not
support the syscall directly.  In such cases the a pipe plus fctnl is used
instead and it may lead to file descriptor leak in parent (as decribed by fcntl
documentation).

The child process stack is allocate with a mmap with MAP_STACK flag using
default architecture stack size.  Although it is slower than use a stack buffer
from parent, it allows some slack for the compatibility code to run scripts
with no shebang (which may use a buffer with size depending of argument list
count).

Performance should be similar to the vfork default posix implementation and
way faster than fork path (vfork on mostly linux ports are basically
clone with CLONE_VM plus CLONE_VFORK).  The only difference is the syscalls
required for the stack allocation/deallocation.

It fixes BZ#10354, BZ#14750, and BZ#18433.

Tested on i386, x86_64, powerpc64le, and aarch64.

[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.

27 files changed:
ChangeLog
include/sched.h
include/unistd.h
posix/Makefile
posix/tst-spawn2.c [new file with mode: 0644]
sysdeps/posix/dup.c
sysdeps/unix/sysv/linux/aarch64/clone.S
sysdeps/unix/sysv/linux/alpha/clone.S
sysdeps/unix/sysv/linux/arm/clone.S
sysdeps/unix/sysv/linux/hppa/clone.S
sysdeps/unix/sysv/linux/i386/clone.S
sysdeps/unix/sysv/linux/ia64/clone2.S
sysdeps/unix/sysv/linux/m68k/clone.S
sysdeps/unix/sysv/linux/microblaze/clone.S
sysdeps/unix/sysv/linux/mips/clone.S
sysdeps/unix/sysv/linux/nios2/clone.S
sysdeps/unix/sysv/linux/nptl-signals.h
sysdeps/unix/sysv/linux/powerpc/powerpc32/clone.S
sysdeps/unix/sysv/linux/powerpc/powerpc64/clone.S
sysdeps/unix/sysv/linux/s390/s390-32/clone.S
sysdeps/unix/sysv/linux/s390/s390-64/clone.S
sysdeps/unix/sysv/linux/sh/clone.S
sysdeps/unix/sysv/linux/sparc/sparc32/clone.S
sysdeps/unix/sysv/linux/sparc/sparc64/clone.S
sysdeps/unix/sysv/linux/spawni.c [new file with mode: 0644]
sysdeps/unix/sysv/linux/tile/clone.S
sysdeps/unix/sysv/linux/x86_64/clone.S

index 3ef2c69006f7b3df2ea501186861bf3551b0a47a..ce8bf046e6197d8894eb2e826bc392a265d988ec 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,39 @@
 2016-03-07  Adhemerval Zanella  <adhemerval.zanella@linaro.org>
 
+       [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.
index 4f5939709056264354d8604aef270ee404d5b186..b4d74064de1e2f912b062867fba2e947a497990e 100644 (file)
@@ -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
index 5152f64f91c4179dfb0edf465e634548732db6c8..d2802b2b3e84f519248cf613109048036f9fed69 100644 (file)
@@ -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);
index 7ec110e80c2ec3a4f156e37078cd65aad2be1d21..5b0e298f75458bd19cf3bd9917c5ec646aee7783 100644 (file)
@@ -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 (file)
index 0000000..149f3d5
--- /dev/null
@@ -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
+   <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"
index 9525e767491376a74665fe6caeb6e21f7b28e344..abf3ff59dd762da0b940ea4a839848feb59dd37e 100644 (file)
@@ -26,5 +26,5 @@ __dup (int fd)
 {
   return fcntl (fd, F_DUPFD, 0);
 }
-
+libc_hidden_def (__dup)
 weak_alias (__dup, dup)
index 596fb9cc770cbe44949e3bd2c42e62a641d12581..8f3ab40954fcfc797ed950cf38abffca82fa283c 100644 (file)
@@ -93,4 +93,5 @@ thread_start:
        cfi_endproc
        .size thread_start, .-thread_start
 
+libc_hidden_def (__clone)
 weak_alias (__clone, clone)
index ec9c06d9af90c80138e6f0f54e678e8ffa8e50d3..556aa63105fddfaf6c5c52f68a135bdbae15af38 100644 (file)
@@ -137,4 +137,5 @@ thread_start:
        cfi_endproc
        .end thread_start
 
+libc_hidden_def (__clone)
 weak_alias (__clone, clone)
index 460a8f5ae8da978b1bd7bbe2dd30197aa3c31900..c1031239654511a390538a1a92a6aada3f43560b 100644 (file)
@@ -93,4 +93,5 @@ PSEUDO_END (__clone)
 
        .fnend
 
+libc_hidden_def (__clone)
 weak_alias (__clone, clone)
index e80fd8d57995be84cb96449a0a153fa35a1da59a..0a18137118d9f6110a53119115efe13b36a5c408 100644 (file)
@@ -175,4 +175,5 @@ ENTRY(__clone)
 
 PSEUDO_END(__clone)
 
+libc_hidden_def (__clone)
 weak_alias (__clone, clone)
index 7d818c1e46b06bfd15e6938ceca2b753c8ac8d74..ef447d161d6eda6855ab43776584008523bd3e3d 100644 (file)
@@ -139,4 +139,5 @@ L(nomoregetpid):
        cfi_startproc
 PSEUDO_END (__clone)
 
+libc_hidden_def (__clone)
 weak_alias (__clone, clone)
index 9b257b3c9be326d54e40244af60caa6f8778c5a5..fc046eb2a5049f3d1ed249fea0a87b85e132b4f3 100644 (file)
@@ -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).          */
index 8b40df27c44b90a462e5af6ce4a0ba7c2bb142c1..33474cc12c60677b3d8d28645fed95ca8ca49051 100644 (file)
@@ -127,4 +127,5 @@ donepid:
        cfi_startproc
 PSEUDO_END (__clone)
 
+libc_hidden_def (__clone)
 weak_alias (__clone, clone)
index 035d88b29f191022f6a582f8e9640ca562873c7d..cbc95ce266952b1d9e2bd0dfeebcd45fd6dd0fdc 100644 (file)
@@ -69,4 +69,5 @@ L(thread_start):
        nop
 PSEUDO_END(__clone)
 
+libc_hidden_def (__clone)
 weak_alias (__clone,clone)
index 755e8ccafe3a6d39250176609246784a064e735a..feb8250ecaf24a340f7eab7f917de28ed264896a 100644 (file)
@@ -166,4 +166,5 @@ L(gotpid):
 
        END(__thread_start)
 
+libc_hidden_def (__clone)
 weak_alias (__clone, clone)
index 4da5c19a73644fc7c7dfe0f12f73b7b70b072f7d..e4d397098317d36fda8b301399d559e8e866f8e1 100644 (file)
@@ -104,4 +104,5 @@ thread_start:
 
        cfi_startproc
 PSEUDO_END (__clone)
+libc_hidden_def (__clone)
 weak_alias (__clone, clone)
index 7560a2182546beb72784c822d9021c45cdda2bce..01f34c27ec0ba32b237a05d3b26938bb20e9ca1c 100644 (file)
@@ -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
 
 /* 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;
index 28948eae84d2587c0608d0ee7765512614079337..eb973db0b80014aa5e14f5bc3a68bde52aa83413 100644 (file)
@@ -108,4 +108,5 @@ L(badargs):
        cfi_startproc
 END (__clone)
 
+libc_hidden_def (__clone)
 weak_alias (__clone, clone)
index c8c6de8229ff0cc720999c65c0f15241d36bd8a6..959fdb7b765bc4a315fc709230ee448d3fdf46d1 100644 (file)
@@ -126,4 +126,5 @@ L(parent):
 
 END (__clone)
 
+libc_hidden_def (__clone)
 weak_alias (__clone, clone)
index cb2afbb8d18d88a704b61aab66c9a344da93511e..f774e909eee48575350352b9dc12a594e6bdaf19 100644 (file)
@@ -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)
index eddab351dd8e03ee113fa2748cd967daa17093d8..928a8812685a97ec1b144c43690c5b6804d729ac 100644 (file)
@@ -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)
index 53cc08b61ebe4425361546c9e1982058b4033ab0..a73dd7d9e321c889425f821542c05170ec2dac25 100644 (file)
@@ -123,4 +123,5 @@ ENTRY(__clone)
        .word   TID - TLS_PRE_TCB_SIZE
 PSEUDO_END (__clone)
 
+libc_hidden_def (__clone)
 weak_alias (__clone, clone)
index 6f41f0f55a7a9ba6f88a168eae7e1d4c64be09ab..68f8202e82e1b417e58b0ac034a9f155d8653fed 100644 (file)
@@ -100,4 +100,5 @@ __thread_start:
 
        .size   __thread_start, .-__thread_start
 
+libc_hidden_def (__clone)
 weak_alias (__clone, clone)
index e2d3914358d230896e1f661da9e98932e60fb645..cecffa7fb9c5406ff1e548ab55607fa53e9847a9 100644 (file)
@@ -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 (file)
index 0000000..454462b
--- /dev/null
@@ -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
+   <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;
+      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);
+}
index 02fe3a867082e5b140dbf49a9582d44cef543aa3..a300eb59ff8fad0d72fe48d0f60c8a9e54c10bc6 100644 (file)
@@ -219,4 +219,5 @@ ENTRY (__clone)
        }
 PSEUDO_END (__clone)
 
+libc_hidden_def (__clone)
 weak_alias (__clone, clone)
index 382568a3e9ebad911eb2fe05094deb8b09231639..1a50957df3a7c30f7dd1841fa063af369619f32c 100644 (file)
@@ -115,4 +115,5 @@ L(thread_start):
        cfi_startproc;
 PSEUDO_END (__clone)
 
+libc_hidden_def (__clone)
 weak_alias (__clone, clone)