/src/WasmEdge/lib/system/fault.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // SPDX-License-Identifier: Apache-2.0 |
2 | | // SPDX-FileCopyrightText: 2019-2024 Second State INC |
3 | | |
4 | | #include "system/fault.h" |
5 | | |
6 | | #include "common/config.h" |
7 | | #include "common/defines.h" |
8 | | #include "common/spdlog.h" |
9 | | #include "system/stacktrace.h" |
10 | | |
11 | | #include <atomic> |
12 | | #include <csetjmp> |
13 | | #include <csignal> |
14 | | #include <cstdint> |
15 | | #include <utility> |
16 | | |
17 | | #if WASMEDGE_OS_WINDOWS |
18 | | #include "system/winapi.h" |
19 | | #endif |
20 | | |
21 | | namespace WasmEdge { |
22 | | |
23 | | namespace { |
24 | | |
25 | | std::atomic_uint handlerCount = 0; |
26 | | thread_local Fault *localHandler = nullptr; |
27 | | |
28 | | #if defined(SA_SIGINFO) |
29 | 0 | void signalHandler(int Signal, siginfo_t *Siginfo, void *) { |
30 | 0 | { |
31 | | // Unblock current signal |
32 | 0 | sigset_t Set; |
33 | 0 | sigemptyset(&Set); |
34 | 0 | sigaddset(&Set, Signal); |
35 | 0 | pthread_sigmask(SIG_UNBLOCK, &Set, nullptr); |
36 | 0 | } |
37 | 0 | switch (Signal) { |
38 | 0 | case SIGBUS: |
39 | 0 | case SIGSEGV: |
40 | 0 | Fault::emitFault(ErrCode::Value::MemoryOutOfBounds); |
41 | 0 | case SIGFPE: |
42 | 0 | assuming(Siginfo->si_code == FPE_INTDIV); |
43 | 0 | Fault::emitFault(ErrCode::Value::DivideByZero); |
44 | 0 | default: |
45 | 0 | assumingUnreachable(); |
46 | 0 | } |
47 | 0 | } |
48 | | |
49 | 0 | void enableHandler() noexcept { |
50 | 0 | struct sigaction Action {}; |
51 | 0 | Action.sa_sigaction = &signalHandler; |
52 | 0 | Action.sa_flags = SA_SIGINFO; |
53 | 0 | sigaction(SIGFPE, &Action, nullptr); |
54 | 0 | sigaction(SIGBUS, &Action, nullptr); |
55 | 0 | sigaction(SIGSEGV, &Action, nullptr); |
56 | 0 | } |
57 | | |
58 | 0 | void disableHandler() noexcept { |
59 | 0 | std::signal(SIGFPE, SIG_DFL); |
60 | 0 | std::signal(SIGBUS, SIG_DFL); |
61 | 0 | std::signal(SIGSEGV, SIG_DFL); |
62 | 0 | } |
63 | | |
64 | | #elif WASMEDGE_OS_WINDOWS |
65 | | |
66 | | winapi::LONG_ WASMEDGE_WINAPI_WINAPI_CC |
67 | | vectoredExceptionHandler(winapi::PEXCEPTION_POINTERS_ ExceptionInfo) { |
68 | | const winapi::DWORD_ Code = ExceptionInfo->ExceptionRecord->ExceptionCode; |
69 | | switch (Code) { |
70 | | case winapi::EXCEPTION_INT_DIVIDE_BY_ZERO_: |
71 | | Fault::emitFault(ErrCode::Value::DivideByZero); |
72 | | case winapi::EXCEPTION_INT_OVERFLOW_: |
73 | | Fault::emitFault(ErrCode::Value::IntegerOverflow); |
74 | | case winapi::EXCEPTION_ACCESS_VIOLATION_: |
75 | | Fault::emitFault(ErrCode::Value::MemoryOutOfBounds); |
76 | | } |
77 | | return winapi::EXCEPTION_CONTINUE_EXECUTION_; |
78 | | } |
79 | | |
80 | | void *HandlerHandle = nullptr; |
81 | | |
82 | | void enableHandler() noexcept { |
83 | | HandlerHandle = |
84 | | winapi::AddVectoredExceptionHandler(1, &vectoredExceptionHandler); |
85 | | } |
86 | | |
87 | | void disableHandler() noexcept { |
88 | | winapi::RemoveVectoredExceptionHandler(HandlerHandle); |
89 | | } |
90 | | |
91 | | #endif |
92 | | |
93 | 0 | void increaseHandler() noexcept { |
94 | 0 | if (handlerCount++ == 0) { |
95 | 0 | enableHandler(); |
96 | 0 | } |
97 | 0 | } |
98 | | |
99 | 0 | void decreaseHandler() noexcept { |
100 | 0 | if (--handlerCount == 0) { |
101 | 0 | disableHandler(); |
102 | 0 | } |
103 | 0 | } |
104 | | |
105 | | } // namespace |
106 | | |
107 | 0 | Fault::Fault() { |
108 | 0 | Prev = std::exchange(localHandler, this); |
109 | 0 | increaseHandler(); |
110 | 0 | } |
111 | | |
112 | 0 | Fault::~Fault() noexcept { |
113 | 0 | decreaseHandler(); |
114 | 0 | localHandler = std::exchange(Prev, nullptr); |
115 | 0 | } |
116 | | |
117 | 0 | [[noreturn]] void Fault::emitFault(ErrCode Error) { |
118 | 0 | assuming(localHandler != nullptr); |
119 | 0 | auto Buffer = stackTrace(localHandler->StackTraceBuffer); |
120 | 0 | localHandler->StackTraceSize = Buffer.size(); |
121 | 0 | longjmp(localHandler->Buffer, static_cast<int>(Error.operator uint32_t())); |
122 | 0 | } |
123 | | |
124 | | } // namespace WasmEdge |