summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrandon Williams <bmwill@google.com>2017-04-19 16:13:20 -0700
committerJunio C Hamano <gitster@pobox.com>2017-04-20 17:55:32 -0700
commite3a434468fecca7c14a6bef32050dfa60534fde6 (patch)
treeec737bb9ea936b0a8ed4fdc155ee5d8ebd91a594
parent3967e25be11ab96ced71f16b9f082de270a518db (diff)
downloadgit-e3a434468fecca7c14a6bef32050dfa60534fde6.tar.gz
run-command: use the async-signal-safe execv instead of execvp
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>
-rw-r--r--run-command.c30
1 files changed, 29 insertions, 1 deletions
diff --git a/run-command.c b/run-command.c
index d8d1437954..1c7a3b6110 100644
--- a/run-command.c
+++ b/run-command.c
@@ -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);
@@ -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
@@ -445,7 +465,15 @@ fail_pipe:
}
}
- 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)