run-command: prepare command before forking
According to [1] we need to only call async-signal-safe operations between fork and exec. Using malloc to build the argv array isn't async-signal-safe. In order to avoid allocation between 'fork()' and 'exec()' prepare the argv array used in the exec call prior to forking the process. [1] http://pubs.opengroup.org/onlinepubs/009695399/functions/fork.html Signed-off-by: Brandon Williams <bmwill@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
c2d3119d7b
commit
3967e25be1
@ -220,18 +220,6 @@ static const char **prepare_shell_cmd(struct argv_array *out, const char **argv)
|
||||
return out->argv;
|
||||
}
|
||||
|
||||
#ifndef GIT_WINDOWS_NATIVE
|
||||
static int execv_shell_cmd(const char **argv)
|
||||
{
|
||||
struct argv_array nargv = ARGV_ARRAY_INIT;
|
||||
prepare_shell_cmd(&nargv, argv);
|
||||
trace_argv_printf(nargv.argv, "trace: exec:");
|
||||
sane_execvp(nargv.argv[0], (char **)nargv.argv);
|
||||
argv_array_clear(&nargv);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef GIT_WINDOWS_NATIVE
|
||||
static int child_notifier = -1;
|
||||
|
||||
@ -244,6 +232,21 @@ static void notify_parent(void)
|
||||
*/
|
||||
xwrite(child_notifier, "", 1);
|
||||
}
|
||||
|
||||
static void prepare_cmd(struct argv_array *out, const struct child_process *cmd)
|
||||
{
|
||||
if (!cmd->argv[0])
|
||||
die("BUG: command is empty");
|
||||
|
||||
if (cmd->git_cmd) {
|
||||
argv_array_push(out, "git");
|
||||
argv_array_pushv(out, cmd->argv);
|
||||
} else if (cmd->use_shell) {
|
||||
prepare_shell_cmd(out, cmd->argv);
|
||||
} else {
|
||||
argv_array_pushv(out, cmd->argv);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void set_cloexec(int fd)
|
||||
@ -372,9 +375,13 @@ fail_pipe:
|
||||
#ifndef GIT_WINDOWS_NATIVE
|
||||
{
|
||||
int notify_pipe[2];
|
||||
struct argv_array argv = ARGV_ARRAY_INIT;
|
||||
|
||||
if (pipe(notify_pipe))
|
||||
notify_pipe[0] = notify_pipe[1] = -1;
|
||||
|
||||
prepare_cmd(&argv, cmd);
|
||||
|
||||
cmd->pid = fork();
|
||||
failed_errno = errno;
|
||||
if (!cmd->pid) {
|
||||
@ -437,12 +444,9 @@ fail_pipe:
|
||||
unsetenv(*cmd->env);
|
||||
}
|
||||
}
|
||||
if (cmd->git_cmd)
|
||||
execv_git_cmd(cmd->argv);
|
||||
else if (cmd->use_shell)
|
||||
execv_shell_cmd(cmd->argv);
|
||||
else
|
||||
sane_execvp(cmd->argv[0], (char *const*) cmd->argv);
|
||||
|
||||
sane_execvp(argv.argv[0], (char *const *) argv.argv);
|
||||
|
||||
if (errno == ENOENT) {
|
||||
if (!cmd->silent_exec_failure)
|
||||
error("cannot run %s: %s", cmd->argv[0],
|
||||
@ -458,7 +462,7 @@ fail_pipe:
|
||||
mark_child_for_cleanup(cmd->pid, cmd);
|
||||
|
||||
/*
|
||||
* Wait for child's execvp. If the execvp succeeds (or if fork()
|
||||
* Wait for child's exec. If the exec succeeds (or if fork()
|
||||
* failed), EOF is seen immediately by the parent. Otherwise, the
|
||||
* child process sends a single byte.
|
||||
* Note that use of this infrastructure is completely advisory,
|
||||
@ -467,7 +471,7 @@ fail_pipe:
|
||||
close(notify_pipe[1]);
|
||||
if (read(notify_pipe[0], ¬ify_pipe[1], 1) == 1) {
|
||||
/*
|
||||
* At this point we know that fork() succeeded, but execvp()
|
||||
* At this point we know that fork() succeeded, but exec()
|
||||
* failed. Errors have been reported to our stderr.
|
||||
*/
|
||||
wait_or_whine(cmd->pid, cmd->argv[0], 0);
|
||||
@ -475,6 +479,8 @@ fail_pipe:
|
||||
cmd->pid = -1;
|
||||
}
|
||||
close(notify_pipe[0]);
|
||||
|
||||
argv_array_clear(&argv);
|
||||
}
|
||||
#else
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user