> 2025-11-07  Bruno Haible  <[email protected]>
> 
>       posix_spawn-internal: Fix use of vfork on SPARC and PowerPC platforms.
>       Reported by Pádraig Brady <[email protected]> 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.

And this patch adds a corresponding unit test. Without the fix,
it crashes on Linux/SPARC, Linux/ppc64, Linux/ppc64le, Solaris/SPARC.


2025-11-10  Bruno Haible  <[email protected]>

        posix_spawnp tests: Add test against previous bug.
        * tests/test-posix_spawnp-vfork.c: New file, based on
        tests/test-posix_spawnp-script.c.
        * modules/posix_spawnp-tests (Files): Add it.
        (Makefile.am): Arrange to compile and test test-posix_spawnp-vfork.

>From 7b98e50eee2d301fc8b126bfc394f9aa6e92099e Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Mon, 10 Nov 2025 10:28:28 +0100
Subject: [PATCH] posix_spawnp tests: Add test against previous bug.

* tests/test-posix_spawnp-vfork.c: New file, based on
tests/test-posix_spawnp-script.c.
* modules/posix_spawnp-tests (Files): Add it.
(Makefile.am): Arrange to compile and test test-posix_spawnp-vfork.
---
 ChangeLog                       |   8 +++
 modules/posix_spawnp-tests      |   7 ++-
 tests/test-posix_spawnp-vfork.c | 106 ++++++++++++++++++++++++++++++++
 3 files changed, 119 insertions(+), 2 deletions(-)
 create mode 100644 tests/test-posix_spawnp-vfork.c

diff --git a/ChangeLog b/ChangeLog
index 3b1613fb0d..04e61f8506 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2025-11-10  Bruno Haible  <[email protected]>
+
+	posix_spawnp tests: Add test against previous bug.
+	* tests/test-posix_spawnp-vfork.c: New file, based on
+	tests/test-posix_spawnp-script.c.
+	* modules/posix_spawnp-tests (Files): Add it.
+	(Makefile.am): Arrange to compile and test test-posix_spawnp-vfork.
+
 2025-11-08  Paul Eggert  <[email protected]>
 
 	gendocs: port better to texi2html 5.0
diff --git a/modules/posix_spawnp-tests b/modules/posix_spawnp-tests
index 11a36b3b68..39f591cb10 100644
--- a/modules/posix_spawnp-tests
+++ b/modules/posix_spawnp-tests
@@ -4,6 +4,7 @@ tests/test-posix_spawn-dup2-stdout.in.sh
 tests/test-posix_spawn-dup2-stdin.c
 tests/test-posix_spawn-dup2-stdin.in.sh
 tests/test-posix_spawnp-script.c
+tests/test-posix_spawnp-vfork.c
 tests/executable-script
 tests/executable-script.sh
 tests/executable-shell-script
@@ -35,11 +36,13 @@ Makefile.am:
 TESTS += \
   test-posix_spawn-dup2-stdout \
   test-posix_spawn-dup2-stdin \
-  test-posix_spawnp-script
+  test-posix_spawnp-script \
+  test-posix_spawnp-vfork
 check_PROGRAMS += \
   test-posix_spawn-dup2-stdout \
   test-posix_spawn-dup2-stdin \
-  test-posix_spawnp-script
+  test-posix_spawnp-script \
+  test-posix_spawnp-vfork
 
 BUILT_SOURCES += test-posix_spawn-dup2-stdout.sh
 test-posix_spawn-dup2-stdout.sh: test-posix_spawn-dup2-stdout.in.sh
diff --git a/tests/test-posix_spawnp-vfork.c b/tests/test-posix_spawnp-vfork.c
new file mode 100644
index 0000000000..633f8e73de
--- /dev/null
+++ b/tests/test-posix_spawnp-vfork.c
@@ -0,0 +1,106 @@
+/* Test of posix_spawnp() function.
+   Copyright (C) 2020-2025 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3, or (at your option)
+   any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+#include <spawn.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "macros.h"
+
+/* Check an invocation via vfork() with a large $PATH (ca. 4 KB).
+   When configured with gl_cv_func_posix_spawnp_secure_exec=no, this test used
+   to crash on Linux/SPARC, Linux/ppc64, Linux/ppc64le, and Solaris/SPARC.  */
+
+int
+main ()
+{
+#if defined _WIN32 && !defined __CYGWIN__
+  /* There is no 'false' program on this platform, but no vfork() either.  */
+  fprintf (stderr, "Skipping test: no vfork() on this platform.\n");
+  return 77;
+#else
+
+  /* Components of file names are limited to ca. 255 characters on most
+     platforms.  (Cf. NAME_MAX in <limits.h>.)
+     Here is one with 250 characters.  */
+# define OOO "oooooooooooooooooooooooooooooooooooooooooooooooooo" \
+             "oooooooooooooooooooooooooooooooooooooooooooooooooo" \
+             "oooooooooooooooooooooooooooooooooooooooooooooooooo" \
+             "oooooooooooooooooooooooooooooooooooooooooooooooooo" \
+             "oooooooooooooooooooooooooooooooooooooooooooooooooo"
+  /* File names are limited to ca. 1024 characters on many platforms,
+     to ca. 4096 characters only on Linux and Cygwin, and unlimited only
+     on GNU/Hurd.  (Cf. PATH_MAX in <limits.h>.)
+     We prepend several directory names below this 1024 limit.  */
+# define ADD "/usr/bin/p" OOO "/" OOO "/" OOO "/" OOO ":" \
+             "/usr/bin/q" OOO "/" OOO "/" OOO "/" OOO ":" \
+             "/usr/bin/r" OOO "/" OOO "/" OOO "/" OOO ":" \
+             "/usr/bin/s" OOO "/" OOO "/" OOO "/" OOO ":"
+  const char *old_path = getenv ("PATH");
+  char *new_path = (char *) malloc (strlen (ADD) + strlen (old_path) + 1);
+  ASSERT (new_path != NULL);
+  memcpy (new_path, ADD, strlen (ADD));
+  strcpy (new_path + strlen (ADD), old_path);
+  ASSERT (setenv ("PATH", new_path, 1) == 0);
+
+  posix_spawnattr_t attr;
+  ASSERT (posix_spawnattr_init (&attr) == 0);
+  ASSERT (posix_spawnattr_setflags (&attr, POSIX_SPAWN_USEVFORK) == 0);
+
+  if (test_exit_status != EXIT_SUCCESS)
+    return test_exit_status;
+
+  /* Can't use 'false' here, because its exit status is only guaranteed to be
+     non-zero, and is in fact 255 on Solaris.  */
+  const char *prog = "sh";
+  const char *prog_argv[4] = { prog, "-c", "exit 1", NULL };
+  pid_t child;
+  int err = posix_spawnp (&child, prog, NULL, &attr, (char **) prog_argv, environ);
+  if (err != 0)
+    {
+      errno = err;
+      perror ("posix_spawn");
+      return 1;
+    }
+
+  /* Wait for child.  */
+  int status = 0;
+  while (waitpid (child, &status, 0) != child)
+    ;
+  if (!WIFEXITED (status))
+    {
+      fprintf (stderr, "subprocess terminated with unexpected wait status %d\n", status);
+      return 1;
+    }
+  int exitstatus = WEXITSTATUS (status);
+  if (exitstatus != 1)
+    {
+      fprintf (stderr, "subprocess terminated with unexpected exit status %d\n", exitstatus);
+      return 1;
+    }
+
+  return test_exit_status;
+
+#endif
+}
-- 
2.51.0

Reply via email to