a20d094456c1f3d12019e0b0f42d336be9b05d42
[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                     return false;
234                 });
235                 RELEASE_ASSERT(sawCurrentCodeBlock);
236                 
237                 return SignalAction::Handled; // We've successfully jettisoned the codeBlocks.
238             });
239         });
240     }
241
242     VMTraps& traps() { return m_vm.traps(); }
243
244 protected:
245     PollResult poll(const AbstractLocker&) override
246     {
247         if (traps().m_isShuttingDown)
248             return PollResult::Stop;
249
250         if (!traps().needTrapHandling())
251             return PollResult::Wait;
252
253         // We know that no trap could have been processed and re-added because we are holding the lock.
254         if (vmIsInactive(m_vm))
255             return PollResult::Wait;
256         return PollResult::Work;
257     }
258
259     WorkResult work() override
260     {
261         VM& vm = m_vm;
262
263         auto optionalOwnerThread = vm.ownerThread();
264         if (optionalOwnerThread) {
265             sendMessage(*optionalOwnerThread.value().get(), [&] (PlatformRegisters& registers) -> void {
266                 SignalContext context(registers);
267
268                 auto ownerThread = vm.apiLock().ownerThread();
269                 // We can't mess with a thread unless it's the one we suspended.
270                 if (!ownerThread || ownerThread != optionalOwnerThread)
271                     return;
272
273                 Thread& thread = *ownerThread->get();
274                 vm.traps().tryInstallTrapBreakpoints(context, thread.stack());
275             });
276         }
277
278         {
279             auto locker = holdLock(*traps().m_lock);
280             if (traps().m_isShuttingDown)
281                 return WorkResult::Stop;
282             traps().m_trapSet->waitFor(*traps().m_lock, 1_ms);
283         }
284         return WorkResult::Continue;
285     }
286     
287 private:
288
289     VM& m_vm;
290 };
291
292 #endif // ENABLE(SIGNAL_BASED_VM_TRAPS)
293
294 void VMTraps::willDestroyVM()
295 {
296     m_isShuttingDown = true;
297 #if ENABLE(SIGNAL_BASED_VM_TRAPS)
298     if (m_signalSender) {
299         {
300             auto locker = holdLock(*m_lock);
301             if (!m_signalSender->tryStop(locker))
302                 m_trapSet->notifyAll(locker);
303         }
304         m_signalSender->join();
305         m_signalSender = nullptr;
306     }
307 #endif
308 }
309
310 void VMTraps::fireTrap(VMTraps::EventType eventType)
311 {
312     ASSERT(!vm().currentThreadIsHoldingAPILock());
313     {
314         auto locker = holdLock(*m_lock);
315         ASSERT(!m_isShuttingDown);
316         setTrapForEvent(locker, eventType);
317         m_needToInvalidatedCodeBlocks = true;
318     }
319     
320 #if ENABLE(SIGNAL_BASED_VM_TRAPS)
321     if (!Options::usePollingTraps()) {
322         // sendSignal() can loop until it has confirmation that the mutator thread
323         // has received the trap request. We'll call it from another trap so that
324         // fireTrap() does not block.
325         auto locker = holdLock(*m_lock);
326         if (!m_signalSender)
327             m_signalSender = adoptRef(new SignalSender(locker, vm()));
328         m_trapSet->notifyAll(locker);
329     }
330 #endif
331 }
332
333 void VMTraps::handleTraps(ExecState* exec, VMTraps::Mask mask)
334 {
335     VM& vm = this->vm();
336     auto scope = DECLARE_THROW_SCOPE(vm);
337
338     {
339         auto codeBlockSetLocker = holdLock(vm.heap.codeBlockSet().getLock());
340         vm.heap.forEachCodeBlockIgnoringJITPlans(codeBlockSetLocker, [&] (CodeBlock* codeBlock) {
341             // We want to jettison all code blocks that have vm traps breakpoints, otherwise we could hit them later.
342             if (codeBlock->hasInstalledVMTrapBreakpoints())
343                 codeBlock->jettison(Profiler::JettisonDueToVMTraps);
344             return false;
345         });
346     }
347
348     ASSERT(needTrapHandling(mask));
349     while (needTrapHandling(mask)) {
350         auto eventType = takeTopPriorityTrap(mask);
351         switch (eventType) {
352         case NeedDebuggerBreak:
353             dataLog("VM ", RawPointer(&vm), " on pid ", getCurrentProcessID(), " received NeedDebuggerBreak trap\n");
354             invalidateCodeBlocksOnStack(exec);
355             break;
356                 
357         case NeedWatchdogCheck:
358             ASSERT(vm.watchdog());
359             if (LIKELY(!vm.watchdog()->shouldTerminate(exec)))
360                 continue;
361             FALLTHROUGH;
362
363         case NeedTermination:
364             throwException(exec, scope, createTerminatedExecutionException(&vm));
365             return;
366
367         default:
368             RELEASE_ASSERT_NOT_REACHED();
369         }
370     }
371 }
372
373 auto VMTraps::takeTopPriorityTrap(VMTraps::Mask mask) -> EventType
374 {
375     auto locker = holdLock(*m_lock);
376     for (int i = 0; i < NumberOfEventTypes; ++i) {
377         EventType eventType = static_cast<EventType>(i);
378         if (hasTrapForEvent(locker, eventType, mask)) {
379             clearTrapForEvent(locker, eventType);
380             return eventType;
381         }
382     }
383     return Invalid;
384 }
385
386 VMTraps::VMTraps()
387     : m_lock(Box<Lock>::create())
388     , m_trapSet(AutomaticThreadCondition::create())
389 {
390 }
391
392 VMTraps::~VMTraps()
393 {
394 #if ENABLE(SIGNAL_BASED_VM_TRAPS)
395     ASSERT(!m_signalSender);
396 #endif
397 }
398
399 } // namespace JSC