Skip to content

Commit 79319b1

Browse files
bmwillgitster
authored andcommitted
run-command: eliminate calls to error handling functions in child
All of our standard error handling paths have the potential to call malloc or take stdio locks; so we must avoid them inside the forked child. Instead, the child only writes an 8 byte struct atomically to the parent through the notification pipe to propagate an error. All user-visible error reporting happens from the parent; even avoiding functions like atexit(3) and exit(3). Helped-by: Eric Wong <e@80x24.org> Signed-off-by: Brandon Williams <bmwill@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent db015a2 commit 79319b1

1 file changed

Lines changed: 89 additions & 32 deletions

File tree

run-command.c

Lines changed: 89 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -211,14 +211,82 @@ static const char **prepare_shell_cmd(struct argv_array *out, const char **argv)
211211
#ifndef GIT_WINDOWS_NATIVE
212212
static int child_notifier = -1;
213213

214-
static void notify_parent(void)
214+
enum child_errcode {
215+
CHILD_ERR_CHDIR,
216+
CHILD_ERR_ENOENT,
217+
CHILD_ERR_SILENT,
218+
CHILD_ERR_ERRNO
219+
};
220+
221+
struct child_err {
222+
enum child_errcode err;
223+
int syserr; /* errno */
224+
};
225+
226+
static void child_die(enum child_errcode err)
215227
{
216-
/*
217-
* execvp failed. If possible, we'd like to let start_command
218-
* know, so failures like ENOENT can be handled right away; but
219-
* otherwise, finish_command will still report the error.
220-
*/
221-
xwrite(child_notifier, "", 1);
228+
struct child_err buf;
229+
230+
buf.err = err;
231+
buf.syserr = errno;
232+
233+
/* write(2) on buf smaller than PIPE_BUF (min 512) is atomic: */
234+
xwrite(child_notifier, &buf, sizeof(buf));
235+
_exit(1);
236+
}
237+
238+
/*
239+
* parent will make it look like the child spewed a fatal error and died
240+
* this is needed to prevent changes to t0061.
241+
*/
242+
static void fake_fatal(const char *err, va_list params)
243+
{
244+
vreportf("fatal: ", err, params);
245+
}
246+
247+
static void child_error_fn(const char *err, va_list params)
248+
{
249+
const char msg[] = "error() should not be called in child\n";
250+
xwrite(2, msg, sizeof(msg) - 1);
251+
}
252+
253+
static void child_warn_fn(const char *err, va_list params)
254+
{
255+
const char msg[] = "warn() should not be called in child\n";
256+
xwrite(2, msg, sizeof(msg) - 1);
257+
}
258+
259+
static void NORETURN child_die_fn(const char *err, va_list params)
260+
{
261+
const char msg[] = "die() should not be called in child\n";
262+
xwrite(2, msg, sizeof(msg) - 1);
263+
_exit(2);
264+
}
265+
266+
/* this runs in the parent process */
267+
static void child_err_spew(struct child_process *cmd, struct child_err *cerr)
268+
{
269+
static void (*old_errfn)(const char *err, va_list params);
270+
271+
old_errfn = get_error_routine();
272+
set_error_routine(fake_fatal);
273+
errno = cerr->syserr;
274+
275+
switch (cerr->err) {
276+
case CHILD_ERR_CHDIR:
277+
error_errno("exec '%s': cd to '%s' failed",
278+
cmd->argv[0], cmd->dir);
279+
break;
280+
case CHILD_ERR_ENOENT:
281+
error_errno("cannot run %s", cmd->argv[0]);
282+
break;
283+
case CHILD_ERR_SILENT:
284+
break;
285+
case CHILD_ERR_ERRNO:
286+
error_errno("cannot exec '%s'", cmd->argv[0]);
287+
break;
288+
}
289+
set_error_routine(old_errfn);
222290
}
223291

224292
static void prepare_cmd(struct argv_array *out, const struct child_process *cmd)
@@ -341,13 +409,6 @@ static int wait_or_whine(pid_t pid, const char *argv0, int in_signal)
341409
code += 128;
342410
} else if (WIFEXITED(status)) {
343411
code = WEXITSTATUS(status);
344-
/*
345-
* Convert special exit code when execvp failed.
346-
*/
347-
if (code == 127) {
348-
code = -1;
349-
failed_errno = ENOENT;
350-
}
351412
} else {
352413
error("waitpid is confused (%s)", argv0);
353414
}
@@ -435,6 +496,7 @@ int start_command(struct child_process *cmd)
435496
int null_fd = -1;
436497
char **childenv;
437498
struct argv_array argv = ARGV_ARRAY_INIT;
499+
struct child_err cerr;
438500

439501
if (pipe(notify_pipe))
440502
notify_pipe[0] = notify_pipe[1] = -1;
@@ -453,20 +515,16 @@ int start_command(struct child_process *cmd)
453515
failed_errno = errno;
454516
if (!cmd->pid) {
455517
/*
456-
* Redirect the channel to write syscall error messages to
457-
* before redirecting the process's stderr so that all die()
458-
* in subsequent call paths use the parent's stderr.
518+
* Ensure the default die/error/warn routines do not get
519+
* called, they can take stdio locks and malloc.
459520
*/
460-
if (cmd->no_stderr || need_err) {
461-
int child_err = dup(2);
462-
set_cloexec(child_err);
463-
set_error_handle(fdopen(child_err, "w"));
464-
}
521+
set_die_routine(child_die_fn);
522+
set_error_routine(child_error_fn);
523+
set_warn_routine(child_warn_fn);
465524

466525
close(notify_pipe[0]);
467526
set_cloexec(notify_pipe[1]);
468527
child_notifier = notify_pipe[1];
469-
atexit(notify_parent);
470528

471529
if (cmd->no_stdin)
472530
dup2(null_fd, 0);
@@ -501,8 +559,7 @@ int start_command(struct child_process *cmd)
501559
}
502560

503561
if (cmd->dir && chdir(cmd->dir))
504-
die_errno("exec '%s': cd to '%s' failed", cmd->argv[0],
505-
cmd->dir);
562+
child_die(CHILD_ERR_CHDIR);
506563

507564
/*
508565
* Attempt to exec using the command and arguments starting at
@@ -517,12 +574,11 @@ int start_command(struct child_process *cmd)
517574
(char *const *) childenv);
518575

519576
if (errno == ENOENT) {
520-
if (!cmd->silent_exec_failure)
521-
error("cannot run %s: %s", cmd->argv[0],
522-
strerror(ENOENT));
523-
exit(127);
577+
if (cmd->silent_exec_failure)
578+
child_die(CHILD_ERR_SILENT);
579+
child_die(CHILD_ERR_ENOENT);
524580
} else {
525-
die_errno("cannot exec '%s'", cmd->argv[0]);
581+
child_die(CHILD_ERR_ERRNO);
526582
}
527583
}
528584
if (cmd->pid < 0)
@@ -533,17 +589,18 @@ int start_command(struct child_process *cmd)
533589
/*
534590
* Wait for child's exec. If the exec succeeds (or if fork()
535591
* failed), EOF is seen immediately by the parent. Otherwise, the
536-
* child process sends a single byte.
592+
* child process sends a child_err struct.
537593
* Note that use of this infrastructure is completely advisory,
538594
* therefore, we keep error checks minimal.
539595
*/
540596
close(notify_pipe[1]);
541-
if (read(notify_pipe[0], &notify_pipe[1], 1) == 1) {
597+
if (xread(notify_pipe[0], &cerr, sizeof(cerr)) == sizeof(cerr)) {
542598
/*
543599
* At this point we know that fork() succeeded, but exec()
544600
* failed. Errors have been reported to our stderr.
545601
*/
546602
wait_or_whine(cmd->pid, cmd->argv[0], 0);
603+
child_err_spew(cmd, &cerr);
547604
failed_errno = errno;
548605
cmd->pid = -1;
549606
}

0 commit comments

Comments
 (0)