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>

Brandon Williams committed Apr 19, 2017 at 16:13 UTC 79319b1949f0055bd42bac7fa398fca8c2f26116
1 file changed +89 -32
run-command.c
+89 -32
@@ -211,14 +211,82 @@ static const char **prepare_shell_cmd(struct argv_array *out, const char **argv)
211 #ifndef GIT_WINDOWS_NATIVE
212 static int child_notifier = -1;
213
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)
227 {
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);
290 }
291
292 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)
409 code += 128;
410 } else if (WIFEXITED(status)) {
411 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 - }
412 } else {
413 error("waitpid is confused (%s)", argv0);
414 }
@@ -435,6 +496,7 @@ fail_pipe:
496 int null_fd = -1;
497 char **childenv;
498 struct argv_array argv = ARGV_ARRAY_INIT;
499 + struct child_err cerr;
500
501 if (pipe(notify_pipe))
502 notify_pipe[0] = notify_pipe[1] = -1;
@@ -453,20 +515,16 @@ fail_pipe:
515 failed_errno = errno;
516 if (!cmd->pid) {
517 /*
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.
520 */
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);
524
525 close(notify_pipe[0]);
526 set_cloexec(notify_pipe[1]);
527 child_notifier = notify_pipe[1];
469 - atexit(notify_parent);
528
529 if (cmd->no_stdin)
530 dup2(null_fd, 0);
@@ -501,8 +559,7 @@ fail_pipe:
559 }
560
561 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);
563
564 /*
565 * Attempt to exec using the command and arguments starting at
@@ -517,12 +574,11 @@ fail_pipe:
574 (char *const *) childenv);
575
576 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);
580 } else {
525 - die_errno("cannot exec '%s'", cmd->argv[0]);
581 + child_die(CHILD_ERR_ERRNO);
582 }
583 }
584 if (cmd->pid < 0)
@@ -533,17 +589,18 @@ fail_pipe:
589 /*
590 * Wait for child's exec. If the exec succeeds (or if fork()
591 * 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.
593 * Note that use of this infrastructure is completely advisory,
594 * therefore, we keep error checks minimal.
595 */
596 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)) {
598 /*
599 * At this point we know that fork() succeeded, but exec()
600 * failed. Errors have been reported to our stderr.
601 */
602 wait_or_whine(cmd->pid, cmd->argv[0], 0);
603 + child_err_spew(cmd, &cerr);
604 failed_errno = errno;
605 cmd->pid = -1;
606 }