Skip to content

Commit 321fd82

Browse files
peffgitster
authored andcommitted
run-command: mark path lookup errors with ENOENT
Since commit e3a4344 (run-command: use the async-signal-safe execv instead of execvp, 2017-04-19), prepare_cmd() does its own PATH lookup for any commands we run (on non-Windows platforms). However, its logic does not match the old execvp call when we fail to find a matching entry in the PATH. Instead of feeding the name directly to execv, execvp would consider that an ENOENT error. By continuing and passing the name directly to execv, we effectively behave as if "." was included at the end of the PATH. This can have confusing and even dangerous results. The fix itself is pretty straight-forward. There's a new test in t0061 to cover this explicitly, and I've also added a duplicate of the ENOENT test to ensure that we return the correct errno for this case. Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent d0832b2 commit 321fd82

File tree

2 files changed

+29
-5
lines changed

2 files changed

+29
-5
lines changed

run-command.c

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ static void child_err_spew(struct child_process *cmd, struct child_err *cerr)
378378
set_error_routine(old_errfn);
379379
}
380380

381-
static void prepare_cmd(struct argv_array *out, const struct child_process *cmd)
381+
static int prepare_cmd(struct argv_array *out, const struct child_process *cmd)
382382
{
383383
if (!cmd->argv[0])
384384
die("BUG: command is empty");
@@ -401,16 +401,22 @@ static void prepare_cmd(struct argv_array *out, const struct child_process *cmd)
401401
/*
402402
* If there are no '/' characters in the command then perform a path
403403
* lookup and use the resolved path as the command to exec. If there
404-
* are no '/' characters or if the command wasn't found in the path,
405-
* have exec attempt to invoke the command directly.
404+
* are '/' characters, we have exec attempt to invoke the command
405+
* directly.
406406
*/
407407
if (!strchr(out->argv[1], '/')) {
408408
char *program = locate_in_PATH(out->argv[1]);
409409
if (program) {
410410
free((char *)out->argv[1]);
411411
out->argv[1] = program;
412+
} else {
413+
argv_array_clear(out);
414+
errno = ENOENT;
415+
return -1;
412416
}
413417
}
418+
419+
return 0;
414420
}
415421

416422
static char **prep_childenv(const char *const *deltaenv)
@@ -635,6 +641,12 @@ int start_command(struct child_process *cmd)
635641
struct child_err cerr;
636642
struct atfork_state as;
637643

644+
if (prepare_cmd(&argv, cmd) < 0) {
645+
failed_errno = errno;
646+
cmd->pid = -1;
647+
goto end_of_spawn;
648+
}
649+
638650
if (pipe(notify_pipe))
639651
notify_pipe[0] = notify_pipe[1] = -1;
640652

@@ -645,7 +657,6 @@ int start_command(struct child_process *cmd)
645657
set_cloexec(null_fd);
646658
}
647659

648-
prepare_cmd(&argv, cmd);
649660
childenv = prep_childenv(cmd->env);
650661
atfork_prepare(&as);
651662

@@ -773,6 +784,8 @@ int start_command(struct child_process *cmd)
773784
argv_array_clear(&argv);
774785
free(childenv);
775786
}
787+
end_of_spawn:
788+
776789
#else
777790
{
778791
int fhin = 0, fhout = 1, fherr = 2;

t/t0061-run-command.sh

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,14 @@ cat >hello-script <<-EOF
1313
EOF
1414
>empty
1515

16-
test_expect_success 'start_command reports ENOENT' '
16+
test_expect_success 'start_command reports ENOENT (slash)' '
1717
test-run-command start-command-ENOENT ./does-not-exist
1818
'
1919

20+
test_expect_success 'start_command reports ENOENT (no slash)' '
21+
test-run-command start-command-ENOENT does-not-exist
22+
'
23+
2024
test_expect_success 'run_command can run a command' '
2125
cat hello-script >hello.sh &&
2226
chmod +x hello.sh &&
@@ -26,6 +30,13 @@ test_expect_success 'run_command can run a command' '
2630
test_cmp empty err
2731
'
2832

33+
test_expect_success 'run_command is restricted to PATH' '
34+
write_script should-not-run <<-\EOF &&
35+
echo yikes
36+
EOF
37+
test_must_fail test-run-command run-command should-not-run
38+
'
39+
2940
test_expect_success !MINGW 'run_command can run a script without a #! line' '
3041
cat >hello <<-\EOF &&
3142
cat hello-script

0 commit comments

Comments
 (0)