From 6d5e65b9df33f9e8b36a15d86680dbd1f2201e29 Mon Sep 17 00:00:00 2001 From: Stephen Heumann Date: Sun, 9 Nov 2014 18:49:58 -0600 Subject: [PATCH] Fix corruption and hanging resulting from using _exit in forked processes by calling QuitGS directly instead. GNO's _exit (contrary to its man page) does clean-up for stuff like the memory allocator, which is inappropriate in a forked child process and leads to hangs and crashes. --- include/libbb.h | 7 +++++++ shell/hush.c | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/include/libbb.h b/include/libbb.h index 1e75e470d..c54357a48 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -1641,6 +1641,13 @@ int signal_parent_to_resume(void); /* Get path of current executable */ char *get_exec_path(void); +/* On GNO, we need to use this wrapper because _exit is broken when + * called from forked child processes. */ +#ifdef __GNO__ +void _exit_wrapper(int status) NORETURN; +# define _exit(s) _exit_wrapper(s) +#endif + /* Simple unit-testing framework */ typedef void (*bbunit_testfunc)(void); diff --git a/shell/hush.c b/shell/hush.c index 598cb9f5a..c86adb7d6 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -1356,6 +1356,43 @@ int signal_parent_to_resume(void) { return 0; } + +#ifdef __GNO__ +#undef _exit + +#include +static QuitRecGS quitRec = {0, NULL, 0}; + +/* _exit (contrary to its documentation) performs clean-up that's + * inappropriate for a forked child process (this usually results + * in corruption of the memory allocator state, and maybe other + * problems), so we define our own version without this problem. + */ +void _exit_wrapper(int status) { + if (getpid() == G.last_execed_pid) { + // We're the root shell or one that's been exec'd... + // Call regular _exit() + _exit(status); + } else { + // We're a forked child. Just call QuitGS. We do it in + // assembly so we can push the return value on the stack. + while (1) { + asm { + lda status + pha + jsl 0xE100A8 + dcw 0x2029 /* QuitGS */ + dcl quitRec; + pla + } + } + } +} + +#define _exit(s) _exit_wrapper(s) +#endif + + /* Basic theory of signal handling in shell * ======================================== * This does not describe what hush does, rather, it is current understanding