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