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>

Brandon Williams committed Apr 19, 2017 at 16:13 UTC e3a434468fecca7c14a6bef32050dfa60534fde6
1 file changed +29 -1
run-command.c
+29 -1
@@ -238,6 +238,12 @@ static void prepare_cmd(struct argv_array *out, const struct child_process *cmd)
238 if (!cmd->argv[0])
239 die("BUG: command is empty");
240
241 + /*
242 + * Add SHELL_PATH so in the event exec fails with ENOEXEC we can
243 + * attempt to interpret the command with 'sh'.
244 + */
245 + argv_array_push(out, SHELL_PATH);
246 +
247 if (cmd->git_cmd) {
248 argv_array_push(out, "git");
249 argv_array_pushv(out, cmd->argv);
@@ -246,6 +252,20 @@ static void prepare_cmd(struct argv_array *out, const struct child_process *cmd)
252 } else {
253 argv_array_pushv(out, cmd->argv);
254 }
255 +
256 + /*
257 + * If there are no '/' characters in the command then perform a path
258 + * lookup and use the resolved path as the command to exec. If there
259 + * are no '/' characters or if the command wasn't found in the path,
260 + * have exec attempt to invoke the command directly.
261 + */
262 + if (!strchr(out->argv[1], '/')) {
263 + char *program = locate_in_PATH(out->argv[1]);
264 + if (program) {
265 + free((char *)out->argv[1]);
266 + out->argv[1] = program;
267 + }
268 + }
269 }
270 #endif
271
@@ -445,7 +465,15 @@ fail_pipe:
465 }
466 }
467
448 - sane_execvp(argv.argv[0], (char *const *) argv.argv);
468 + /*
469 + * Attempt to exec using the command and arguments starting at
470 + * argv.argv[1]. argv.argv[0] contains SHELL_PATH which will
471 + * be used in the event exec failed with ENOEXEC at which point
472 + * we will try to interpret the command using 'sh'.
473 + */
474 + execv(argv.argv[1], (char *const *) argv.argv + 1);
475 + if (errno == ENOEXEC)
476 + execv(argv.argv[0], (char *const *) argv.argv);
477
478 if (errno == ENOENT) {
479 if (!cmd->silent_exec_failure)