summaryrefslogtreecommitdiff
path: root/src/signal
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2018-09-01 01:54:44 -0400
committerRich Felker <dalias@aerifal.cx>2018-09-01 02:06:10 -0400
commit9b14ad541068d4f7d0be9bcd1ff4c70090d868d3 (patch)
treeee80fd4be7d6d4a96858e02d76e4b53f038bf35d /src/signal
parent0b4c92b7acf63529858e7f8a3bb6505cd2b6e962 (diff)
downloadmusl-9b14ad541068d4f7d0be9bcd1ff4c70090d868d3.tar.gz
musl-9b14ad541068d4f7d0be9bcd1ff4c70090d868d3.tar.bz2
musl-9b14ad541068d4f7d0be9bcd1ff4c70090d868d3.tar.xz
musl-9b14ad541068d4f7d0be9bcd1ff4c70090d868d3.zip
always terminate by SIGABRT when abort is called
Linux makes this surprisingly difficult, but it can be done. the trick here is using the fact that we control the implementation of sigaction to prevent changing the disposition of SIGABRT to anything but SIG_DFL after abort has tried and failed to terminate the process simply by calling raise(SIGABRT).
Diffstat (limited to 'src/signal')
-rw-r--r--src/signal/sigaction.c25
1 files changed, 21 insertions, 4 deletions
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)