Obscure crash report

Hello everyone!

I have a crash report that I've been trying to figure out for almost a week now.

The crash only reproduces on release, never on debug, and it’s cryptic to me. I tried to optimise the build so that I can have the release mode directly from Xcode, but when I attach it to the process it never reproduces.

Could you help, please?

I have attached the full crash log.

Thank

you!

This is quite tricky. The immediate cause of the crash is this:

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000000000000

You’ve crashed due to a memory access exception because you’ve referenced nil. The backtrace of the crash thread looks like this:

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   libswiftCore.dylib       … swift_getObjectType + 36
1   libswiftCore.dylib       … findDynamicValueAndType+ 3101980 
    (swift::OpaqueValue*, swift::TargetMetadata<swift::InProcess> const*, 
    swift::OpaqueValue*&, swift::TargetMetadata<swift::InProcess> const*&, 
    bool&, bool, bool) + 308
2   libswiftCore.dylib       … findDynamicValueAndType+ 3101980 
    (swift::OpaqueValue*, swift::TargetMetadata<swift::InProcess> const*, 
    swift::OpaqueValue*&, swift::TargetMetadata<swift::InProcess> const*&, 
    bool&, bool, bool) + 308
3   libswiftCore.dylib       … _dynamicCastToExistential+ 3112148 
    (swift::OpaqueValue*, swift::OpaqueValue*, 
    swift::TargetMetadata<swift::InProcess> const*, 
    swift::TargetExistentialTypeMetadata<swift::InProcess> const*, 
    swift::DynamicCastFlags) + 152
4   libswiftFoundation.dylib … specialized _arrayForceCast<A, B>+ 207620 
    (_:) + 524
5   libswiftFoundation.dylib … 
    static Array._unconditionallyBridgeFromObjectiveC+ 293220 (_:) + 276
6   ClickDocDE               … thunk for @escaping @callee_guaranteed 
    (@guaranteed [Any]) -> () + 2127344 (<compiler-generated>:0)
7   libdispatch.dylib        … _dispatch_call_block_and_release + 24

Note I’ve done a bunch of manual line wrapping to make this easier to read.

Frames 6 is the most interesting frame here. This thunk was generated by the compiler to check incoming parameters. For example, if you implement an IBAction in Swift like so:

@IBAction
private func okAction(_ button: UIButton) { … your code … }

then the compiler emits two functions:

  • A Swift function that represents your code.

  • An Objective-C compatible thunk that checks the incoming parameter (that it’s not nil and that it’s actually a UIButton) and then calls through to your Swift function. This ensures that undefined behaviour is caught early.

Normally these thunks have a meaningful name because they wrap a named function. In this case, however, there’s no name, most likely because the original Swift function is a closure.

What we can tell is that the function has a single parameter of type [Any]. This explains frame 5, which is the thunk’s call to Array._unconditionallyBridgeFromObjectiveC to check that the incoming NSArray is compatible with the Swift type [Any]. It’s this call that crashes. I originally thought that it might be because the NSArray was nil, but I tested that case [1] and it doesn’t crash; rather, the callee just gets an an empty array.

Beyond that it starts to get harder. Annoyingly, frames 7 and up are just standard Dispatch infrastructure, so that doesn’t offer any clues as to where things went wrong.

I can see two paths forward here:

  • Dig into the Swift runtime to uncover exactly what’s causing it to dereference nil.

  • Try to locate the original Swift code associated with frame 6.

I can’t really help you with the first path; I don’t have nearly enough expertise in the Swift runtime’s internals to offer any immediate insight. You might want to ask over on Swift Forums.

With regards the second path, it’s likely that this thunk was emitted right next to the original code, so you could:

  1. Find the binary that corresponds to this crash report, matching it up using the UUID in the Binary Images section.

  2. Load that up in the debugger.

  3. Map the address in frame 6 (0x000000010461f5f0) to the address in your loaded binary. You have to undo the ASLR using the load address (0x104418000) from the Binary Images section.

  4. Disassemble around that to see what’s nearby.

Like I said, this is quite tricky.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Using code like this:

// Swift

Hack.test { input in
    print(input)
}

// Objective-C

+ (void)testWithBlock:(void (^)(NSArray *))block {
    block(nil);
}

Notably, if I set a breakpoint on the print then the backtrace includes this line:

frame #1: 0x000000010f6254c6 xxsi`thunk for @escaping @callee_guaranteed (@guaranteed [Any]) -> () at <compiler-generated>:0

which is good evidence for my thunk theory.

Obscure crash report
 
 
Q