]> git.ipfire.org Git - thirdparty/make.git/commitdiff
[SV 56834] Support local PATH search with posix_spawnp
authorPaul Smith <psmith@gnu.org>
Sat, 7 Sep 2019 02:24:46 +0000 (22:24 -0400)
committerPaul Smith <psmith@gnu.org>
Sun, 8 Sep 2019 19:12:40 +0000 (15:12 -0400)
When using exec we install the child's environment before invoking
execlp(), so commands are found on the child's PATH.  posix_spawnp
searches on the parent's PATH, which we don't want.

Import gnulib's findprog-in module and use it to search the child's
PATH, then use posix_spawn() to run it.

Also, posix_spawn() does not fall back to trying sh on ENOEXEC, as
execlp() does, so implement that as well.

* bootstrap.conf: Add the findprog-in gnulib module
* src/job.c: Include findprog.h if we're using posix_spawn.
(start_job_command): Remove the handling of child->cmd_name,
(child_execute_job): and add it here.  Look up the command to be
run in the child's path and invoke it if found.  If it fails with
ENOEXEC then retry it as an argument to the default shell.
* tests/scripts/misc/general4: Test makefile PATH assignments.
* tests/scripts/features/targetvars: Ditto, for target variables.

bootstrap.conf
src/job.c
tests/scripts/features/targetvars
tests/scripts/misc/general4

index 8265660cb2d5024655f59f34077b43bcf870374f..5e1d538e04f69a9a853d8fe4cbd33c1280104cad 100644 (file)
@@ -46,6 +46,7 @@ gnulib_files=doc/make-stds.texi
 gnulib_modules="\
 alloca
 fdl
+findprog-in
 getloadavg
 host-cpu-c-abi
 strerror
index 0c68e01f15ba9f60a58634acfb1d6df9be1f106c..115d8481d2f0b08d3def032ea2603cbd3e3bd067 100644 (file)
--- a/src/job.c
+++ b/src/job.c
@@ -17,6 +17,7 @@ this program.  If not, see <http://www.gnu.org/licenses/>.  */
 #include "makeint.h"
 
 #include <assert.h>
+#include <string.h>
 
 #include "job.h"
 #include "debug.h"
@@ -25,8 +26,6 @@ this program.  If not, see <http://www.gnu.org/licenses/>.  */
 #include "variable.h"
 #include "os.h"
 
-#include <string.h>
-
 /* Default shell to use.  */
 #ifdef WINDOWS32
 # ifdef HAVE_STRINGS_H
@@ -139,6 +138,7 @@ extern int wait3 ();
 
 #ifdef USE_POSIX_SPAWN
 # include <spawn.h>
+# include "findprog.h"
 #endif
 
 #if !defined (wait) && !defined (POSIX)
@@ -1466,8 +1466,6 @@ start_job_command (struct child *child)
       environ = parent_environ; /* Restore value child may have clobbered.  */
       jobserver_post_child (flags & COMMANDS_RECURSE);
 
-      free (child->cmd_name);
-      child->cmd_name = child->pid > 0 ? xstrdup(argv[0]) : NULL;
 #endif /* !VMS */
     }
 
@@ -2271,9 +2269,10 @@ child_execute_job (struct childbase *child, int good_stdin, char **argv)
   pid_t pid;
   int r;
 #if defined(USE_POSIX_SPAWN)
-  short flags = 0;
+  char *cmd;
   posix_spawnattr_t attr;
   posix_spawn_file_actions_t fa;
+  short flags = 0;
 #endif
 
   /* Divert child output if we want to capture it.  */
@@ -2312,7 +2311,7 @@ child_execute_job (struct childbase *child, int good_stdin, char **argv)
   /* Run the command.  */
   exec_command (argv, child->environment);
 
-#else /* use posix_spawn() */
+#else /* USE_POSIX_SPAWN */
 
   if ((r = posix_spawnattr_init (&attr)) != 0)
     goto done;
@@ -2359,10 +2358,67 @@ child_execute_job (struct childbase *child, int good_stdin, char **argv)
   if ((r = posix_spawnattr_setflags (&attr, flags)) != 0)
     goto cleanup;
 
+  /* Look up the program on the child's PATH, if needed.  */
+  {
+    const char *p = NULL;
+    char **pp;
+
+    for (pp = child->environment; *pp != NULL; ++pp)
+      if ((*pp)[0] == 'P' && (*pp)[1] == 'A' && (*pp)[2] == 'T'
+          && (*pp)[3] == 'H' &&(*pp)[4] == '=')
+        {
+          p = (*pp) + 5;
+          break;
+        }
+
+    cmd = (char *)find_in_given_path (argv[0], p);
+  }
+
+  if (!cmd)
+    {
+      r = ENOENT;
+      goto cleanup;
+    }
+
   /* Start the program.  */
-  while ((r = posix_spawnp (&pid, argv[0], &fa, &attr, argv, child->environment)) == EINTR)
+  while ((r = posix_spawn (&pid, cmd, &fa, &attr, argv,
+                           child->environment)) == EINTR)
     ;
 
+  /* posix_spawn() doesn't provide sh fallback like exec() does; implement
+     it here.  POSIX doesn't specify the path to sh so use the default.  */
+
+  if (r == ENOEXEC)
+    {
+      char **nargv;
+      char **pp;
+      size_t l = 0;
+
+      for (pp = argv; *pp != NULL; ++pp)
+        ++l;
+
+      nargv = xmalloc (sizeof (char *) * (l + 2));
+      nargv[0] = (char *)default_shell;
+      nargv[1] = cmd;
+      memcpy (&nargv[2], &argv[1], sizeof (char *) * l);
+
+      while ((r = posix_spawn (&pid, nargv[0], &fa, &attr, nargv,
+                               child->environment)) == EINTR)
+        ;
+
+      free (nargv);
+    }
+
+  if (r == 0)
+    {
+      /* Spawn succeeded but may fail later: remember the command.  */
+      free (child->cmd_name);
+      if (cmd != argv[0])
+        child->cmd_name = cmd;
+      else
+        child->cmd_name = xstrdup(cmd);
+    }
+
  cleanup:
   posix_spawn_file_actions_destroy (&fa);
   posix_spawnattr_destroy (&attr);
@@ -2371,7 +2427,7 @@ child_execute_job (struct childbase *child, int good_stdin, char **argv)
   if (r != 0)
     pid = -1;
 
-#endif /* have posix_spawn() */
+#endif /* USE_POSIX_SPAWN */
 
   if (pid < 0)
     OSS (error, NILF, "%s: %s", argv[0], strerror (r));
index 72b7ddc7ff1c15b6e02b18eccdfc9644461857f1..1eb29a768c53b85239fd2f350b777f0cea01bd28 100644 (file)
@@ -255,6 +255,32 @@ a: ; @echo $(A)
 !,
               '', "hello; world\n");
 
+# TEST #21: SV-56834 Ensure setting PATH in a target var works properly
+mkdir('sd', 0775);
+open(my $fh, '>', 'sd/foobar');
+print $fh "exit 0";
+close($fh);
+chmod 0755, 'sd/foobar';
+
+run_make_test(q!
+all: PATH := sd
+all: ; foobar
+!,
+              '', "foobar\n");
+
+# Don't use the general PATH if not found on the target path
+
+$extraENV{PATH} = "$ENV{PATH}:sd";
+
+run_make_test(q!
+all: PATH := ..
+all: ; foobar
+!,
+              '', "foobar\n#MAKE#: foobar: $ERR_no_such_file\n#MAKE#: *** [#MAKEFILE#;3: all] Error 127", 512);
+
+unlink('sd/foobar');
+rmdir ('sd');
+
 # TEST #19: Test define/endef variables as target-specific vars
 
 # run_make_test('
index 6d42a16dcb07f371241f941f1341196775237556..72f3dbd0e828294d283de7e551ac8f51fe780547 100644 (file)
@@ -79,4 +79,45 @@ all: ; \@echo hi
 ",
               '', "hi\n");
 
+# SV-56834 Ensure setting PATH in the makefile works properly
+mkdir('sd', 0775);
+open(my $fh, '>', 'sd/foobar');
+print $fh "exit 0\n";
+close($fh);
+chmod 0755, 'sd/foobar';
+
+run_make_test(q!
+PATH := sd
+all: ; foobar
+!,
+              '', "foobar\n");
+
+# Don't use the general PATH if not found on the target path
+
+$extraENV{PATH} = "$ENV{PATH}:sd";
+
+run_make_test(q!
+PATH := ..
+all: ; foobar
+!,
+              '', "foobar\n#MAKE#: foobar: $ERR_no_such_file\n#MAKE#: *** [#MAKEFILE#;3: all] Error 127", 512);
+
+unlink('sd/foobar');
+rmdir('sd');
+
+# Ensure that local programs are not found if "." is not on the PATH
+
+open(my $fh, '>', 'foobar');
+print $fh "exit 0\n";
+close($fh);
+chmod 0755, 'foobar';
+
+run_make_test(q!
+PATH := ..
+all: ; foobar
+!,
+              '', "foobar\n#MAKE#: foobar: $ERR_no_such_file\n#MAKE#: *** [#MAKEFILE#;3: all] Error 127", 512);
+
+unlink('foobar');
+
 1;