Index: libc/src/assert/CMakeLists.txt =================================================================== --- libc/src/assert/CMakeLists.txt +++ libc/src/assert/CMakeLists.txt @@ -10,4 +10,11 @@ # later. sys_syscall_h linux_syscall_h + # TOOD: Remove these as they are dependencies of abort + sigaction + sigfillset + sigdelset + sigprocmask + signal + __errno_location ) Index: libc/src/signal/linux/CMakeLists.txt =================================================================== --- libc/src/signal/linux/CMakeLists.txt +++ libc/src/signal/linux/CMakeLists.txt @@ -95,6 +95,8 @@ DEPENDS sigaction signal_h + # TODO: Remove this it is a dependency of sigaction + __errno_location ) add_entrypoint_object( Index: libc/src/stdlib/CMakeLists.txt =================================================================== --- libc/src/stdlib/CMakeLists.txt +++ libc/src/stdlib/CMakeLists.txt @@ -12,4 +12,10 @@ raise _Exit stdlib_h + sigfillset + sigdelset + sigprocmask + signal + # TODO: Remove this, dependency of signal via sigaction + __errno_location ) Index: libc/src/stdlib/abort.cpp =================================================================== --- libc/src/stdlib/abort.cpp +++ libc/src/stdlib/abort.cpp @@ -8,21 +8,43 @@ #include "src/__support/common.h" #include "src/signal/raise.h" +#include "src/signal/sigdelset.h" +#include "src/signal/sigfillset.h" +#include "src/signal/signal.h" +#include "src/signal/sigprocmask.h" #include "src/stdlib/_Exit.h" #include "src/stdlib/abort.h" namespace __llvm_libc { +// Currently the signal implementation doesn't guarantee that SIGABRT's handler +// or the signal mask as a whole is not modified from other threads. void LLVM_LIBC_ENTRYPOINT(abort)() { - // TODO: When sigprocmask and sigaction land: - // Unblock SIGABRT, raise it, if it was ignored or the handler returned, - // change its action to SIG_DFL, raise it again. - // TODO: When C11 mutexes land: - // Aquire recursive mutex (in case the current signal handler for SIGABRT - // itself calls abort we don't want to deadlock on the same thread trying - // to aquire it's own mutex.) + // Block all blockable signals except SIGABRT, so this function doesn't get + // interrupted by any async signals. This also unblocks SIGABRT. + sigset_t set; + __llvm_libc::sigfillset(&set); + __llvm_libc::sigdelset(&set, SIGABRT); + __llvm_libc::sigprocmask(SIG_SETMASK, &set, nullptr); + + // If the current SIGABRT handler calls abort(), we should make sure it + // it does not infinitely call itself. + static bool called; + if (!called) { + called = true; + __llvm_libc::raise(SIGABRT); + // Reset the signal mask in case the user handler changed it. + __llvm_libc::sigprocmask(SIG_SETMASK, &set, nullptr); + } + + // If the user handler returned, we re-raise SIGABRT with the default handler + // to attempt to die with SIGABRT. + __llvm_libc::signal(SIGABRT, SIG_DFL); __llvm_libc::raise(SIGABRT); + + // If the program still hasn't been killed by SIGABRT we must guarantee that + // this function never returns. __llvm_libc::raise(SIGKILL); __llvm_libc::_Exit(127); } Index: libc/test/src/assert/CMakeLists.txt =================================================================== --- libc/test/src/assert/CMakeLists.txt +++ libc/test/src/assert/CMakeLists.txt @@ -12,4 +12,10 @@ abort raise _Exit + sigaction + sigfillset + sigdelset + sigprocmask + signal + __errno_location ) Index: libc/test/src/assert/assert_test.cpp =================================================================== --- libc/test/src/assert/assert_test.cpp +++ libc/test/src/assert/assert_test.cpp @@ -7,21 +7,21 @@ //===----------------------------------------------------------------------===// #undef NDEBUG +#include "include/signal.h" #include "src/assert/assert.h" #include "utils/UnitTest/Test.h" extern "C" int close(int); TEST(Assert, Enabled) { - // -1 matches against any signal, which is necessary for now until - // __llvm_libc::abort() unblocks SIGABRT. Close standard error for the - // child process so we don't print the assertion failure message. + // Close standard error for the child process so we don't print the assertion + // failure message. EXPECT_DEATH( [] { close(2); assert(0); }, - -1); + SIGABRT); } #define NDEBUG Index: libc/test/src/stdlib/CMakeLists.txt =================================================================== --- libc/test/src/stdlib/CMakeLists.txt +++ libc/test/src/stdlib/CMakeLists.txt @@ -23,4 +23,13 @@ abort _Exit raise + # TODO: Remove depencies of abort + sigaction + sigemptyset + sigaddset + sigfillset + sigdelset + sigprocmask + signal + __errno_location ) Index: libc/test/src/stdlib/abort_test.cpp =================================================================== --- libc/test/src/stdlib/abort_test.cpp +++ libc/test/src/stdlib/abort_test.cpp @@ -8,11 +8,47 @@ #include "include/signal.h" #include "include/stdlib.h" +#include "src/signal/sigaddset.h" +#include "src/signal/sigemptyset.h" +#include "src/signal/signal.h" +#include "src/signal/sigprocmask.h" #include "src/stdlib/abort.h" #include "utils/UnitTest/Test.h" -TEST(Stdlib, abort) { - // -1 matches against any signal, which is necessary for now until - // __llvm_libc::abort() unblocks SIGABRT. - EXPECT_DEATH([] { __llvm_libc::abort(); }, -1); +TEST(Abort, Basic) { + EXPECT_DEATH([] { __llvm_libc::abort(); }, SIGABRT); +} + +TEST(Abort, Blocked) { + __llvm_libc::signal(SIGABRT, SIG_IGN); + EXPECT_DEATH([] { __llvm_libc::abort(); }, SIGABRT); +} + +TEST(Abort, RecursiveHandler) { + __llvm_libc::signal( + SIGABRT, +[](int) { __llvm_libc::abort(); }); + EXPECT_DEATH([] { __llvm_libc::abort(); }, SIGABRT); +} + +TEST(Abort, BlockFromHandler) { + __llvm_libc::signal( + SIGABRT, +[](int) { + sigset_t set; + __llvm_libc::sigemptyset(&set); + __llvm_libc::sigaddset(&set, SIGABRT); + __llvm_libc::sigprocmask(SIG_BLOCK, &set, nullptr); + }); + EXPECT_DEATH([] { __llvm_libc::abort(); }, SIGABRT); +} + +TEST(Abort, BlockFromHandlerRecursive) { + __llvm_libc::signal( + SIGABRT, +[](int) { + sigset_t set; + __llvm_libc::sigemptyset(&set); + __llvm_libc::sigaddset(&set, SIGABRT); + __llvm_libc::sigprocmask(SIG_BLOCK, &set, nullptr); + __llvm_libc::abort(); + }); + EXPECT_DEATH([] { __llvm_libc::abort(); }, SIGABRT); }