Skip to content

Commit

Permalink
run-command: use the async-signal-safe execv instead of execvp
Browse files Browse the repository at this point in the history
Convert the function used to exec from 'execvp()' to 'execv()' as the (p)
variant of exec isn't async-signal-safe and has the potential to call malloc
during the path resolution it performs.  Instead we simply do the path
resolution ourselves during the preparation stage prior to forking.  There also
don't exist any portable (p) variants which also take in an environment to use
in the exec'd process.  This allows easy migration to using 'execve()' in a
future patch.

Also, as noted in [1], in the event of an ENOEXEC the (p) variants of
exec will attempt to execute the command by interpreting it with the
'sh' utility.  To maintain this functionality, if 'execv()' fails with
ENOEXEC, start_command will atempt to execute the command by
interpreting it with 'sh'.

[1] http://pubs.opengroup.org/onlinepubs/009695399/functions/exec.html

Signed-off-by: Brandon Williams <bmwill@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
bmwill authored and gitster committed Apr 21, 2017
1 parent 3967e25 commit e3a4344
Showing 1 changed file with 29 additions and 1 deletion.
30 changes: 29 additions & 1 deletion run-command.c
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,12 @@ static void prepare_cmd(struct argv_array *out, const struct child_process *cmd)
if (!cmd->argv[0])
die("BUG: command is empty");

/*
* Add SHELL_PATH so in the event exec fails with ENOEXEC we can
* attempt to interpret the command with 'sh'.
*/
argv_array_push(out, SHELL_PATH);

if (cmd->git_cmd) {
argv_array_push(out, "git");
argv_array_pushv(out, cmd->argv);
Expand All @@ -246,6 +252,20 @@ static void prepare_cmd(struct argv_array *out, const struct child_process *cmd)
} else {
argv_array_pushv(out, cmd->argv);
}

/*
* If there are no '/' characters in the command then perform a path
* lookup and use the resolved path as the command to exec. If there
* are no '/' characters or if the command wasn't found in the path,
* have exec attempt to invoke the command directly.
*/
if (!strchr(out->argv[1], '/')) {
char *program = locate_in_PATH(out->argv[1]);
if (program) {
free((char *)out->argv[1]);
out->argv[1] = program;
}
}
}
#endif

Expand Down Expand Up @@ -445,7 +465,15 @@ int start_command(struct child_process *cmd)
}
}

sane_execvp(argv.argv[0], (char *const *) argv.argv);
/*
* Attempt to exec using the command and arguments starting at
* argv.argv[1]. argv.argv[0] contains SHELL_PATH which will
* be used in the event exec failed with ENOEXEC at which point
* we will try to interpret the command using 'sh'.
*/
execv(argv.argv[1], (char *const *) argv.argv + 1);
if (errno == ENOEXEC)
execv(argv.argv[0], (char *const *) argv.argv);

if (errno == ENOENT) {
if (!cmd->silent_exec_failure)
Expand Down

0 comments on commit e3a4344

Please sign in to comment.