summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/exit/abort.c19
-rw-r--r--src/signal/sigaction.c25
2 files changed, 40 insertions, 4 deletions
diff --git a/src/exit/abort.c b/src/exit/abort.c
index ecc0f735..d6bd546b 100644
--- a/src/exit/abort.c
+++ b/src/exit/abort.c
@@ -3,11 +3,30 @@
#include "syscall.h"
#include "pthread_impl.h"
#include "atomic.h"
+#include "libc.h"
+#include "ksigaction.h"
+
+__attribute__((__visibility__("hidden")))
+volatile int __abort_lock[1];
_Noreturn void abort(void)
{
raise(SIGABRT);
+
+ /* If there was a SIGABRT handler installed and it returned, or if
+ * SIGABRT was blocked or ignored, take an AS-safe lock to prevent
+ * sigaction from installing a new SIGABRT handler, uninstall any
+ * handler that may be present, and re-raise the signal to generate
+ * the default action of abnormal termination. */
__block_all_sigs(0);
+ LOCK(__abort_lock);
+ __syscall(SYS_rt_sigaction, SIGABRT,
+ &(struct k_sigaction){.handler = SIG_DFL}, 0, _NSIG/8);
+ __syscall(SYS_tkill, __pthread_self()->tid, SIGABRT);
+ __syscall(SYS_rt_sigprocmask, SIG_UNBLOCK,
+ &(long[_NSIG/(8*sizeof(long))]){1UL<<(SIGABRT-1)}, 0, _NSIG/8);
+
+ /* Beyond this point should be unreachable. */
a_crash();
raise(SIGKILL);
_Exit(127);
diff --git a/src/signal/sigaction.c b/src/signal/sigaction.c
index 6eca06f1..79989500 100644
--- a/src/signal/sigaction.c
+++ b/src/signal/sigaction.c
@@ -6,6 +6,11 @@
#include "libc.h"
#include "ksigaction.h"
+volatile int dummy_lock[1] = { 0 };
+
+__attribute__((__visibility__("hidden")))
+weak_alias(dummy_lock, __abort_lock);
+
static int unmask_done;
static unsigned long handler_set[_NSIG/(8*sizeof(long))];
@@ -17,6 +22,7 @@ void __get_handler_set(sigset_t *set)
int __libc_sigaction(int sig, const struct sigaction *restrict sa, struct sigaction *restrict old)
{
struct k_sigaction ksa, ksa_old;
+ unsigned long set[_NSIG/(8*sizeof(long))];
if (sa) {
if ((uintptr_t)sa->sa_handler > 1UL) {
a_or_l(handler_set+(sig-1)/(8*sizeof(long)),
@@ -36,19 +42,30 @@ int __libc_sigaction(int sig, const struct sigaction *restrict sa, struct sigact
unmask_done = 1;
}
}
+ /* Changing the disposition of SIGABRT to anything but
+ * SIG_DFL requires a lock, so that it cannot be changed
+ * while abort is terminating the process after simply
+ * calling raise(SIGABRT) failed to do so. */
+ if (sa->sa_handler != SIG_DFL && sig == SIGABRT) {
+ __block_all_sigs(&set);
+ LOCK(__abort_lock);
+ }
ksa.handler = sa->sa_handler;
ksa.flags = sa->sa_flags | SA_RESTORER;
ksa.restorer = (sa->sa_flags & SA_SIGINFO) ? __restore_rt : __restore;
memcpy(&ksa.mask, &sa->sa_mask, sizeof ksa.mask);
}
- if (syscall(SYS_rt_sigaction, sig, sa?&ksa:0, old?&ksa_old:0, sizeof ksa.mask))
- return -1;
- if (old) {
+ int r = __syscall(SYS_rt_sigaction, sig, sa?&ksa:0, old?&ksa_old:0, sizeof ksa.mask);
+ if (sig == SIGABRT && sa && sa->sa_handler != SIG_DFL) {
+ UNLOCK(__abort_lock);
+ __restore_sigs(&set);
+ }
+ if (old && !r) {
old->sa_handler = ksa_old.handler;
old->sa_flags = ksa_old.flags;
memcpy(&old->sa_mask, &ksa_old.mask, sizeof ksa_old.mask);
}
- return 0;
+ return __syscall_ret(r);
}
int __sigaction(int sig, const struct sigaction *restrict sa, struct sigaction *restrict old)