always terminate by SIGABRT when abort is called
authorRich Felker <dalias@aerifal.cx>
Sat, 1 Sep 2018 05:54:44 +0000 (01:54 -0400)
committerRich Felker <dalias@aerifal.cx>
Sat, 1 Sep 2018 06:06:10 +0000 (02:06 -0400)
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).

src/exit/abort.c
src/signal/sigaction.c

index ecc0f735aaee71ca9324f537db1a6db1fb1e5897..d6bd546b40ec92e9063c388a081088f39cfb21bd 100644 (file)
@@ -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);
index 6eca06f11fb63c3aa260fce4cdc8104458fd9117..79989500a9d6b77d53221244ea2c782eb8aa9e50 100644 (file)
@@ -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)