Weird crashes when accessing Swift Array

For some time now Xcode has been downloading crash reports from users of my app about crashes related to arrays. One of them looks like this:

...
Code Type:             ARM-64
Parent Process:        launchd [1]
User ID:               501

Date/Time:             2024-07-18 14:59:40.4375 +0800
OS Version:            macOS 15.0 (24A5289h)
...

Crashed Thread:        0

Exception Type:        EXC_BREAKPOINT (SIGTRAP)
Exception Codes:       0x0000000000000001, 0x00000001045048b8

Termination Reason:    Namespace SIGNAL, Code 5 Trace/BPT trap: 5
Terminating Process:   exc handler [1771]


Thread 0 Crashed:
0   MyApp                      	0x00000001045048b8 specialized Collection.map<A>(_:) + 596
1   MyApp                      	0x00000001045011e4 MyViewController.validateToolbarButtons() + 648 (MyViewController.swift:742)
...

The relevant code looks like this:

class MyViewController {
    func validateToolbarButtons() {
        let indexes = tableView.clickedRow == -1 || tableView.selectedRowIndexes.contains(tableView.clickedRow) ? tableView.selectedRowIndexes : IndexSet(integer: tableView.clickedRow)
        let items = indexes.map({ myArray[$0] })
        ...
    }
}

The second crash looks like this:

...
Code Type:             X86-64 (Native)
Parent Process:        launchd [1]
User ID:               502

Date/Time:             2024-07-15 15:53:35.2229 -0400
OS Version:            macOS 15.0 (24A5289h)
...

Crashed Thread:        0

Exception Type:        EXC_BAD_INSTRUCTION (SIGILL)
Exception Codes:       0x0000000000000001, 0x0000000000000000

Termination Reason:    Namespace SIGNAL, Code 4 Illegal instruction: 4
Terminating Process:   exc handler [13244]


Thread 0 Crashed:
0   libswiftCore.dylib              	0x00007ff812904fc0 _assertionFailure(_:_:flags:) + 288
1   MyApp                            	0x0000000101a31e04 specialized _ArrayBuffer._getElementSlowPath(_:) + 516
2   MyApp                            	0x00000001019d04eb MyObject.myProperty.setter + 203 (MyObject.swift:706)
3   MyApp                            	0x000000010192f66e MyViewController.controlTextDidChange(_:) + 190 (MyViewController.swift:166)
...

And the relevant code looks like this:

class MyObject {
    var myProperty: [MyObject] {
        get {
            ...
        }
        set {
            let items = newValue.map({ $0.id })
            ...
        }
    }
}

What could cause such crashes? Could they be caused by anything other than concurrent access from multiple threads (which I'm quite sure is not the case here, as I only access these arrays from the main thread)?

Answered by DTS Engineer in 796453022

While these crashes have different codes, they’re likely to be caused by the some immediate issue: a Swift runtime trap. See EXC_BREAKPOINT (SIGTRAP) and EXC_BAD_INSTRUCTION (SIGILL). You get different exception codes because the first crash is on Apple silicon and the second on Intel.


Regarding the first crash, the most likely cause is this: myArray[$0]. If $0 is out of bounds, the array access will trap.


The second trap is trickier. Note how frame is _getElementSlowPath(…). This suggests that you’re working with a non-native array, like an NSArray. Those do lazy type enforcement, so the setter will check that the element type is correct and trap if it’s not. I think that’s what you’re hitting here.

Consider this program:

import Foundation

class MyElement {
    let myProperty: Int
    init(myProperty: Int) {
        self.myProperty = myProperty
    }
}

func printElements(ns: NSArray) {
    print("before cast")
    let a = ns as! [MyElement]
    print("after cast")
    for e in a {
        print(e.myProperty)
    }
}

func main() {
    let ns = NSMutableArray()
    ns.add(MyElement(myProperty: 42))
    ns.add(MyElement(myProperty: 6))
    ns.add("Hello Cruel World!" as NSString)
    ns.add(MyElement(myProperty: 7))
    printElements(ns: ns)
}

main()

When I run it on my Mac (Xcode 16.0b3 on macOS 14.5) I see this output:

before cast
after cast
42
6
Fatal error: NSArray element failed to match the Swift Array Element type
Expected MyElement but found __NSCFString

This shows how the ns as! [MyElement] cast is done lazily, only triggering a trap when you try to access the element that’s the wrong type.

Notably, the crashing thread backtrace looks like this:

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = Fatal error: NSArray element failed to match the Swift Array Element type
Expected MyElement but found __NSCFString
    frame #0: 0x00000001a53f9890 libswiftCore.dylib`_swift_runtime_on_report
    frame #1: 0x00000001a54b8c74 libswiftCore.dylib`_swift_stdlib_reportFatalError + 176
    frame #2: 0x00000001a508b114 libswiftCore.dylib`function signature specialization <Arg[1] = [Closure Propagated : closure #1 (Swift.UnsafeBufferPointer<Swift.UInt8>) -> () in closure #1 (Swift.UnsafeBufferPointer<Swift.UInt8>) -> () in Swift._assertionFailure(_: Swift.StaticString, _: Swift.String, flags: Swift.UInt32) -> Swift.Never, Argument Types : [Swift.UnsafeBufferPointer<Swift.UInt8>Swift.UInt32]> of generic specialization <()> of Swift.String.withUTF8<τ_0_0>((Swift.UnsafeBufferPointer<Swift.UInt8>) throws -> τ_0_0) throws -> τ_0_0 + 156
    frame #3: 0x00000001a5062c84 libswiftCore.dylib`Swift._assertionFailure(_: Swift.StaticString, _: Swift.String, flags: Swift.UInt32) -> Swift.Never + 180
    frame #4: 0x00000001a5064c38 libswiftCore.dylib`Swift._ArrayBuffer._getElementSlowPath(Swift.Int) -> Swift.AnyObject + 2056
    frame #5: 0x00000001a506b6d8 libswiftCore.dylib`Swift.Array.subscript.read : (Swift.Int) -> τ_0_0 + 304
    frame #6: 0x00000001a506b574 libswiftCore.dylib`protocol witness for Swift.Collection.subscript.read : (τ_0_0.Index) -> τ_0_0.Element in conformance Swift.Array<τ_0_0> : Swift.Collection in Swift + 68
    frame #7: 0x00000001a5055e4c libswiftCore.dylib`Swift.IndexingIterator.next() -> Swift.Optional<τ_0_0.Element> + 668
  * frame #8: 0x0000000100001a64 Test`printElements(ns=4 elements) at main.swift:14:5
    frame #9: 0x00000001000016d0 Test`main() at main.swift:25:5
    frame #10: 0x000000010000159c Test`main at main.swift:28:1
    frame #11: 0x0000000194ffe0e0 dyld`start + 2360

Frames 4 and 3 should ring some bells.

Share and Enjoy

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

Weird crashes when accessing Swift Array
 
 
Q