CodeBlocks should be in IsoSubspaces
[WebKit-https.git] / Source / JavaScriptCore / runtime / VMTraps.cpp
1 /*
2  * Copyright (C) 2017 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 "VMTraps.h"
28
29 #include "CallFrame.h"
30 #include "CodeBlock.h"
31 #include "CodeBlockSet.h"
32 #include "DFGCommonData.h"
33 #include "ExceptionHelpers.h"
34 #include "HeapInlines.h"
35 #include "LLIntPCRanges.h"
36 #include "MachineContext.h"
37 #include "MachineStackMarker.h"
38 #include "MacroAssembler.h"
39 #include "VM.h"
40 #include "VMInspector.h"
41 #include "Watchdog.h"
42 #include <wtf/ProcessID.h>
43 #include <wtf/ThreadMessage.h>
44 #include <wtf/threads/Signals.h>
45
46 namespace JSC {
47
48 ALWAYS_INLINE VM& VMTraps::vm() const
49 {
50     return *bitwise_cast<VM*>(bitwise_cast<uintptr_t>(this) - OBJECT_OFFSETOF(VM, m_traps));
51 }
52
53 #if ENABLE(SIGNAL_BASED_VM_TRAPS)
54
55 struct SignalContext {
56     SignalContext(PlatformRegisters& registers)
57         : registers(registers)
58         , trapPC(MachineContext::instructionPointer(registers))
59         , stackPointer(MachineContext::stackPointer(registers))
60         , framePointer(MachineContext::framePointer(registers))
61     {
62     }
63
64     PlatformRegisters& registers;
65     void* trapPC;
66     void* stackPointer;
67     void* framePointer;
68 };
69
70 inline static bool vmIsInactive(VM& vm)
71 {
72     return !vm.entryScope && !vm.ownerThread();
73 }
74
75 inline CallFrame* sanitizedTopCallFrame(CallFrame* topCallFrame)
76 {
77 #if !defined(NDEBUG) && !CPU(ARM) && !CPU(MIPS)
78     // prepareForExternalCall() in DFGSpeculativeJIT.h may set topCallFrame to a bad word
79     // before calling native functions, but tryInstallTrapBreakpoints() below expects
80     // topCallFrame to be null if not set.
81 #if USE(JSVALUE64)
82     const uintptr_t badBeefWord = 0xbadbeef0badbeef;
83 #else
84     const uintptr_t badBeefWord = 0xbadbeef;
85 #endif
86     if (topCallFrame == reinterpret_cast<CallFrame*>(badBeefWord))
87         topCallFrame = nullptr;
88 #endif
89     return topCallFrame;
90 }
91
92 static bool isSaneFrame(CallFrame* frame, CallFrame* calleeFrame, EntryFrame* entryFrame, StackBounds stackBounds)
93 {
94     if (reinterpret_cast<void*>(frame) >= reinterpret_cast<void*>(entryFrame))
95         return false;
96     if (calleeFrame >= frame)
97         return false;
98     return stackBounds.contains(frame);
99 }
100     
101 void VMTraps::tryInstallTrapBreakpoints(SignalContext& context, StackBounds stackBounds)
102 {
103     // This must be the initial signal to get the mutator thread's attention.
104     // Let's get the thread to break at invalidation points if needed.
105     VM& vm = this->vm();
106     void* trapPC = context.trapPC;
107
108     CallFrame* callFrame = reinterpret_cast<CallFrame*>(context.framePointer);
109
110     auto& lock = vm.heap.codeBlockSet().getLock();
111     // If the target thread is in C++ code it might be holding the codeBlockSet lock.
112     // if it's in JIT code then it cannot be holding that lock but the GC might be.
113     auto codeBlockSetLocker = isJITPC(trapPC) ? holdLock(lock) : tryHoldLock(lock);
114     if (!codeBlockSetLocker)
115         return; // Let the SignalSender try again later.
116
117     if (!isJITPC(trapPC) && !LLInt::isLLIntPC(trapPC)) {
118         // We resort to topCallFrame to see if we can get anything
119         // useful. We usually get here when we're executing C code.
120         callFrame = sanitizedTopCallFrame(vm.topCallFrame);
121     }
122
123     CodeBlock* foundCodeBlock = nullptr;
124     EntryFrame* entryFrame = vm.topEntryFrame;
125
126     // We don't have a callee to start with. So, use the end of the stack to keep the
127     // isSaneFrame() checker below happy for the first iteration. It will still check
128     // to ensure that the address is in the stackBounds.
129     CallFrame* calleeFrame = reinterpret_cast<CallFrame*>(stackBounds.end());
130
131     if (!entryFrame || !callFrame)
132         return; // Not running JS code. Let the SignalSender try again later.
133
134     do {
135         if (!isSaneFrame(callFrame, calleeFrame, entryFrame, stackBounds))
136             return; // Let the SignalSender try again later.
137
138         CodeBlock* candidateCodeBlock = callFrame->codeBlock();
139         if (candidateCodeBlock && vm.heap.codeBlockSet().contains(codeBlockSetLocker, candidateCodeBlock)) {
140             foundCodeBlock = candidateCodeBlock;
141             break;
142         }
143
144         calleeFrame = callFrame;
145         callFrame = callFrame->callerFrame(entryFrame);
146
147     } while (callFrame && entryFrame);
148
149     if (!foundCodeBlock) {
150         // We may have just entered the frame and the codeBlock pointer is not
151         // initialized yet. Just bail and let the SignalSender try again later.
152         return;
153     }
154
155     if (JITCode::isOptimizingJIT(foundCodeBlock->jitType())) {
156         auto locker = tryHoldLock(*m_lock);
157         if (!locker)
158             return; // Let the SignalSender try again later.
159
160         if (!foundCodeBlock->hasInstalledVMTrapBreakpoints())
161             foundCodeBlock->installVMTrapBreakpoints();
162         return;
163     }
164 }
165
166 void VMTraps::invalidateCodeBlocksOnStack()
167 {
168     invalidateCodeBlocksOnStack(vm().topCallFrame);
169 }
170
171 void VMTraps::invalidateCodeBlocksOnStack(ExecState* topCallFrame)
172 {
173     auto codeBlockSetLocker = holdLock(vm().heap.codeBlockSet().getLock());
174     invalidateCodeBlocksOnStack(codeBlockSetLocker, topCallFrame);
175 }
176     
177 void VMTraps::invalidateCodeBlocksOnStack(Locker<Lock>&, ExecState* topCallFrame)
178 {
179     if (!m_needToInvalidatedCodeBlocks)
180         return;
181
182     m_needToInvalidatedCodeBlocks = false;
183
184     EntryFrame* entryFrame = vm().topEntryFrame;
185     CallFrame* callFrame = topCallFrame;
186
187     if (!entryFrame)
188         return; // Not running JS code. Nothing to invalidate.
189
190     while (callFrame) {
191         CodeBlock* codeBlock = callFrame->codeBlock();
192         if (codeBlock && JITCode::isOptimizingJIT(codeBlock->jitType()))
193             codeBlock->jettison(Profiler::JettisonDueToVMTraps);
194         callFrame = callFrame->callerFrame(entryFrame);
195     }
196 }
197
198 class VMTraps::SignalSender final : public AutomaticThread {
199 public:
200     using Base = AutomaticThread;
201     SignalSender(const AbstractLocker& locker, VM& vm)
202         : Base(locker, vm.traps().m_lock, vm.traps().m_trapSet)
203         , m_vm(vm)
204     {
205         static std::once_flag once;
206         std::call_once(once, [] {
207             installSignalHandler(Signal::BadAccess, [] (Signal, SigInfo&, PlatformRegisters& registers) -> SignalAction {
208                 SignalContext context(registers);
209
210                 if (!isJITPC(context.trapPC))
211                     return SignalAction::NotHandled;
212
213                 CodeBlock* currentCodeBlock = DFG::codeBlockForVMTrapPC(context.trapPC);
214                 if (!currentCodeBlock) {
215                     // Either we trapped for some other reason, e.g. Wasm OOB, or we didn't properly monitor the PC. Regardless, we can't do much now...
216                     return SignalAction::NotHandled;
217                 }
218                 ASSERT(currentCodeBlock->hasInstalledVMTrapBreakpoints());
219                 VM& vm = *currentCodeBlock->vm();
220                 ASSERT(vm.traps().needTrapHandling()); // We should have already jettisoned this code block when we handled the trap.
221
222                 // We are in JIT code so it's safe to aquire this lock.
223                 auto codeBlockSetLocker = holdLock(vm.heap.codeBlockSet().getLock());
224                 bool sawCurrentCodeBlock = false;
225                 vm.heap.forEachCodeBlockIgnoringJITPlans(codeBlockSetLocker, [&] (CodeBlock* codeBlock) {
226                     // We want to jettison all code blocks that have vm traps breakpoints, otherwise we could hit them later.
227                     if (codeBlock->hasInstalledVMTrapBreakpoints()) {
228                         if (currentCodeBlock == codeBlock)
229                             sawCurrentCodeBlock = true;
230
231                         codeBlock->jettison(Profiler::JettisonDueToVMTraps);
232                     }
233                 });
234                 RELEASE_ASSERT(sawCurrentCodeBlock);
235                 
236                 return SignalAction::Handled; // We've successfully jettisoned the codeBlocks.
237             });
238         });
239     }
240
241     VMTraps& traps() { return m_vm.traps(); }
242
243 protected:
244     PollResult poll(const AbstractLocker&) override
245     {
246         if (traps().m_isShuttingDown)
247             return PollResult::Stop;
248
249         if (!traps().needTrapHandling())
250             return PollResult::Wait;
251
252         // We know that no trap could have been processed and re-added because we are holding the lock.
253         if (vmIsInactive(m_vm))
254             return PollResult::Wait;
255         return PollResult::Work;
256     }
257
258     WorkResult work() override
259     {
260         VM& vm = m_vm;
261
262         auto optionalOwnerThread = vm.ownerThread();
263         if (optionalOwnerThread) {
264             sendMessage(*optionalOwnerThread.value().get(), [&] (PlatformRegisters& registers) -> void {
265                 SignalContext context(registers);
266
267                 auto ownerThread = vm.apiLock().ownerThread();
268                 // We can't mess with a thread unless it's the one we suspended.
269                 if (!ownerThread || ownerThread != optionalOwnerThread)
270                     return;
271
272                 Thread& thread = *ownerThread->get();
273                 vm.traps().tryInstallTrapBreakpoints(context, thread.stack());
274             });
275         }
276
277         {
278             auto locker = holdLock(*traps().m_lock);
279             if (traps().m_isShuttingDown)
280                 return WorkResult::Stop;
281             traps().m_trapSet->waitFor(*traps().m_lock, 1_ms);
282         }
283         return WorkResult::Continue;
284     }
285     
286 private:
287
288     VM& m_vm;
289 };
290
291 #endif // ENABLE(SIGNAL_BASED_VM_TRAPS)
292
293 void VMTraps::willDestroyVM()
294 {
295     m_isShuttingDown = true;
296 #if ENABLE(SIGNAL_BASED_VM_TRAPS)
297     if (m_signalSender) {
298         {
299             auto locker = holdLock(*m_lock);
300             if (!m_signalSender->tryStop(locker))
301                 m_trapSet->notifyAll(locker);
302         }
303         m_signalSender->join();
304         m_signalSender = nullptr;
305     }
306 #endif
307 }
308
309 void VMTraps::fireTrap(VMTraps::EventType eventType)
310 {
311     ASSERT(!vm().currentThreadIsHoldingAPILock());
312     {
313         auto locker = holdLock(*m_lock);
314         ASSERT(!m_isShuttingDown);
315         setTrapForEvent(locker, eventType);
316         m_needToInvalidatedCodeBlocks = true;
317     }
318     
319 #if ENABLE(SIGNAL_BASED_VM_TRAPS)
320     if (!Options::usePollingTraps()) {
321         // sendSignal() can loop until it has confirmation that the mutator thread
322         // has received the trap request. We'll call it from another trap so that
323         // fireTrap() does not block.
324         auto locker = holdLock(*m_lock);
325         if (!m_signalSender)
326             m_signalSender = adoptRef(new SignalSender(locker, vm()));
327         m_trapSet->notifyAll(locker);
328     }
329 #endif
330 }
331
332 void VMTraps::handleTraps(ExecState* exec, VMTraps::Mask mask)
333 {
334     VM& vm = this->vm();
335     auto scope = DECLARE_THROW_SCOPE(vm);
336
337     {
338         auto codeBlockSetLocker = holdLock(vm.heap.codeBlockSet().getLock());
339         vm.heap.forEachCodeBlockIgnoringJITPlans(codeBlockSetLocker, [&] (CodeBlock* codeBlock) {
340             // We want to jettison all code blocks that have vm traps breakpoints, otherwise we could hit them later.
341             if (codeBlock->hasInstalledVMTrapBreakpoints())
342                 codeBlock->jettison(Profiler::JettisonDueToVMTraps);
343         });
344     }
345
346     ASSERT(needTrapHandling(mask));
347     while (needTrapHandling(mask)) {
348         auto eventType = takeTopPriorityTrap(mask);
349         switch (eventType) {
350         case NeedDebuggerBreak:
351             dataLog("VM ", RawPointer(&vm), " on pid ", getCurrentProcessID(), " received NeedDebuggerBreak trap\n");
352             invalidateCodeBlocksOnStack(exec);
353             break;
354                 
355         case NeedWatchdogCheck:
356             ASSERT(vm.watchdog());
357             if (LIKELY(!vm.watchdog()->shouldTerminate(exec)))
358                 continue;
359             FALLTHROUGH;
360
361         case NeedTermination:
362             throwException(exec, scope, createTerminatedExecutionException(&vm));
363             return;
364
365         default:
366             RELEASE_ASSERT_NOT_REACHED();
367         }
368     }
369 }
370
371 auto VMTraps::takeTopPriorityTrap(VMTraps::Mask mask) -> EventType
372 {
373     auto locker = holdLock(*m_lock);
374     for (int i = 0; i < NumberOfEventTypes; ++i) {
375         EventType eventType = static_cast<EventType>(i);
376         if (hasTrapForEvent(locker, eventType, mask)) {
377             clearTrapForEvent(locker, eventType);
378             return eventType;
379         }
380     }
381     return Invalid;
382 }
383
384 VMTraps::VMTraps()
385     : m_lock(Box<Lock>::create())
386     , m_trapSet(AutomaticThreadCondition::create())
387 {
388 }
389
390 VMTraps::~VMTraps()
391 {
392 #if ENABLE(SIGNAL_BASED_VM_TRAPS)
393     ASSERT(!m_signalSender);
394 #endif
395 }
396
397 } // namespace JSC