|
|
6743a1 |
From 1e1167f676252c220fbee6038715157c457c7d2f Mon Sep 17 00:00:00 2001
|
|
|
6743a1 |
From: Jeff King <peff@peff.net>
|
|
|
6743a1 |
Date: Wed, 24 Oct 2018 03:38:00 -0400
|
|
|
6743a1 |
Subject: [PATCH] run-command: mark path lookup errors with ENOENT
|
|
|
6743a1 |
|
|
|
6743a1 |
Since commit e3a434468f (run-command: use the
|
|
|
6743a1 |
async-signal-safe execv instead of execvp, 2017-04-19),
|
|
|
6743a1 |
prepare_cmd() does its own PATH lookup for any commands we
|
|
|
6743a1 |
run (on non-Windows platforms).
|
|
|
6743a1 |
|
|
|
6743a1 |
However, its logic does not match the old execvp call when
|
|
|
6743a1 |
we fail to find a matching entry in the PATH. Instead of
|
|
|
6743a1 |
feeding the name directly to execv, execvp would consider
|
|
|
6743a1 |
that an ENOENT error. By continuing and passing the name
|
|
|
6743a1 |
directly to execv, we effectively behave as if "." was
|
|
|
6743a1 |
included at the end of the PATH. This can have confusing and
|
|
|
6743a1 |
even dangerous results.
|
|
|
6743a1 |
|
|
|
6743a1 |
The fix itself is pretty straight-forward. There's a new
|
|
|
6743a1 |
test in t0061 to cover this explicitly, and I've also added
|
|
|
6743a1 |
a duplicate of the ENOENT test to ensure that we return the
|
|
|
6743a1 |
correct errno for this case.
|
|
|
6743a1 |
|
|
|
6743a1 |
Signed-off-by: Jeff King <peff@peff.net>
|
|
|
6743a1 |
Signed-off-by: Junio C Hamano <gitster@pobox.com>
|
|
|
6743a1 |
---
|
|
|
6743a1 |
run-command.c | 21 +++++++++++++++++----
|
|
|
6743a1 |
t/t0061-run-command.sh | 13 ++++++++++++-
|
|
|
6743a1 |
2 files changed, 29 insertions(+), 5 deletions(-)
|
|
|
6743a1 |
|
|
|
6743a1 |
diff --git a/run-command.c b/run-command.c
|
|
|
6743a1 |
index 84b883c213..d679cc267c 100644
|
|
|
6743a1 |
--- a/run-command.c
|
|
|
6743a1 |
+++ b/run-command.c
|
|
|
6743a1 |
@@ -380,7 +380,7 @@ static void child_err_spew(struct child_process *cmd, struct child_err *cerr)
|
|
|
6743a1 |
set_error_routine(old_errfn);
|
|
|
6743a1 |
}
|
|
|
6743a1 |
|
|
|
6743a1 |
-static void prepare_cmd(struct argv_array *out, const struct child_process *cmd)
|
|
|
6743a1 |
+static int prepare_cmd(struct argv_array *out, const struct child_process *cmd)
|
|
|
6743a1 |
{
|
|
|
6743a1 |
if (!cmd->argv[0])
|
|
|
6743a1 |
BUG("command is empty");
|
|
|
6743a1 |
@@ -403,16 +403,22 @@ static void prepare_cmd(struct argv_array *out, const struct child_process *cmd)
|
|
|
6743a1 |
/*
|
|
|
6743a1 |
* If there are no '/' characters in the command then perform a path
|
|
|
6743a1 |
* lookup and use the resolved path as the command to exec. If there
|
|
|
6743a1 |
- * are no '/' characters or if the command wasn't found in the path,
|
|
|
6743a1 |
- * have exec attempt to invoke the command directly.
|
|
|
6743a1 |
+ * are '/' characters, we have exec attempt to invoke the command
|
|
|
6743a1 |
+ * directly.
|
|
|
6743a1 |
*/
|
|
|
6743a1 |
if (!strchr(out->argv[1], '/')) {
|
|
|
6743a1 |
char *program = locate_in_PATH(out->argv[1]);
|
|
|
6743a1 |
if (program) {
|
|
|
6743a1 |
free((char *)out->argv[1]);
|
|
|
6743a1 |
out->argv[1] = program;
|
|
|
6743a1 |
+ } else {
|
|
|
6743a1 |
+ argv_array_clear(out);
|
|
|
6743a1 |
+ errno = ENOENT;
|
|
|
6743a1 |
+ return -1;
|
|
|
6743a1 |
}
|
|
|
6743a1 |
}
|
|
|
6743a1 |
+
|
|
|
6743a1 |
+ return 0;
|
|
|
6743a1 |
}
|
|
|
6743a1 |
|
|
|
6743a1 |
static char **prep_childenv(const char *const *deltaenv)
|
|
|
6743a1 |
@@ -719,6 +725,12 @@ int start_command(struct child_process *cmd)
|
|
|
6743a1 |
struct child_err cerr;
|
|
|
6743a1 |
struct atfork_state as;
|
|
|
6743a1 |
|
|
|
6743a1 |
+ if (prepare_cmd(&argv, cmd) < 0) {
|
|
|
6743a1 |
+ failed_errno = errno;
|
|
|
6743a1 |
+ cmd->pid = -1;
|
|
|
6743a1 |
+ goto end_of_spawn;
|
|
|
6743a1 |
+ }
|
|
|
6743a1 |
+
|
|
|
6743a1 |
if (pipe(notify_pipe))
|
|
|
6743a1 |
notify_pipe[0] = notify_pipe[1] = -1;
|
|
|
6743a1 |
|
|
|
6743a1 |
@@ -729,7 +741,6 @@ int start_command(struct child_process *cmd)
|
|
|
6743a1 |
set_cloexec(null_fd);
|
|
|
6743a1 |
}
|
|
|
6743a1 |
|
|
|
6743a1 |
- prepare_cmd(&argv, cmd);
|
|
|
6743a1 |
childenv = prep_childenv(cmd->env);
|
|
|
6743a1 |
atfork_prepare(&as);
|
|
|
6743a1 |
|
|
|
6743a1 |
@@ -857,6 +868,8 @@ int start_command(struct child_process *cmd)
|
|
|
6743a1 |
argv_array_clear(&argv);
|
|
|
6743a1 |
free(childenv);
|
|
|
6743a1 |
}
|
|
|
6743a1 |
+end_of_spawn:
|
|
|
6743a1 |
+
|
|
|
6743a1 |
#else
|
|
|
6743a1 |
{
|
|
|
6743a1 |
int fhin = 0, fhout = 1, fherr = 2;
|
|
|
6743a1 |
diff --git a/t/t0061-run-command.sh b/t/t0061-run-command.sh
|
|
|
6743a1 |
index c887ed5b45..b9cfc03a53 100755
|
|
|
6743a1 |
--- a/t/t0061-run-command.sh
|
|
|
6743a1 |
+++ b/t/t0061-run-command.sh
|
|
|
6743a1 |
@@ -13,10 +13,14 @@ cat >hello-script <<-EOF
|
|
|
6743a1 |
EOF
|
|
|
6743a1 |
>empty
|
|
|
6743a1 |
|
|
|
6743a1 |
-test_expect_success 'start_command reports ENOENT' '
|
|
|
6743a1 |
+test_expect_success 'start_command reports ENOENT (slash)' '
|
|
|
6743a1 |
test-tool run-command start-command-ENOENT ./does-not-exist
|
|
|
6743a1 |
'
|
|
|
6743a1 |
|
|
|
6743a1 |
+test_expect_success 'start_command reports ENOENT (no slash)' '
|
|
|
6743a1 |
+ test-tool run-command start-command-ENOENT does-not-exist
|
|
|
6743a1 |
+'
|
|
|
6743a1 |
+
|
|
|
6743a1 |
test_expect_success 'run_command can run a command' '
|
|
|
6743a1 |
cat hello-script >hello.sh &&
|
|
|
6743a1 |
chmod +x hello.sh &&
|
|
|
6743a1 |
@@ -26,6 +30,13 @@ test_expect_success 'run_command can run a command' '
|
|
|
6743a1 |
test_cmp empty err
|
|
|
6743a1 |
'
|
|
|
6743a1 |
|
|
|
6743a1 |
+test_expect_success 'run_command is restricted to PATH' '
|
|
|
6743a1 |
+ write_script should-not-run <<-\EOF &&
|
|
|
6743a1 |
+ echo yikes
|
|
|
6743a1 |
+ EOF
|
|
|
6743a1 |
+ test_must_fail test-tool run-command run-command should-not-run
|
|
|
6743a1 |
+'
|
|
|
6743a1 |
+
|
|
|
6743a1 |
test_expect_success !MINGW 'run_command can run a script without a #! line' '
|
|
|
6743a1 |
cat >hello <<-\EOF &&
|
|
|
6743a1 |
cat hello-script
|
|
|
6743a1 |
--
|
|
|
6743a1 |
2.14.4
|
|
|
6743a1 |
|