summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2020-05-29 06:12:17 -0700
committerKaz Kylheku <kaz@kylheku.com>2020-05-29 06:12:17 -0700
commit64ba99161ac55d77b09a72a0a64f2333ab7f0ffb (patch)
tree1a88d6554cbd9185856eef986ffde7c35bdca7a1
parent69fd1c3054df29be26299b4b23bac29e2c2bceb5 (diff)
downloadtxr-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.
-rw-r--r--signal.c74
1 files changed, 36 insertions, 38 deletions
diff --git a/signal.c b/signal.c
index 06a812c4..cddd46fe 100644
--- a/signal.c
+++ b/signal.c
@@ -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)