diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2020-05-29 06:12:17 -0700 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2020-05-29 06:12:17 -0700 |
commit | 64ba99161ac55d77b09a72a0a64f2333ab7f0ffb (patch) | |
tree | 1a88d6554cbd9185856eef986ffde7c35bdca7a1 /signal.c | |
parent | 69fd1c3054df29be26299b4b23bac29e2c2bceb5 (diff) | |
download | txr-64ba99161ac55d77b09a72a0a64f2333ab7f0ffb.tar.gz txr-64ba99161ac55d77b09a72a0a64f2333ab7f0ffb.tar.bz2 txr-64ba99161ac55d77b09a72a0a64f2333ab7f0ffb.zip |
signal: bugfix: sharing of alt stack.
The alternative stack is shared by SIGSEGV and SIGBUS.
Therefore, we cannot tear it down when disabling either
signal; the other may be using it.
* signal.c (stack_refcount): New static variable.
(setup_alt_stack, teardown_alt_stack): Functions removed.
(addref_alt_stack, release_alt_stack): New functions to manage
stack with reference counting.
(set_sig_handler): Rearrange the code to only call addref_alt_stack
when transitioning from no-handler to handler for SIGSEGV and
SIGBUS and to only call release_alt_stack when transitioning
from handler to no-handler for these signals.
Diffstat (limited to 'signal.c')
-rw-r--r-- | signal.c | 74 |
1 files changed, 36 insertions, 38 deletions
@@ -211,40 +211,42 @@ void sig_init(void) #if HAVE_SIGALTSTACK static mem_t *stack; +int stack_refcount; -static void setup_alt_stack(void) +static void addref_alt_stack(void) { - stack_t ss; - - if (!stack) + if (stack_refcount++ == 0) { + stack_t ss; stack = chk_malloc(SIGSTKSZ); + ss.ss_sp = stack; + ss.ss_size = SIGSTKSZ; + ss.ss_flags = 0; - ss.ss_sp = stack; - ss.ss_size = SIGSTKSZ; - ss.ss_flags = 0; - - if (sigaltstack(&ss, NULL) == -1) { - free(stack); - stack = 0; + if (sigaltstack(&ss, NULL) == -1) { + free(stack); + stack = 0; + } } } -static void teardown_alt_stack(void) +static void release_alt_stack(void) { - stack_t ss; + if (--stack_refcount == 0) { + stack_t ss; - if (!stack) - return; + if (!stack) + return; - ss.ss_sp = stack; - ss.ss_size = SIGSTKSZ; - ss.ss_flags = SS_DISABLE; + ss.ss_sp = stack; + ss.ss_size = SIGSTKSZ; + ss.ss_flags = SS_DISABLE; - if (sigaltstack(&ss, NULL) == -1) - return; + if (sigaltstack(&ss, NULL) == -1) + return; - free(stack); - stack = 0; + free(stack); + stack = 0; + } } #endif @@ -259,7 +261,7 @@ val set_sig_handler(val signo, val lambda) static struct sigaction blank; val self = lit("set-sig-handler"); cnum sig = c_num(signo); - val old_lambda; + val old; small_sigset_t block, saved; small_sigfillset(&block); @@ -268,17 +270,18 @@ val set_sig_handler(val signo, val lambda) if (sig < 0 || sig >= MAX_SIG) uw_throwf(error_s, lit("~a: signal ~s out of range"), self, sig, nao); - old_lambda = sig_lambda[sig]; + old = sig_lambda[sig]; - if (lambda != old_lambda) { + if (lambda != old) { unsigned long mask = 1UL << sig; - if (lambda == nil) { - signal(sig, SIG_IGN); - sig_deferred &= ~mask; - } else if (lambda == t) { - signal(sig, SIG_DFL); + if (lambda == nil || lambda == t) { + signal(sig, if3(lambda, SIG_DFL, SIG_IGN)); sig_deferred &= ~mask; +#if HAVE_SIGALTSTACK + if ((sig == SIGSEGV || sig == SIGBUS) && old != t && old != nil) + release_alt_stack(); +#endif } else { struct sigaction sa = blank; @@ -288,25 +291,20 @@ val set_sig_handler(val signo, val lambda) sa.sa_handler = sig_handler; sigfillset(&sa.sa_mask); #if HAVE_SIGALTSTACK - if (sig == SIGSEGV || sig == SIGBUS) { - setup_alt_stack(); + if ((sig == SIGSEGV || sig == SIGBUS) && (old == t || old == nil)) { + addref_alt_stack(); sa.sa_flags |= SA_ONSTACK; } #endif sigaction(sig, &sa, 0); } -#if HAVE_SIGALTSTACK - if ((sig == SIGSEGV || sig == SIGBUS) && (lambda == nil || lambda == t)) - teardown_alt_stack(); -#endif - sig_lambda[sig] = lambda; } sig_mask(SIG_SETMASK, &saved, 0); - return old_lambda; + return old; } val get_sig_handler(val signo) |