]> git.ipfire.org Git - thirdparty/gnulib.git/commitdiff
posix_spawn-internal: Fix use of vfork on SPARC and PowerPC platforms.
authorBruno Haible <bruno@clisp.org>
Fri, 7 Nov 2025 12:15:48 +0000 (13:15 +0100)
committerBruno Haible <bruno@clisp.org>
Fri, 7 Nov 2025 12:21:49 +0000 (13:21 +0100)
Reported by Pádraig Brady <P@draigBrady.com> in
<https://lists.gnu.org/archive/html/coreutils/2025-11/msg00052.html>.

* lib/spawni.c (__spawni): Revert last change. Do the stack allocations
with alloca() in the parent process, not in the child process.

ChangeLog
lib/spawni.c

index 1dd55d1ade7d658f7b0b600a41a4139f71551f6c..b55ba26ea928c5604ea11b8eb8da16a31282fec1 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2025-11-07  Bruno Haible  <bruno@clisp.org>
+
+       posix_spawn-internal: Fix use of vfork on SPARC and PowerPC platforms.
+       Reported by Pádraig Brady <P@draigBrady.com> in
+       <https://lists.gnu.org/archive/html/coreutils/2025-11/msg00052.html>.
+       * lib/spawni.c (__spawni): Revert last change. Do the stack allocations
+       with alloca() in the parent process, not in the child process.
+
 2025-11-06  Paul Eggert  <eggert@cs.ucla.edu>
 
        stdcountof-h: allow compound literal args
index b99f8eeabbc956d27ce7a4f27149960eb75c0e09..7500be0f1f546e901037f86b777815b7800234f5 100644 (file)
@@ -869,11 +869,6 @@ __spawni (pid_t *pid, const char *file,
           const posix_spawnattr_t *attrp, const char *const argv[],
           const char *const envp[], int use_path)
 {
-  pid_t new_pid;
-  char *path, *p, *name;
-  size_t len;
-  size_t pathlen;
-
   /* Do this once.  */
   short int flags = attrp == NULL ? 0 : attrp->_flags;
 
@@ -881,9 +876,46 @@ __spawni (pid_t *pid, const char *file,
        "variable 'flags' might be clobbered by 'longjmp' or 'vfork'"  */
   (void) &flags;
 
+  use_path = use_path && strchr (file, '/') == NULL;
+
+  /* Prepare a stack-allocated copy of $PATH and FILE, for iterating through
+     $PATH.  We do this already in the parent, because on Linux/SPARC,
+     Linux/ppc64, Linux/ppc64le, and Solaris/SPARC, it causes a SIGBUS or
+     SIGSEGV when done in the child process after vfork() and when $PATH is long
+     (ca. 4 KB or so).  */
+  char *path, *name;
+  if (use_path)
+    {
+      /* We have to search for FILE on the path.  */
+      path = getenv ("PATH");
+      if (path == NULL)
+        {
+#if HAVE_CONFSTR
+          /* There is no 'PATH' in the environment.
+             The default search path is the current directory
+             followed by the path 'confstr' returns for '_CS_PATH'.  */
+          size_t len = confstr (_CS_PATH, (char *) NULL, 0);
+          path = (char *) alloca (1 + len);
+          path[0] = ':';
+          (void) confstr (_CS_PATH, path + 1, len);
+#else
+          /* Pretend that the PATH contains only the current directory.  */
+          path = "";
+#endif
+        }
+
+      size_t len = strlen (file) + 1;
+      size_t pathlen = strlen (path);
+      name = alloca (pathlen + len + 1);
+      /* Copy the file name at the top.  */
+      name = (char *) memcpy (name + pathlen + 1, file, len);
+      /* And add the slash.  */
+      *--name = '/';
+    }
+
   /* Generate the new process.  */
-  /* Use of vfork() on Solaris/SPARC crashes; avoid it there.  */
-#if HAVE_VFORK && !(defined __sun && defined __sparc)
+  pid_t new_pid;
+#if HAVE_VFORK
   if ((flags & POSIX_SPAWN_USEVFORK) != 0
       /* If no major work is done, allow using vfork.  Note that we
          might perform the path searching.  But this would be done by
@@ -1029,42 +1061,16 @@ __spawni (pid_t *pid, const char *file,
         }
     }
 
-  if (! use_path || strchr (file, '/') != NULL)
+  if (! use_path)
     {
-      /* The FILE parameter is actually a path.  */
+      /* No need to iterate through $PATH.  Use FILE directly.  */
       execve (file, (char * const *) argv, (char * const *) envp);
 
       /* Oh, oh.  'execve' returns.  This is bad.  */
       _exit (SPAWN_ERROR);
     }
 
-  /* We have to search for FILE on the path.  */
-  path = getenv ("PATH");
-  if (path == NULL)
-    {
-#if HAVE_CONFSTR
-      /* There is no 'PATH' in the environment.
-         The default search path is the current directory
-         followed by the path 'confstr' returns for '_CS_PATH'.  */
-      len = confstr (_CS_PATH, (char *) NULL, 0);
-      path = (char *) alloca (1 + len);
-      path[0] = ':';
-      (void) confstr (_CS_PATH, path + 1, len);
-#else
-      /* Pretend that the PATH contains only the current directory.  */
-      path = "";
-#endif
-    }
-
-  len = strlen (file) + 1;
-  pathlen = strlen (path);
-  name = alloca (pathlen + len + 1);
-  /* Copy the file name at the top.  */
-  name = (char *) memcpy (name + pathlen + 1, file, len);
-  /* And add the slash.  */
-  *--name = '/';
-
-  p = path;
+  char *p = path;
   do
     {
       char *startp;