1/*
2 * Copyright (C) 2017-2019 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "WasmFaultSignalHandler.h"
28
29#if ENABLE(WEBASSEMBLY)
30
31#include "ExecutableAllocator.h"
32#include "MachineContext.h"
33#include "WasmCallee.h"
34#include "WasmCalleeRegistry.h"
35#include "WasmCapabilities.h"
36#include "WasmExceptionType.h"
37#include "WasmMemory.h"
38#include "WasmThunks.h"
39
40#include <wtf/HashSet.h>
41#include <wtf/Lock.h>
42#include <wtf/NeverDestroyed.h>
43#include <wtf/threads/Signals.h>
44
45namespace JSC { namespace Wasm {
46
47namespace {
48namespace WasmFaultSignalHandlerInternal {
49static constexpr bool verbose = false;
50}
51}
52
53static bool fastHandlerInstalled { false };
54
55#if ENABLE(WEBASSEMBLY_FAST_MEMORY)
56
57static SignalAction trapHandler(Signal, SigInfo& sigInfo, PlatformRegisters& context)
58{
59 auto instructionPointer = MachineContext::instructionPointer(context);
60 if (!instructionPointer)
61 return SignalAction::NotHandled;
62 void* faultingInstruction = instructionPointer->untaggedExecutableAddress();
63 dataLogLnIf(WasmFaultSignalHandlerInternal::verbose, "starting handler for fault at: ", RawPointer(faultingInstruction));
64
65 dataLogLnIf(WasmFaultSignalHandlerInternal::verbose, "JIT memory start: ", RawPointer(startOfFixedExecutableMemoryPool()), " end: ", RawPointer(endOfFixedExecutableMemoryPool()));
66 // First we need to make sure we are in JIT code before we can aquire any locks. Otherwise,
67 // we might have crashed in code that is already holding one of the locks we want to aquire.
68 assertIsNotTagged(faultingInstruction);
69 if (isJITPC(faultingInstruction)) {
70 bool faultedInActiveFastMemory = false;
71 {
72 void* faultingAddress = sigInfo.faultingAddress;
73 dataLogLnIf(WasmFaultSignalHandlerInternal::verbose, "checking faulting address: ", RawPointer(faultingAddress), " is in an active fast memory");
74 faultedInActiveFastMemory = Wasm::Memory::addressIsInActiveFastMemory(faultingAddress);
75 }
76 if (faultedInActiveFastMemory) {
77 dataLogLnIf(WasmFaultSignalHandlerInternal::verbose, "found active fast memory for faulting address");
78 auto& calleeRegistry = CalleeRegistry::singleton();
79 auto locker = holdLock(calleeRegistry.getLock());
80 for (auto* callee : calleeRegistry.allCallees(locker)) {
81 auto [start, end] = callee->range();
82 dataLogLnIf(WasmFaultSignalHandlerInternal::verbose, "function start: ", RawPointer(start), " end: ", RawPointer(end));
83 if (start <= faultingInstruction && faultingInstruction < end) {
84 dataLogLnIf(WasmFaultSignalHandlerInternal::verbose, "found match");
85 MacroAssemblerCodeRef<JITThunkPtrTag> exceptionStub = Thunks::singleton().existingStub(throwExceptionFromWasmThunkGenerator);
86 // If for whatever reason we don't have a stub then we should just treat this like a regular crash.
87 if (!exceptionStub)
88 break;
89 dataLogLnIf(WasmFaultSignalHandlerInternal::verbose, "found stub: ", RawPointer(exceptionStub.code().executableAddress()));
90 MachineContext::argumentPointer<1>(context) = reinterpret_cast<void*>(ExceptionType::OutOfBoundsMemoryAccess);
91 MachineContext::setInstructionPointer(context, exceptionStub.code().retagged<CFunctionPtrTag>());
92 return SignalAction::Handled;
93 }
94 }
95 }
96 }
97 return SignalAction::NotHandled;
98}
99
100#endif // ENABLE(WEBASSEMBLY_FAST_MEMORY)
101
102bool fastMemoryEnabled()
103{
104 return fastHandlerInstalled;
105}
106
107void enableFastMemory()
108{
109#if ENABLE(WEBASSEMBLY_FAST_MEMORY)
110 static std::once_flag once;
111 std::call_once(once, [] {
112 if (!Wasm::isSupported())
113 return;
114
115 if (!Options::useWebAssemblyFastMemory())
116 return;
117
118 installSignalHandler(Signal::BadAccess, [] (Signal signal, SigInfo& sigInfo, PlatformRegisters& ucontext) {
119 return trapHandler(signal, sigInfo, ucontext);
120 });
121
122 fastHandlerInstalled = true;
123 });
124#endif // ENABLE(WEBASSEMBLY_FAST_MEMORY)
125}
126
127} } // namespace JSC::Wasm
128
129#endif // ENABLE(WEBASSEMBLY)
130
131