|
28 | 28 | #include <time.h> |
29 | 29 | #include <unistd.h> |
30 | 30 | #include "common.h" |
| 31 | +#include "counters.h" |
31 | 32 | #include "os.h" |
32 | 33 |
|
33 | 34 | #ifndef __musl__ |
@@ -726,4 +727,88 @@ SigAction OS::replaceSigbusHandler(SigAction action) { |
726 | 727 | return old_action; |
727 | 728 | } |
728 | 729 |
|
| 730 | +// ============================================================================ |
| 731 | +// sigaction interposition to prevent other libraries from overwriting our |
| 732 | +// SIGSEGV/SIGBUS handlers. This is needed because libraries like wasmtime |
| 733 | +// install broken signal handlers that call malloc() (not async-signal-safe). |
| 734 | +// ============================================================================ |
| 735 | + |
| 736 | +// Our protected handlers and their chain targets |
| 737 | +static SigAction _protected_segv_handler = nullptr; |
| 738 | +static SigAction _protected_bus_handler = nullptr; |
| 739 | +static volatile SigAction _segv_chain_target = nullptr; |
| 740 | +static volatile SigAction _bus_chain_target = nullptr; |
| 741 | + |
| 742 | +// Real sigaction function pointer (resolved via dlsym) |
| 743 | +typedef int (*real_sigaction_t)(int, const struct sigaction*, struct sigaction*); |
| 744 | +static real_sigaction_t _real_sigaction = nullptr; |
| 745 | + |
| 746 | +void OS::protectSignalHandlers(SigAction segvHandler, SigAction busHandler) { |
| 747 | + // Resolve real sigaction BEFORE enabling protection, while we can still use RTLD_DEFAULT |
| 748 | + if (_real_sigaction == nullptr) { |
| 749 | + _real_sigaction = (real_sigaction_t)dlsym(RTLD_DEFAULT, "sigaction"); |
| 750 | + } |
| 751 | + _protected_segv_handler = segvHandler; |
| 752 | + _protected_bus_handler = busHandler; |
| 753 | +} |
| 754 | + |
| 755 | +SigAction OS::getSegvChainTarget() { |
| 756 | + return __atomic_load_n(&_segv_chain_target, __ATOMIC_ACQUIRE); |
| 757 | +} |
| 758 | + |
| 759 | +SigAction OS::getBusChainTarget() { |
| 760 | + return __atomic_load_n(&_bus_chain_target, __ATOMIC_ACQUIRE); |
| 761 | +} |
| 762 | + |
| 763 | +// sigaction hook - called via GOT patching to intercept sigaction calls |
| 764 | +static int sigaction_hook(int signum, const struct sigaction* act, struct sigaction* oldact) { |
| 765 | + // _real_sigaction must be resolved before any GOT patching happens |
| 766 | + if (_real_sigaction == nullptr) { |
| 767 | + errno = EFAULT; |
| 768 | + return -1; |
| 769 | + } |
| 770 | + |
| 771 | + // If this is SIGSEGV or SIGBUS and we have protected handlers installed, |
| 772 | + // intercept the call to keep our handler on top |
| 773 | + if (act != nullptr) { |
| 774 | + if (signum == SIGSEGV && _protected_segv_handler != nullptr) { |
| 775 | + // Only intercept SA_SIGINFO handlers (3-arg form) for safe chaining |
| 776 | + if (act->sa_flags & SA_SIGINFO) { |
| 777 | + SigAction new_handler = act->sa_sigaction; |
| 778 | + // Don't intercept if it's our own handler being installed |
| 779 | + if (new_handler != _protected_segv_handler) { |
| 780 | + // Save their handler as our chain target |
| 781 | + __atomic_exchange_n(&_segv_chain_target, new_handler, __ATOMIC_ACQ_REL); |
| 782 | + if (oldact != nullptr) { |
| 783 | + _real_sigaction(signum, nullptr, oldact); |
| 784 | + } |
| 785 | + Counters::increment(SIGACTION_INTERCEPTED); |
| 786 | + // Don't actually install their handler - keep ours on top |
| 787 | + return 0; |
| 788 | + } |
| 789 | + } |
| 790 | + // Let 1-arg handlers (without SA_SIGINFO) pass through - we can't safely chain them |
| 791 | + } else if (signum == SIGBUS && _protected_bus_handler != nullptr) { |
| 792 | + if (act->sa_flags & SA_SIGINFO) { |
| 793 | + SigAction new_handler = act->sa_sigaction; |
| 794 | + if (new_handler != _protected_bus_handler) { |
| 795 | + __atomic_exchange_n(&_bus_chain_target, new_handler, __ATOMIC_ACQ_REL); |
| 796 | + if (oldact != nullptr) { |
| 797 | + _real_sigaction(signum, nullptr, oldact); |
| 798 | + } |
| 799 | + Counters::increment(SIGACTION_INTERCEPTED); |
| 800 | + return 0; |
| 801 | + } |
| 802 | + } |
| 803 | + } |
| 804 | + } |
| 805 | + |
| 806 | + // For all other cases, pass through to real sigaction |
| 807 | + return _real_sigaction(signum, act, oldact); |
| 808 | +} |
| 809 | + |
| 810 | +void* OS::getSigactionHook() { |
| 811 | + return (void*)sigaction_hook; |
| 812 | +} |
| 813 | + |
729 | 814 | #endif // __linux__ |
0 commit comments