path: root/run-command.c
diff options
authorBrandon Williams <>2017-04-25 23:47:00 (GMT)
committerJunio C Hamano <>2017-04-26 06:17:36 (GMT)
commit940283101ce87250cf31a592730386f5061e1286 (patch)
tree8959b34793de45f243d177cbec99ad2c3a4df7a4 /run-command.c
parent38124a40e480c1717326b7bc27bcbca758de908e (diff)
run-command: restrict PATH search to executable files
In some situations run-command will incorrectly try (and fail) to execute a directory instead of an executable file. This was observed by having a directory called "ssh" in $PATH before the real ssh and trying to use ssh protoccol, reslting in the following: $ git ls-remote ssh://url fatal: cannot exec 'ssh': Permission denied It ends up being worse and run-command will even try to execute a non-executable file if it preceeds the executable version of a file on the PATH. For example, if PATH=~/bin1:~/bin2:~/bin3 and there exists a directory 'git-hello' in 'bin1', a non-executable file 'git-hello' in bin2 and an executable file 'git-hello' (which prints "Hello World!") in bin3 the following will occur: $ git hello fatal: cannot exec 'git-hello': Permission denied This is due to only checking 'access()' when locating an executable in PATH, which doesn't distinguish between files and directories. Instead use 'is_executable()' which check that the path is to a regular, executable file. Now run-command won't try to execute the directory or non-executable file 'git-hello': $ git hello Hello World! which matches what execvp(3) would have done when asked to execute git-hello with such a $PATH. Reported-by: Brian Hatfield <> Signed-off-by: Brandon Williams <> Reviewed-by: Jonathan Nieder <> Signed-off-by: Junio C Hamano <>
Diffstat (limited to 'run-command.c')
1 files changed, 18 insertions, 1 deletions
diff --git a/run-command.c b/run-command.c
index 2ffbd7e..9e36151 100644
--- a/run-command.c
+++ b/run-command.c
@@ -159,6 +159,23 @@ int is_executable(const char *name)
return st.st_mode & S_IXUSR;
+ * Search $PATH for a command. This emulates the path search that
+ * execvp would perform, without actually executing the command so it
+ * can be used before fork() to prepare to run a command using
+ * execve() or after execvp() to diagnose why it failed.
+ *
+ * The caller should ensure that file contains no directory
+ * separators.
+ *
+ * Returns the path to the command, as found in $PATH or NULL if the
+ * command could not be found. The caller inherits ownership of the memory
+ * used to store the resultant path.
+ *
+ * This should not be used on Windows, where the $PATH search rules
+ * are more complicated (e.g., a search for "foo" should find
+ * "foo.exe").
+ */
static char *locate_in_PATH(const char *file)
const char *p = getenv("PATH");
@@ -179,7 +196,7 @@ static char *locate_in_PATH(const char *file)
strbuf_addstr(&buf, file);
- if (!access(buf.buf, F_OK))
+ if (is_executable(buf.buf))
return strbuf_detach(&buf, NULL);
if (!*end)