1/*
2 * Copyright (C) 2017-2018 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 "SigillCrashAnalyzer.h"
28
29#include "CallFrame.h"
30#include "CodeBlock.h"
31#include "MachineContext.h"
32#include "VMInspector.h"
33#include <mutex>
34#include <wtf/StdLibExtras.h>
35
36#if USE(ARM64_DISASSEMBLER)
37#include "A64DOpcode.h"
38#endif
39
40#include <wtf/threads/Signals.h>
41
42namespace JSC {
43
44struct SignalContext;
45
46class SigillCrashAnalyzer {
47public:
48 static SigillCrashAnalyzer& instance();
49
50 enum class CrashSource {
51 Unknown,
52 JavaScriptCore,
53 Other,
54 };
55 CrashSource analyze(SignalContext&);
56
57private:
58 SigillCrashAnalyzer() { }
59 void dumpCodeBlock(CodeBlock*, void* machinePC);
60
61#if USE(ARM64_DISASSEMBLER)
62 A64DOpcode m_arm64Opcode;
63#endif
64};
65
66#if OS(DARWIN)
67
68#if USE(OS_LOG)
69
70#define log(format, ...) \
71 os_log_info(OS_LOG_DEFAULT, format, ##__VA_ARGS__)
72
73#else // USE(OS_LOG)
74
75#define log(format, ...) \
76 dataLogF(format, ##__VA_ARGS__)
77
78#endif // USE(OS_LOG)
79
80struct SignalContext {
81private:
82 SignalContext(PlatformRegisters& registers, MacroAssemblerCodePtr<PlatformRegistersPCPtrTag> machinePC)
83 : registers(registers)
84 , machinePC(machinePC)
85 , stackPointer(MachineContext::stackPointer(registers))
86 , framePointer(MachineContext::framePointer(registers))
87 { }
88
89public:
90 static Optional<SignalContext> tryCreate(PlatformRegisters& registers)
91 {
92 auto instructionPointer = MachineContext::instructionPointer(registers);
93 if (!instructionPointer)
94 return WTF::nullopt;
95 return SignalContext(registers, *instructionPointer);
96 }
97
98 void dump()
99 {
100#if CPU(X86_64)
101#define FOR_EACH_REGISTER(v) \
102 v(rax) \
103 v(rbx) \
104 v(rcx) \
105 v(rdx) \
106 v(rdi) \
107 v(rsi) \
108 v(rbp) \
109 v(rsp) \
110 v(r8) \
111 v(r9) \
112 v(r10) \
113 v(r11) \
114 v(r12) \
115 v(r13) \
116 v(r14) \
117 v(r15) \
118 v(rip) \
119 v(rflags) \
120 v(cs) \
121 v(fs) \
122 v(gs)
123
124#define DUMP_REGISTER(__reg) \
125 log("Register " #__reg ": %p", reinterpret_cast<void*>(registers.__##__reg));
126 FOR_EACH_REGISTER(DUMP_REGISTER)
127#undef FOR_EACH_REGISTER
128
129#elif CPU(ARM64) && defined(__LP64__)
130 int i;
131 for (i = 0; i < 28; i += 4) {
132 log("x%d: %016llx x%d: %016llx x%d: %016llx x%d: %016llx",
133 i, registers.__x[i],
134 i+1, registers.__x[i+1],
135 i+2, registers.__x[i+2],
136 i+3, registers.__x[i+3]);
137 }
138 ASSERT(i < 29);
139 log("x%d: %016llx fp: %016llx lr: %016llx",
140 i, registers.__x[i],
141 MachineContext::framePointer<uint64_t>(registers),
142 MachineContext::linkRegister(registers).untaggedExecutableAddress<uint64_t>());
143 log("sp: %016llx pc: %016llx cpsr: %08x",
144 MachineContext::stackPointer<uint64_t>(registers),
145 machinePC.untaggedExecutableAddress<uint64_t>(),
146 registers.__cpsr);
147#endif
148 }
149
150 PlatformRegisters& registers;
151 MacroAssemblerCodePtr<PlatformRegistersPCPtrTag> machinePC;
152 void* stackPointer;
153 void* framePointer;
154};
155
156static void installCrashHandler()
157{
158#if CPU(X86_64) || CPU(ARM64)
159 installSignalHandler(Signal::Ill, [] (Signal, SigInfo&, PlatformRegisters& registers) {
160 auto signalContext = SignalContext::tryCreate(registers);
161 if (!signalContext)
162 return SignalAction::NotHandled;
163
164 void* machinePC = signalContext->machinePC.untaggedExecutableAddress();
165 if (!isJITPC(machinePC))
166 return SignalAction::NotHandled;
167
168 SigillCrashAnalyzer& analyzer = SigillCrashAnalyzer::instance();
169 analyzer.analyze(*signalContext);
170 return SignalAction::NotHandled;
171 });
172#endif
173}
174
175#else // OS(DARWIN)
176
177#define log(format, ...) do { } while (false)
178
179struct SignalContext {
180 SignalContext() { }
181
182 void dump() { }
183
184 MacroAssemblerCodePtr<PlatformRegistersPCPtrTag> machinePC;
185 void* stackPointer;
186 void* framePointer;
187};
188
189static void installCrashHandler()
190{
191 // Do nothing. Not supported for this platform.
192}
193
194#endif // OS(DARWIN)
195
196SigillCrashAnalyzer& SigillCrashAnalyzer::instance()
197{
198 static SigillCrashAnalyzer* analyzer;
199 static std::once_flag once;
200 std::call_once(once, [] {
201 installCrashHandler();
202 analyzer = new SigillCrashAnalyzer;
203 });
204 return *analyzer;
205}
206
207void enableSigillCrashAnalyzer()
208{
209 // Just instantiating the SigillCrashAnalyzer will enable it.
210 SigillCrashAnalyzer::instance();
211}
212
213auto SigillCrashAnalyzer::analyze(SignalContext& context) -> CrashSource
214{
215 CrashSource crashSource = CrashSource::Unknown;
216 log("BEGIN SIGILL analysis");
217
218 do {
219 // First, dump the signal context info so that we'll at least have the same info
220 // that the default crash handler would given us in case this crash analyzer
221 // itself crashes.
222 context.dump();
223
224 VMInspector& inspector = VMInspector::instance();
225
226 // Use a timeout period of 2 seconds. The client is about to crash, and we don't
227 // want to turn the crash into a hang by re-trying the lock for too long.
228 auto expectedLocker = inspector.lock(Seconds(2));
229 if (!expectedLocker) {
230 ASSERT(expectedLocker.error() == VMInspector::Error::TimedOut);
231 log("ERROR: Unable to analyze SIGILL. Timed out while waiting to iterate VMs.");
232 break;
233 }
234 auto& locker = expectedLocker.value();
235
236 void* pc = context.machinePC.untaggedExecutableAddress();
237 auto isInJITMemory = inspector.isValidExecutableMemory(locker, pc);
238 if (!isInJITMemory) {
239 log("ERROR: Timed out: not able to determine if pc %p is in valid JIT executable memory", pc);
240 break;
241 }
242 if (!isInJITMemory.value()) {
243 log("pc %p is NOT in valid JIT executable memory", pc);
244 crashSource = CrashSource::Other;
245 break;
246 }
247 log("pc %p is in valid JIT executable memory", pc);
248 crashSource = CrashSource::JavaScriptCore;
249
250#if CPU(ARM64)
251 size_t pcAsSize = reinterpret_cast<size_t>(pc);
252 if (pcAsSize != roundUpToMultipleOf<sizeof(uint32_t)>(pcAsSize)) {
253 log("pc %p is NOT properly aligned", pc);
254 break;
255 }
256
257 // We know it's safe to read the word at the PC because we're handling a SIGILL.
258 // Otherwise, we would have crashed with a SIGBUS instead.
259 uint32_t wordAtPC = *reinterpret_cast<uint32_t*>(pc);
260 log("instruction bits at pc %p is: 0x%08x", pc, wordAtPC);
261#endif
262
263 auto expectedCodeBlock = inspector.codeBlockForMachinePC(locker, pc);
264 if (!expectedCodeBlock) {
265 if (expectedCodeBlock.error() == VMInspector::Error::TimedOut)
266 log("ERROR: Timed out: not able to determine if pc %p is in a valid CodeBlock", pc);
267 else
268 log("The current thread does not own any VM JSLock");
269 break;
270 }
271 CodeBlock* codeBlock = expectedCodeBlock.value();
272 if (!codeBlock) {
273 log("machine PC %p does not belong to any CodeBlock in the currently entered VM", pc);
274 break;
275 }
276
277 log("pc %p belongs to CodeBlock %p of type %s", pc, codeBlock, JITCode::typeName(codeBlock->jitType()));
278
279 dumpCodeBlock(codeBlock, pc);
280 } while (false);
281
282 log("END SIGILL analysis");
283 return crashSource;
284}
285
286void SigillCrashAnalyzer::dumpCodeBlock(CodeBlock* codeBlock, void* machinePC)
287{
288#if CPU(ARM64) && ENABLE(JIT)
289 JITCode* jitCode = codeBlock->jitCode().get();
290
291 // Dump the raw bits of the code.
292 uint32_t* start = reinterpret_cast<uint32_t*>(jitCode->start());
293 uint32_t* end = reinterpret_cast<uint32_t*>(jitCode->end());
294 log("JITCode %p [%p-%p]:", jitCode, start, end);
295 if (start < end) {
296 uint32_t* p = start;
297 while (p + 8 <= end) {
298 log("[%p-%p]: %08x %08x %08x %08x %08x %08x %08x %08x", p, p+7, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
299 p += 8;
300 }
301 if (p + 7 <= end)
302 log("[%p-%p]: %08x %08x %08x %08x %08x %08x %08x", p, p+6, p[0], p[1], p[2], p[3], p[4], p[5], p[6]);
303 else if (p + 6 <= end)
304 log("[%p-%p]: %08x %08x %08x %08x %08x %08x", p, p+5, p[0], p[1], p[2], p[3], p[4], p[5]);
305 else if (p + 5 <= end)
306 log("[%p-%p]: %08x %08x %08x %08x %08x", p, p+4, p[0], p[1], p[2], p[3], p[4]);
307 else if (p + 4 <= end)
308 log("[%p-%p]: %08x %08x %08x %08x", p, p+3, p[0], p[1], p[2], p[3]);
309 if (p + 3 <= end)
310 log("[%p-%p]: %08x %08x %08x", p, p+2, p[0], p[1], p[2]);
311 else if (p + 2 <= end)
312 log("[%p-%p]: %08x %08x", p, p+1, p[0], p[1]);
313 else if (p + 1 <= end)
314 log("[%p-%p]: %08x", p, p, p[0]);
315 }
316
317 // Dump the disassembly of the code.
318 log("Disassembly:");
319 uint32_t* currentPC = reinterpret_cast<uint32_t*>(jitCode->executableAddress());
320 size_t byteCount = jitCode->size();
321 while (byteCount) {
322 char pcString[24];
323 if (currentPC == machinePC) {
324 snprintf(pcString, sizeof(pcString), "* 0x%lx", reinterpret_cast<uintptr_t>(currentPC));
325 log("%20s: %s <=========================", pcString, m_arm64Opcode.disassemble(currentPC));
326 } else {
327 snprintf(pcString, sizeof(pcString), "0x%lx", reinterpret_cast<uintptr_t>(currentPC));
328 log("%20s: %s", pcString, m_arm64Opcode.disassemble(currentPC));
329 }
330 currentPC++;
331 byteCount -= sizeof(uint32_t);
332 }
333#else
334 UNUSED_PARAM(codeBlock);
335 UNUSED_PARAM(machinePC);
336 // Not implemented yet.
337#endif
338}
339
340} // namespace JSC
341