diff --git a/configure.ac b/configure.ac index 1c8f890dc..e30f3a0e0 100644 --- a/configure.ac +++ b/configure.ac @@ -411,6 +411,14 @@ dnl CXXFLAGS+=" -fcheck-pointer-bounds" CXXFLAGS+=" -fchkp-instrument-marked-only" CXXFLAGS+=" -fstack-protector-explicit" +dnl !!! Experimental !!! +dnl Allows a signal handler to throw a C++ exception if the signal is from +dnl a CPU trap on an instruction (ex. SIGFPE from a floating point divide +dnl by zero). This allows the signal handler to transfer control to the +dnl catch block wrapping the area which errored, thereby aborting any further +dnl execution after the SIGFPE was received which would have to manually check +dnl for such an error after each floating point operation and throw. +CXXFLAGS+=" -fnon-call-exceptions" dnl -ffriend-injection allows us to deduplicate declarations of friend dnl functions in both the friend class and the enclosing namespace diff --git a/include/ircd/fpe.h b/include/ircd/fpe.h index c2687696a..a7a7aac9e 100644 --- a/include/ircd/fpe.h +++ b/include/ircd/fpe.h @@ -11,10 +11,17 @@ #pragma once #define HAVE_IRCD_FPE_H +extern "C" +{ + struct sigaction; +}; + namespace ircd::fpe { struct errors_handle; + struct errors_throw; + string_view reflect_sicode(const int &); string_view reflect(const ushort &flag); string_view reflect(const mutable_buffer &, const ushort &flags); void throw_errors(const ushort &flags); @@ -32,3 +39,14 @@ struct ircd::fpe::errors_handle errors_handle(); ~errors_handle() noexcept(false); }; + +struct ircd::fpe::errors_throw +{ + custom_ptr their_sa; + long their_fenabled; + fexcept_t their_fe; + + public: + errors_throw(); + ~errors_throw() noexcept; +}; diff --git a/ircd/fpe.cc b/ircd/fpe.cc index f5adef628..a3ea62cc4 100644 --- a/ircd/fpe.cc +++ b/ircd/fpe.cc @@ -8,10 +8,88 @@ // copyright notice and this permission notice is present in all copies. The // full license for this software is available in the LICENSE file. +#include + #ifndef __GNUC__ #pragma STDC FENV_ACCESS on #endif +// +// errors_throw +// + +namespace ircd::fpe +{ + constexpr const size_t &SA_HEAP_MAX {64}; + thread_local allocator::fixed sa_heap; + + extern const std::exception_ptr sigfpe_eptr; + extern "C" void ircd_fpe_handle_sigfpe(int signum, siginfo_t *const si, void *const uctx); +} + + +decltype(ircd::fpe::sigfpe_eptr) +ircd::fpe::sigfpe_eptr +{ + std::make_exception_ptr(std::domain_error + { + "Floating Point Exception" + }) +}; + +__attribute__((noreturn)) +void +ircd::fpe::ircd_fpe_handle_sigfpe(int signum, + siginfo_t *const si, + void *const uctx) +{ + assert(si); + assert(signum == SIGFPE); + std::rethrow_exception(sigfpe_eptr); //TODO: still malloc()'s :/ +} + +// +// errors_throw::errors_throw +// + +ircd::fpe::errors_throw::errors_throw() +:their_sa +{ + new (sa_heap().allocate(1)) struct sigaction, + [](auto *const ptr) + { + sa_heap().deallocate(ptr, 1); + } +} +,their_fenabled +{ + syscall(::fegetexcept) +} +{ + struct sigaction ours {0}; + ours.sa_sigaction = ircd_fpe_handle_sigfpe; + ours.sa_flags |= SA_SIGINFO; + ours.sa_flags |= SA_NODEFER; // Required to throw out of the handler + + syscall(std::fegetexceptflag, &their_fe, FE_ALL_EXCEPT); + syscall(std::feclearexcept, FE_ALL_EXCEPT); + syscall(::sigaction, SIGFPE, &ours, their_sa.get()); + syscall(::feenableexcept, FE_ALL_EXCEPT); +} + +ircd::fpe::errors_throw::~errors_throw() +noexcept +{ + syscall(::fedisableexcept, FE_ALL_EXCEPT); + syscall(::sigaction, SIGFPE, their_sa.get(), nullptr); + syscall(::feenableexcept, their_fenabled); + syscall(std::fesetexceptflag, &their_fe, FE_ALL_EXCEPT); +} + +// +// errors_handle +// + ircd::fpe::errors_handle::errors_handle() { syscall(std::fegetexceptflag, &theirs, FE_ALL_EXCEPT); @@ -98,3 +176,21 @@ ircd::fpe::reflect(const ushort &flag) return "?????"; } + +ircd::string_view +ircd::fpe::reflect_sicode(const int &code) +{ + switch(code) + { + case FPE_INTDIV: return "INTDIV"; + case FPE_INTOVF: return "INTOVF"; + case FPE_FLTDIV: return "FLTDIV"; + case FPE_FLTOVF: return "FLTOVF"; + case FPE_FLTUND: return "FLTUND"; + case FPE_FLTRES: return "FLTRES"; + case FPE_FLTINV: return "FLTINV"; + case FPE_FLTSUB: return "FLTSUB"; + } + + return "?????"; +}