Skip to content

Commit e3a4344

Browse files
bmwillgitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 3967e25 commit e3a4344

File tree

1 file changed

+29
-1
lines changed

1 file changed

+29
-1
lines changed

run-command.c

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,12 @@ static void prepare_cmd(struct argv_array *out, const struct child_process *cmd)
238238
if (!cmd->argv[0])
239239
die("BUG: command is empty");
240240

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+
241247
if (cmd->git_cmd) {
242248
argv_array_push(out, "git");
243249
argv_array_pushv(out, cmd->argv);
@@ -246,6 +252,20 @@ static void prepare_cmd(struct argv_array *out, const struct child_process *cmd)
246252
} else {
247253
argv_array_pushv(out, cmd->argv);
248254
}
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+
}
249269
}
250270
#endif
251271

@@ -445,7 +465,15 @@ int start_command(struct child_process *cmd)
445465
}
446466
}
447467

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);
449477

450478
if (errno == ENOENT) {
451479
if (!cmd->silent_exec_failure)

0 commit comments

Comments
 (0)