diff options
Diffstat (limited to 'shell/hush.c')
-rw-r--r-- | shell/hush.c | 37 |
1 files changed, 28 insertions, 9 deletions
diff --git a/shell/hush.c b/shell/hush.c index 132b974f0..4c348ec2b 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -815,6 +815,7 @@ struct globals { unsigned long memleak_value; int debug_indent; #endif + struct sigaction sa; char user_input_buf[ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 2]; }; #define G (*ptr_to_globals) @@ -823,6 +824,9 @@ struct globals { * is global, thus "G." prefix is a useful hint */ #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ + /* memset(&G.sa, 0, sizeof(G.sa)); */ \ + sigfillset(&G.sa.sa_mask); \ + G.sa.sa_flags = SA_RESTART; \ } while (0) @@ -1414,9 +1418,6 @@ static void restore_G_args(save_arg_t *sv, char **argv) * Standard says "When a subshell is entered, traps that are not being ignored * are set to the default actions". bash interprets it so that traps which * are set to '' (ignore) are NOT reset to defaults. We do the same. - * - * TODO: don't use signal() to install sighandlers: need to mask ALL signals - * while handler runs. I saw signal nesting in one strace, race window isn't small. */ enum { SPECIAL_INTERACTIVE_SIGS = 0 @@ -1444,6 +1445,24 @@ static void record_pending_signo(int sig) #endif } +static sighandler_t install_sighandler(int sig, sighandler_t handler) +{ + struct sigaction old_sa; + + /* We could use signal() to install handlers... almost: + * except that we need to mask ALL signals while handlers run. + * I saw signal nesting in strace, race window isn't small. + * SA_RESTART is also needed, but in Linux, signal() + * sets SA_RESTART too. + */ + /* memset(&G.sa, 0, sizeof(G.sa)); - already done */ + /* sigfillset(&G.sa.sa_mask); - already done */ + /* G.sa.sa_flags = SA_RESTART; - already done */ + G.sa.sa_handler = handler; + sigaction(sig, &G.sa, &old_sa); + return old_sa.sa_handler; +} + #if ENABLE_HUSH_JOB /* After [v]fork, in child: do not restore tty pgrp on xfunc death */ @@ -5451,7 +5470,7 @@ static void switch_off_special_sigs(unsigned mask) G.traps[sig] = NULL; } /* We are here only if no trap or trap was not '' */ - signal(sig, SIG_DFL); + install_sighandler(sig, SIG_DFL); } } @@ -5496,7 +5515,7 @@ static void reset_traps_to_defaults(void) /* There is no signal for trap 0 (EXIT) */ if (sig == 0) continue; - signal(sig, pick_sighandler(sig)); + install_sighandler(sig, pick_sighandler(sig)); } } @@ -7524,7 +7543,7 @@ static void install_sighandlers(unsigned mask) sig++; if (!(mask & 1)) continue; - old_handler = signal(sig, pick_sighandler(sig)); + old_handler = install_sighandler(sig, pick_sighandler(sig)); /* POSIX allows shell to re-enable SIGCHLD * even if it was SIG_IGN on entry. * Therefore we skip IGN check for it: @@ -7533,7 +7552,7 @@ static void install_sighandlers(unsigned mask) continue; if (old_handler == SIG_IGN) { /* oops... restore back to IGN, and record this fact */ - signal(sig, old_handler); + install_sighandler(sig, old_handler); if (!G.traps) G.traps = xzalloc(sizeof(G.traps[0]) * NSIG); free(G.traps[sig]); @@ -7854,7 +7873,7 @@ int hush_main(int argc, char **argv) for (sig = 1; sig < NSIG; sig++) { if (empty_trap_mask & (1LL << sig)) { G.traps[sig] = xzalloc(1); /* == xstrdup(""); */ - signal(sig, SIG_IGN); + install_sighandler(sig, SIG_IGN); } } } @@ -8389,7 +8408,7 @@ static int FAST_FUNC builtin_trap(char **argv) else /* We are removing trap handler */ handler = pick_sighandler(sig); - signal(sig, handler); + install_sighandler(sig, handler); } return ret; } |