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! Reported-by: Brian Hatfield <bhatfield@xxxxxxxxxx> Signed-off-by: Brandon Williams <bmwill@xxxxxxxxxx> --- run-command.c | 2 +- t/t0061-run-command.sh | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/run-command.c b/run-command.c index a97d7bf9f..ec08e0951 100644 --- a/run-command.c +++ b/run-command.c @@ -137,7 +137,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) diff --git a/t/t0061-run-command.sh b/t/t0061-run-command.sh index 98c09dd98..fd5e43766 100755 --- a/t/t0061-run-command.sh +++ b/t/t0061-run-command.sh @@ -37,6 +37,29 @@ test_expect_success !MINGW 'run_command can run a script without a #! line' ' test_cmp empty err ' +test_expect_success 'run_command should not try to execute a directory' ' + test_when_finished "rm -rf bin1 bin2 bin3" && + mkdir -p bin1/greet bin2 bin3 && + write_script bin2/greet <<-\EOF && + cat bin2/greet + EOF + chmod -x bin2/greet && + write_script bin3/greet <<-\EOF && + cat bin3/greet + EOF + + # Test that run-command does not try to execute the "greet" directory in + # "bin1", or the non-executable file "greet" in "bin2", but rather + # correcty executes the "greet" script located in bin3. + ( + PATH=$PWD/bin1:$PWD/bin2:$PWD/bin3:$PATH && + export PATH && + test-run-command run-command greet >actual 2>err + ) && + test_cmp bin3/greet actual && + test_cmp empty err +' + test_expect_success POSIXPERM 'run_command reports EACCES' ' cat hello-script >hello.sh && chmod -x hello.sh && -- 2.13.0.rc0.306.g87b477812d-goog