@@ -109,7 +109,9 @@ tests := test-errno tstgetopt testfnm runtests runptests \
tst-glob-tilde test-ssize-max tst-spawn4 bug-regex37 \
bug-regex38 tst-regcomp-truncated tst-spawn-chdir \
tst-wordexp-nocmd tst-execveat tst-spawn5 \
- tst-sched_getaffinity tst-spawn6
+ tst-sched_getaffinity tst-spawn6 \
+ tst-spawn-chdir-timens tst-spawn4-timens tst-spawn5-timens \
+ tst-spawn6-timens
# Test for the glob symbol version that was replaced in glibc 2.27.
ifeq ($(have-GLIBC_2.26)$(build-shared),yesyes)
@@ -130,7 +132,15 @@ xtests := tst-getaddrinfo4 tst-getaddrinfo5 tst-sched_rr_get_interval
xtests-time64 := tst-sched_rr_get_interval-time64
ifeq (yes,$(build-shared))
test-srcs := globtest
-tests += wordexp-test tst-exec tst-spawn tst-spawn2 tst-spawn3
+tests += \
+ tst-exec \
+ tst-spawn \
+ tst-spawn-timens \
+ tst-spawn2 \
+ tst-spawn2-timens \
+ tst-spawn3 \
+ wordexp-test \
+ # tests
endif
ifeq (yesyes,$(build-shared)$(have-thread-library))
tests += tst-getopt-cancel tst-_Fork
@@ -290,7 +300,9 @@ tst-execvpe5-ARGS = -- $(host-test-program-cmd)
tst-spawn-ARGS = -- $(host-test-program-cmd)
tst-spawn-static-ARGS = $(tst-spawn-ARGS)
tst-spawn5-ARGS = -- $(host-test-program-cmd)
+tst-spawn5-timens-ARGS = -- $(host-test-program-cmd)
tst-spawn6-ARGS = -- $(host-test-program-cmd)
+tst-spawn6-timens-ARGS = -- $(host-test-program-cmd)
tst-dir-ARGS = `pwd` `cd $(common-objdir)/$(subdir); pwd` `cd $(common-objdir); pwd` $(objpfx)tst-dir
tst-chmod-ARGS = $(objdir)
tst-vfork3-ARGS = --test-dir=$(objpfx)
new file mode 100644
@@ -0,0 +1,2 @@
+#define CHECK_TIMENAMESPACE
+#include "tst-spawn-chdir.c"
@@ -29,6 +29,7 @@
#include <support/test-driver.h>
#include <support/xstdio.h>
#include <support/xunistd.h>
+#include <support/namespace.h>
#include <unistd.h>
/* Reads the file at PATH, which must consist of exactly one line.
@@ -87,6 +88,12 @@ add_chdir (posix_spawn_file_actions_t *actions, const char *path,
static int
do_test (void)
{
+#ifdef CHECK_TIMENAMESPACE
+ support_become_root();
+ if (!support_enter_time_namespace ())
+ return EXIT_UNSUPPORTED;
+#endif
+
/* Directory for temporary file data. Each subtest uses a numeric
subdirectory. */
char *directory = support_create_temp_directory ("tst-spawn-chdir-");
new file mode 100644
@@ -0,0 +1,2 @@
+#define CHECK_TIMENAMESPACE
+#include "tst-spawn-chdir.c"
@@ -178,6 +178,12 @@ do_test (int argc, char *argv[])
return handle_restart (argv[1], argv[2], argv[3], argv[4], argv[5],
argv[6]);
+#ifdef CHECK_TIMENAMESPACE
+ support_become_root();
+ if (!support_enter_time_namespace ())
+ return EXIT_UNSUPPORTED;
+#endif
+
/* Prepare the test. We are creating four files: two which file descriptor
will be marked with FD_CLOEXEC, another which is not. */
new file mode 100644
@@ -0,0 +1,2 @@
+#define CHECK_TIMENAMESPACE
+#include "tst-spawn2.c"
@@ -26,10 +26,18 @@
#include <stdio.h>
#include <support/check.h>
+#include <support/test-driver.h>
+#include <support/namespace.h>
int
do_test (void)
{
+#ifdef CHECK_TIMENAMESPACE
+ support_become_root();
+ if (!support_enter_time_namespace ())
+ return EXIT_UNSUPPORTED;
+#endif
+
/* Check if posix_spawn correctly returns an error and an invalid pid
by trying to spawn an invalid binary. */
new file mode 100644
@@ -0,0 +1,2 @@
+#define CHECK_TIMENAMESPACE
+#include "tst-spawn4.c"
@@ -21,13 +21,21 @@
#include <unistd.h>
#include <sys/stat.h>
-#include <support/xunistd.h>
#include <support/check.h>
+#include <support/namespace.h>
#include <support/temp_file.h>
+#include <support/test-driver.h>
+#include <support/xunistd.h>
static int
do_test (void)
{
+#ifdef CHECK_TIMENAMESPACE
+ support_become_root();
+ if (!support_enter_time_namespace ())
+ return EXIT_UNSUPPORTED;
+#endif
+
char *scriptname;
int fd = create_temp_file ("tst-spawn4.", &scriptname);
TEST_VERIFY_EXIT (fd >= 0);
new file mode 100644
@@ -0,0 +1,2 @@
+#define CHECK_TIMENAMESPACE
+#include "tst-spawn5.c"
@@ -28,8 +28,10 @@
#include <limits.h>
#include <support/check.h>
-#include <support/xunistd.h>
+#include <support/namespace.h>
#include <support/support.h>
+#include <support/test-driver.h>
+#include <support/xunistd.h>
#include <arch-fd_to_filename.h>
#include <array_length.h>
@@ -278,6 +280,12 @@ do_test (int argc, char *argv[])
/* Ignore the application name. */
handle_restart (argc - 1, &argv[1]);
+#ifdef CHECK_TIMENAMESPACE
+ support_become_root();
+ if (!support_enter_time_namespace ())
+ return EXIT_UNSUPPORTED;
+#endif
+
TEST_VERIFY_EXIT (argc == 2 || argc == 5);
int i;
new file mode 100644
@@ -0,0 +1,2 @@
+#define CHECK_TIMENAMESPACE
+#include "tst-spawn6.c"
@@ -25,12 +25,14 @@
#include <spawn.h>
#include <stdbool.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <support/check.h>
+#include <support/namespace.h>
+#include <support/test-driver.h>
#include <support/xunistd.h>
-#include <sys/wait.h>
#include <sys/ioctl.h>
-#include <stdlib.h>
+#include <sys/wait.h>
#include <termios.h>
#ifndef PATH_MAX
@@ -124,6 +126,12 @@ run_subprogram (int argc, char *argv[], const posix_spawnattr_t *attr,
static int
run_test (int argc, char *argv[])
{
+#ifdef CHECK_TIMENAMESPACE
+ support_become_root();
+ if (!support_enter_time_namespace ())
+ return EXIT_UNSUPPORTED;
+#endif
+
/* We must have either:
- four parameters left if called initially:
+ path to ld.so optional
@@ -44,7 +44,12 @@
third issue is done by a stack allocation in parent, and by using a
field in struct spawn_args where the child can write an error
code. CLONE_VFORK ensures that the parent does not run until the
- child has either exec'ed successfully or exited. */
+ child has either exec'ed successfully or exited.
+
+ If the clone with CLONE_VM and CLONE_VFORK fails (due any kernel limitation
+ such as time namespace), only CLONE_VFORK is used instead and the
+ preparation and execve failures are communicated through a shared
+ memory. */
/* The Unix standard contains a long explanation of the way to signal
@@ -67,6 +72,7 @@ struct posix_spawn_args
char *const *envp;
int xflags;
int err;
+ int *errp;
};
/* Older version requires that shell script without shebang definition
@@ -97,8 +103,8 @@ maybe_script_execute (struct posix_spawn_args *args)
/* 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)
+static _Noreturn int
+spawni_child (void *arguments)
{
struct posix_spawn_args *args = arguments;
const posix_spawnattr_t *restrict attr = args->attr;
@@ -300,10 +306,91 @@ fail:
(EINTERNALBUG) describing that, use ECHILD. Another option would
be to set args->err to some negative sentinel and have the parent
abort(), but that seems needlessly harsh. */
- args->err = errno ? : ECHILD;
+ *args->errp = errno ? : ECHILD;
+
_exit (SPAWN_ERROR);
}
+/* Spawn a new process using clone with FLAGS, STACK, and STACK_SIZE and
+ return TRUE if the helper was created or if the failure was not due
+ resource exhaustion. */
+static bool
+spawni_clone (struct posix_spawn_args *args, pid_t *new_pid, int *ec,
+ int flags, void *stack, size_t stack_size)
+{
+ /* 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.
+
+ 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). */
+ struct clone_args clone_args =
+ {
+ .flags = flags,
+ .exit_signal = SIGCHLD,
+ .stack = (uintptr_t) stack,
+ .stack_size = stack_size,
+ };
+ *new_pid = __clone_internal (&clone_args, spawni_child, 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->errp;
+ 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 = errno;
+
+ /* There is no much point in retrying with fork and exec if kernel returns a
+ failure due resource exhaustion. */
+ return *new_pid > 0 || (errno == ENOMEM || errno == EAGAIN);
+}
+
+static bool
+spawni_clone_vfork_vm (struct posix_spawn_args *args, pid_t *new_pid, int *ec,
+ void *stack, size_t stack_size)
+{
+ args->errp = &args->err;
+ bool r = spawni_clone (args, new_pid, ec, CLONE_VM | CLONE_VFORK,
+ stack, stack_size);
+ return r;
+}
+
+static void
+spawni_clone_vfork (struct posix_spawn_args *args, pid_t *new_pid, int *ec,
+ void *stack, size_t stack_size)
+{
+ int *errmap = __mmap (NULL, sizeof (int), PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ if (__glibc_unlikely (errmap == MAP_FAILED))
+ {
+ *ec = errno;
+ return;
+ }
+
+ args->errp = errmap;
+ spawni_clone (args, new_pid, ec, CLONE_VFORK, stack, stack_size);
+ __munmap (errmap, sizeof (int));
+}
+
/* 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
@@ -370,47 +457,11 @@ __spawnix (pid_t * pid, const char *file,
__libc_signal_block_all (&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.
-
- 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). */
- struct clone_args clone_args =
- {
- .flags = CLONE_VM | CLONE_VFORK,
- .exit_signal = SIGCHLD,
- .stack = (uintptr_t) stack,
- .stack_size = stack_size,
- };
- new_pid = __clone_internal (&clone_args, __spawni_child, &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;
- 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 = errno;
-
+ /* First try with CLONE_VM | CLONE_VFORK with errors being communicate through
+ ARGS. If clone fails, only CLONE_VFORK is used and error status is passed
+ through a MAP_SHARED memory. */
+ if (!spawni_clone_vfork_vm (&args, &new_pid, &ec, stack, stack_size))
+ spawni_clone_vfork (&args, &new_pid, &ec, stack, stack_size);
__munmap (stack, stack_size);
if ((ec == 0) && (pid != NULL))