Skip to content

Understanding the exception types in a crash report

EXC_BREAKPOINT (SIGTRAP) and EXC_BAD_INSTRUCTION (SIGILL)

The breakpoint exception type indicates a trace trap interrupted the process. A trace trap gives an attached debugger the chance to interrupt the process at a specific point in its execution.

  • On ARM processors, this appears as EXC_BREAKPOINT (SIGTRAP).
  • On x86_64 processors, this appears as EXC_BAD_INSTRUCTION (SIGILL).

The Swift runtime uses trace traps for specific types of unrecoverable errors.

Swift Force Unwrap

If you use the ! operator to force unwrap an optional value that’s nil, or if you force a type downcast that fails with the as! operator, the Swift runtime catches these errors and intentionally crashes the app.

On ARM processors, the exception info in the crash report looks like:

Exception Type: EXC_BREAKPOINT (SIGTRAP)
...
Termination Signal: Trace/BPT trap: 5
Termination Reason: Namespace SIGNAL, Code 0x5

On Intel processors (including apps for macOS, Mac Catalyst, and the simulators for iOS, watchOS and tvOS), the exception info in the crash report looks like:

Exception Type: EXC_BAD_INSTRUCTION (SIGILL)
...
Exception Note: EXC_CORPSE_NOTIFY

Termination Signal: Illegal instruction: 4
Termination Reason: Namespace SIGNAL, Code 0x4

Swift Array Out of Range

swift
let array:[Int] = [1,2,3]
let item = array[4]
let array:[Int] = [1,2,3]
let item = array[4]

Swift Array Out of Range

Others

Some low-level libraries, such as Dispatch, trap the process with this exception upon encountering an unrecoverable error, and log additional information about the error in the Additional Diagnostic Information section of the crash report.

If you want to use the same technique in your own code for unrecoverable errors, call __builtin_trap() function. This allows the system to generate a cash report with thread backtraces that show how you reached the unrecoverable error.

EXC_BAD_ACCESS

The crash is due to a memory access issue.

A crash due to a memory access issue occurs when an app uses memory in an unexpected way. Memory access problems have numerous causes, such as

  • dereferencing a pointer to an invalid memory address
  • writing to read-only memory
  • jumping to an instruction at an invalid address

These crashes are most identified by the EXC_BAD_ACCESS (SIGSEGV) or EXC_BAD_ACCESS (SIGBUS) exceptions in the crash report.

On macOS, bad memory access crashes are occasionally identified only by a signal, such as SIGSEGV, SEGV_MAPERR, or SEGV_NOOP.

Exception Subtype

The Exception Subtype field in the crash report contains a kern_return_t value describing the error and the address of the memory that was incorrectly accessed.

There are several exception subtypes:

  • KERN_INVALID_ADDRESS
    The crashed thread accessed unmapped memory, either by accessing data or an instruction fetch.
    The arm64e CPU architecture uses pointer authentication codes with cryptographic signatures to detect and guard against unexpected changes to pointers in memory. A crash due to a possible pointer authentication failure uses the KERN_INVALID_ADDRESS exception subtype with an additional message on the end:

Exception Type: EXC_BAD_ACCESS (SIGBUS)
Exception Subtype: KERN_INVALID_ADDRESS at 0x00006f126c1a9aa0 -> 0x000000126c1a9aa0 (possible pointer authentication failure)

  • KERN_PROTECTION_FAILURE
    The crashed thread tried to use a valid memory address that’s protected. Some types of protected memory include read-only memory regions, or nonexecutable memory regions.
  • KERN_MEMORY_ERROR
    The crashed thread tried to access memory that couldn’t return data at that moment, such as a memory-mapped file that became unavailable.
  • EXC_ARM_DA_ALIGN
    The crashed thread tried to access memory that isn’t appropriately aligned. This exception code is rare because 64-bit ARM CPUs work with misaligned data. However, you may see this exception subtype if the memory address is both misaligned and located in an unmapped memory region.

Check the crashed thread’s backtrace for clues about the source of the memory access issue

  • If objc_msgSend, objc_retain, or objc_release is at the top of the backtrace, the crash is due to a zombie object.
    Once an Objective-C or Swift object no longer has any strong references to it, the object is deallocated. Attempting to further send messages to the object as if it were still a valid object is a “use after free” issue, with the deallocated object still receiving messages called a zombie object.
    Another pattern that indicates a zombie object is a stack frame for an unrecognized selector, which is a method that an object doesn’t implement. The operating system reused memory that once held the deallocated object, and that memory now contains a different kind of object.
  • If gpus_ReturnNotPermittedKillClient is at the top of the backtrace, the operating system terminated the process because it attempted to do rendering with OpenGL ES while in the background.

Memory corruption

Memory corruption occurs when a memory location is unexpectedly modified. After this modification, another part of your app may crash when it tries to use that memory location.

The backtrace shows the code accessing the modified memory, but not the code that unexpectedly modified the memory. The unexpected modification may have occurred a long time before the crash, so the source of the issue isn’t visible in the backtrace.

If you have numerous crash reports with signs of a memory access issue, but with different backtraces, you may have a memory corruption issue.

Check the program counter register to determine which type of memory access: memory fetches or instruction fetches

There are two categories of memory access issues: invalid memory fetches and invalid instruction fetches.

  • An invalid memory fetch occurs when code dereferences an invalid pointer.
  • An invalid instruction fetch occurs when a function jumps to another function through a bad function pointer, or through a function call to an unexpected object.

To determine which type of memory access issue caused a crash, focus on the program counter, a register that contains the address of the instruction that caused the memory access exception.

On ARM CPU architectures, this is the pc register. On the x86_64 CPU architecture, this is the rip register.

  • If the program counter register isn’t the same as the exception address, the crash is due to an invalid memory fetch.
  • If the program counter register is the same as the exception address, the crash is due to an invalid instruction fetch.
    On ARM CPU architectures, the link register lr, contains the location the code would return to after a function call under normal circumstances. The value in the link register allows you trace the origin of the jump to a bad instruction pointer.
    The x86_64 CPU architecture stores return addresses on the stack, instead of in a link register, so you can’t trace the origin of the bad function pointer on x86_64 CPUs.

Memory Debugging Tools

Xcode contains a suite of debugging tools you can use to identify memory access issues as your app runs.

  • Address Sanitizer
  • Undefined Behavior Sanitizer
  • Thread Sanitizer

EXC_CRASH (SIGABRT)

EXC_CRASH (SIGABRT) indicates the process terminated because it received the SIGABRT signal. Typically, this signal is sent because a function in the process called abort().

  • Uncaught Objective-C or C++ language exception
    Language exceptions, such as those from Objective-C, indicate programming errors discovered at runtime, such as accessing an array with an index that’s out-of-bounds or not implementing a required method of a protocol.
  • App extension takes too much time to initialize.
    When an app extension takes too much time to initialize, the operating system sends a SIGABRT to the app extension process. These crashes include an Exception Subtype field with the value LAUNCH_HANG.

EXC_CRASH (SIGKILL)

EXC_CRASH (SIGKILL) indicates the operating system terminated the process. The crash report contains a Termination Reason field with a code that explains the reason for the crash. In the following example, that code is 0xdead10cc:

Exception Type: EXC_CRASH (SIGKILL)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
Termination Reason: Namespace RUNNINGBOARD, Code 0xdead10cc

The code is one of the following values:

  • 0x8badf00d (pronounced “ate bad food”).
    The operating system’s watchdog terminated the app.
  • 0xc00010ff (pronounced “cool off”).
  • 0xdead10cc (pronounced “dead lock”).
  • 0xbaadca11 (pronounced “bad call”).
  • 0xbad22222.
  • 0xbaddd15c (pronounced “bad disc”).
  • 0xc51bad01.
  • 0xc51bad02.
  • 0xc51bad03.

References