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