about summary refs log tree commit diff
path: root/sysdeps/unix
diff options
context:
space:
mode:
authorAdhemerval Zanella Netto <adhemerval.zanella@linaro.org>2023-01-12 10:58:51 -0300
committerAdhemerval Zanella <adhemerval.zanella@linaro.org>2023-02-01 08:42:11 -0300
commit2053c11331991818882f7cf023ed2ce4ff44b274 (patch)
treeb1edaafb8036456c39bff1756ed8827922c0b3b5 /sysdeps/unix
parent2290cf73cce1292d9345a8183fd29ae3994a9481 (diff)
downloadglibc-2053c11331991818882f7cf023ed2ce4ff44b274.tar.gz
glibc-2053c11331991818882f7cf023ed2ce4ff44b274.tar.xz
glibc-2053c11331991818882f7cf023ed2ce4ff44b274.zip
linux: Add clone3 CLONE_CLEAR_SIGHAND optimization to posix_spawn
The clone3 flag resets all signal handlers of the child not set to
SIG_IGN to SIG_DFL.  It allows to skip most of the sigaction calls
to setup child signal handling, where previously a posix_spawn
had to issue 2 times NSIG sigaction calls (one to obtain the current
disposition and another to set either SIG_DFL or SIG_IGN).

With POSIX_SPAWN_SETSIGDEF the child will setup the signal for the case
where the disposition is SIG_IGN.

The code must handle the fallback where clone3 is not available. This is
done by splitting __clone_internal_fallback from __clone_internal.

Checked on x86_64-linux-gnu.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Diffstat (limited to 'sysdeps/unix')
-rw-r--r--sysdeps/unix/sysv/linux/Makefile3
-rw-r--r--sysdeps/unix/sysv/linux/clone-internal.c37
-rw-r--r--sysdeps/unix/sysv/linux/clone3.h7
-rw-r--r--sysdeps/unix/sysv/linux/spawni.c31
4 files changed, 52 insertions, 26 deletions
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index f298878e8f..f8bd12d991 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -269,8 +269,7 @@ tests-clone-internal = \
   tst-align-clone-internal \
   tst-clone2-internal \
   tst-clone3-internal \
-  tst-getpid1-internal \
-  tst-misalign-clone-internal
+  tst-getpid1-internal
 tests-internal += $(tests-clone-internal)
 tests-static += $(tests-clone-internal)
 
diff --git a/sysdeps/unix/sysv/linux/clone-internal.c b/sysdeps/unix/sysv/linux/clone-internal.c
index a8611772a2..e125c9125e 100644
--- a/sysdeps/unix/sysv/linux/clone-internal.c
+++ b/sysdeps/unix/sysv/linux/clone-internal.c
@@ -44,27 +44,15 @@ _Static_assert (sizeof (struct clone_args) == CLONE_ARGS_SIZE_VER2,
 		"sizeof (struct clone_args) != CLONE_ARGS_SIZE_VER2");
 
 int
-__clone_internal (struct clone_args *cl_args,
-		  int (*func) (void *arg), void *arg)
+__clone_internal_fallback (struct clone_args *cl_args,
+			   int (*func) (void *arg), void *arg)
 {
-  int ret;
-#ifdef HAVE_CLONE3_WRAPPER
-  /* Try clone3 first.  */
-  int saved_errno = errno;
-  ret = __clone3 (cl_args, sizeof (*cl_args), func, arg);
-  if (ret != -1 || errno != ENOSYS)
-    return ret;
-
-  /* NB: Restore errno since errno may be checked against non-zero
-     return value.  */
-  __set_errno (saved_errno);
-#endif
-
   /* Map clone3 arguments to clone arguments.  NB: No need to check
      invalid clone3 specific bits in flags nor exit_signal since this
      is an internal function.  */
   int flags = cl_args->flags | cl_args->exit_signal;
   void *stack = cast_to_pointer (cl_args->stack);
+  int ret;
 
 #ifdef __ia64__
   ret = __clone2 (func, stack, cl_args->stack_size,
@@ -88,4 +76,23 @@ __clone_internal (struct clone_args *cl_args,
   return ret;
 }
 
+
+int
+__clone_internal (struct clone_args *cl_args,
+		  int (*func) (void *arg), void *arg)
+{
+#ifdef HAVE_CLONE3_WRAPPER
+  int saved_errno = errno;
+  int ret = __clone3 (cl_args, sizeof (*cl_args), func, arg);
+  if (ret != -1 || errno != ENOSYS)
+    return ret;
+
+  /* NB: Restore errno since errno may be checked against non-zero
+     return value.  */
+  __set_errno (saved_errno);
+#endif
+
+  return __clone_internal_fallback (cl_args, func, arg);
+}
+
 libc_hidden_def (__clone_internal)
diff --git a/sysdeps/unix/sysv/linux/clone3.h b/sysdeps/unix/sysv/linux/clone3.h
index e4d642e521..451b156405 100644
--- a/sysdeps/unix/sysv/linux/clone3.h
+++ b/sysdeps/unix/sysv/linux/clone3.h
@@ -23,6 +23,13 @@
 #include <stddef.h>
 #include <bits/types.h>
 
+
+/* Flags for the clone3 syscall.  */
+#define CLONE_CLEAR_SIGHAND 0x100000000ULL /* Clear any signal handler and
+					      reset to SIG_DFL.  */
+#define CLONE_INTO_CGROUP 0x200000000ULL /* Clone into a specific cgroup given
+					    the right permissions.  */
+
 /* The unsigned 64-bit and 8-byte aligned integer type.  */
 typedef __U64_TYPE __aligned_uint64_t __attribute__ ((__aligned__ (8)));
 
diff --git a/sysdeps/unix/sysv/linux/spawni.c b/sysdeps/unix/sysv/linux/spawni.c
index a1a14a58ae..bc321d4c58 100644
--- a/sysdeps/unix/sysv/linux/spawni.c
+++ b/sysdeps/unix/sysv/linux/spawni.c
@@ -66,6 +66,7 @@ struct posix_spawn_args
   ptrdiff_t argc;
   char *const *envp;
   int xflags;
+  bool use_clone3;
   int err;
 };
 
@@ -104,12 +105,11 @@ __spawni_child (void *arguments)
   const posix_spawnattr_t *restrict attr = args->attr;
   const posix_spawn_file_actions_t *file_actions = args->fa;
 
-  /* 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).  */
+  /* The child must ensure that no signal handler is enabled because it
+     shares memory with parent, so all signal dispositions must be either
+     SIG_DFL or SIG_IGN.  If clone3/CLONE_CLEAR_SIGHAND is used, there is
+     only the need to set the defined signals POSIX_SPAWN_SETSIGDEF to
+     SIG_DFL; otherwise, the code iterates over all signals.  */
   struct sigaction sa;
   memset (&sa, '\0', sizeof (sa));
 
@@ -122,7 +122,7 @@ __spawni_child (void *arguments)
 	{
 	  sa.sa_handler = SIG_DFL;
 	}
-      else if (__sigismember (&hset, sig))
+      else if (!args->use_clone3 && __sigismember (&hset, sig))
 	{
 	  if (is_internal_signal (sig))
 	    sa.sa_handler = SIG_IGN;
@@ -382,12 +382,25 @@ __spawnix (pid_t * pid, const char *file,
      for instance).  */
   struct clone_args clone_args =
     {
-      .flags = CLONE_VM | CLONE_VFORK,
+      /* Unsupported flags like CLONE_CLEAR_SIGHAND will be cleared up by
+	 __clone_internal_fallback.  */
+      .flags = CLONE_CLEAR_SIGHAND | CLONE_VM | CLONE_VFORK,
       .exit_signal = SIGCHLD,
       .stack = (uintptr_t) stack,
       .stack_size = stack_size,
     };
-  new_pid = __clone_internal (&clone_args, __spawni_child, &args);
+#ifdef HAVE_CLONE3_WRAPPER
+  args.use_clone3 = true;
+  new_pid = __clone3 (&clone_args, sizeof (clone_args), __spawni_child,
+		      &args);
+  /* clone3 was added in 5.3 and CLONE_CLEAR_SIGHAND in 5.5.  */
+  if (new_pid == -1 && (errno == ENOSYS || errno == EINVAL))
+#endif
+    {
+      args.use_clone3 = false;
+      new_pid = __clone_internal_fallback (&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