objc_msgSend crash

My app experiencing a rare crash that I am unable to reproduce and am struggling to make progress with:

Thread 0 name:
Thread 0 Crashed:
0   libobjc.A.dylib               	0x00000001926c3c20 objc_msgSend + 32 (:-1)
1   Foundation                    	0x00000001997357b4 __NSThreadPerformPerform + 264 (NSThread.m:1084)
2   CoreFoundation                	0x000000019a82b834 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 28 (CFRunLoop.c:1957)
3   CoreFoundation                	0x000000019a82b7c8 __CFRunLoopDoSource0 + 176 (CFRunLoop.c:2001)
4   CoreFoundation                	0x000000019a8292f8 __CFRunLoopDoSources0 + 340 (CFRunLoop.c:2046)
5   CoreFoundation                	0x000000019a828484 __CFRunLoopRun + 828 (CFRunLoop.c:2955)
6   CoreFoundation                	0x000000019a827cd8 CFRunLoopRunSpecific + 608 (CFRunLoop.c:3420)
7   GraphicsServices              	0x00000001df2751a8 GSEventRunModal + 164 (GSEvent.c:2196)
8   UIKitCore                     	0x000000019ce61ae8 -[UIApplication _run] + 888 (UIApplication.m:3713)
9   UIKitCore                     	0x000000019cf15d98 UIApplicationMain + 340 (UIApplication.m:5303)
10  <redacted>                     	0x000000010287af04 main + 64 (AppDelegate.swift:15)
11  dyld                          	0x00000001bdfff154 start + 2356 (dyldMain.cpp:1298)

I have done a fair amount of digging, looking at other similar crashes and at this: https://developer.apple.com/forums/thread/92102 but being unable to reproduce is quite limiting.

I found this very similar issue with useful info on finding which function was being called: https://forums.developer.apple.com/forums/thread/67763

But in my case, the x1 (and x2) register values seem to point to an area outside of the ranges in the "Binary Images" section..

I've attached an example of a full crash report (with the app name redacted):

Any help would be greatly appreciated.

Answered by MattGApa in 816460022

Thanks for the response. We think we have a fix for this. It turns out this thread was more directly relevant than we realised: https://forums.developer.apple.com/forums/thread/67763

We found a variant of the crash which mentioned finishPlaying and attempts to call it on random types, e.g. CAShapeLayer here, but we also saw __NSDictionaryM and various others:

-[CAShapeLayer finishedPlaying:]: unrecognized selector sent to instance 0x30073a1e0
1  libobjc.A.dylib                0x172e4 objc_exception_throw
2  CoreFoundation                 0x1888c8 +[NSObject(NSObject) _copyDescription]
3  CoreFoundation                 0x20b08 ___forwarding___
4  CoreFoundation                 0x20430 _CF_forwarding_prep_0
5  Foundation                     0xa7e50 __NSThreadPerformPerform
6  CoreFoundation                 0x56328 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
7  CoreFoundation                 0x562bc __CFRunLoopDoSource0
8  CoreFoundation                 0x53e24 __CFRunLoopDoSources0
9  CoreFoundation                 0x52fbc __CFRunLoopRun
10 CoreFoundation                 0x52830 CFRunLoopRunSpecific
11 GraphicsServices               0x11c4 GSEventRunModal
12 UIKitCore                      0x3d2eb0 -[UIApplication _run]
13 UIKitCore                      0x4815b4 UIApplicationMain
14 <redacted>                     0x74d9c main + 16 (AppDelegate.swift:16)
15 ???                            0x1b7b36ec8 (Missing)

This seems to happen if an AVAudioPlayer is deallocated the split second it has finished playing.

This is the code we had:

let audioPlayer = try AVAudioPlayer(data: audioData)
audioPlayer.prepareToPlay()
audioPlayer.play(atTime: audioPlayer.deviceCurrentTime + delay)
self.audioPlayer = audioPlayer

It was possible for this to be called just after a previous sound had finished and deallocate the current self.audioPlayer (and we have a large enough user base (in the millions) for this to happen in the wild).

Our fix was to add this to stop any existing audio player before reassigning it and we have not see the crash since we released this yesterday:

+ self.audioPlayer?.stop()
let audioPlayer = try AVAudioPlayer(data: audioData)
audioPlayer.prepareToPlay()
audioPlayer.play(atTime: audioPlayer.deviceCurrentTime + delay)
self.audioPlayer = audioPlayer

Easy to reproduce

Incidentally, it is pretty trivial to force this crash to happen, if it's something that could be worked on and prevented by Apple:

let audioPlayer = try AVAudioPlayer(data: audioData)
audioPlayer.prepareToPlay()
audioPlayer.play(atTime: audioPlayer.deviceCurrentTime + delay)
self.audioPlayer = audioPlayer
while audioPlayer.isPlaying {
    // Wait
}
self.audioPlayer = nil // deallocation immediately after isPlaying becomes false causes crash

Focusing on this:

0   libobjc.A.dylib … objc_msgSend + 32 (:-1)
1   Foundation      … __NSThreadPerformPerform + 264 (NSThread.m:1084)

Internally, the NSThread perform subsystem bundles the various perform arguments — the target, selector, arguments, run loop modes, and other bookkeeping stuff — up into an internal object and puts that on a queue. The run loop then pulls items off that queue and runs them. It does that by calling an -invoke method on that internal object.

AFAICT the callsite in frame 1 is the run loop calling that -invoke method, which means that somehow this object managed to get corrupted, either going into the queue, while on the queue, or coming out of the queue.

I can’t see any obvious way of that happening. This brings us back to the standard memory debugging tools, where the goal is to try to make the program more reproducible, and hence debuggable. Sadly, that doesn’t seem to have worked in this case.

Share and Enjoy

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

Accepted Answer

Thanks for the response. We think we have a fix for this. It turns out this thread was more directly relevant than we realised: https://forums.developer.apple.com/forums/thread/67763

We found a variant of the crash which mentioned finishPlaying and attempts to call it on random types, e.g. CAShapeLayer here, but we also saw __NSDictionaryM and various others:

-[CAShapeLayer finishedPlaying:]: unrecognized selector sent to instance 0x30073a1e0
1  libobjc.A.dylib                0x172e4 objc_exception_throw
2  CoreFoundation                 0x1888c8 +[NSObject(NSObject) _copyDescription]
3  CoreFoundation                 0x20b08 ___forwarding___
4  CoreFoundation                 0x20430 _CF_forwarding_prep_0
5  Foundation                     0xa7e50 __NSThreadPerformPerform
6  CoreFoundation                 0x56328 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
7  CoreFoundation                 0x562bc __CFRunLoopDoSource0
8  CoreFoundation                 0x53e24 __CFRunLoopDoSources0
9  CoreFoundation                 0x52fbc __CFRunLoopRun
10 CoreFoundation                 0x52830 CFRunLoopRunSpecific
11 GraphicsServices               0x11c4 GSEventRunModal
12 UIKitCore                      0x3d2eb0 -[UIApplication _run]
13 UIKitCore                      0x4815b4 UIApplicationMain
14 <redacted>                     0x74d9c main + 16 (AppDelegate.swift:16)
15 ???                            0x1b7b36ec8 (Missing)

This seems to happen if an AVAudioPlayer is deallocated the split second it has finished playing.

This is the code we had:

let audioPlayer = try AVAudioPlayer(data: audioData)
audioPlayer.prepareToPlay()
audioPlayer.play(atTime: audioPlayer.deviceCurrentTime + delay)
self.audioPlayer = audioPlayer

It was possible for this to be called just after a previous sound had finished and deallocate the current self.audioPlayer (and we have a large enough user base (in the millions) for this to happen in the wild).

Our fix was to add this to stop any existing audio player before reassigning it and we have not see the crash since we released this yesterday:

+ self.audioPlayer?.stop()
let audioPlayer = try AVAudioPlayer(data: audioData)
audioPlayer.prepareToPlay()
audioPlayer.play(atTime: audioPlayer.deviceCurrentTime + delay)
self.audioPlayer = audioPlayer

Easy to reproduce

Incidentally, it is pretty trivial to force this crash to happen, if it's something that could be worked on and prevented by Apple:

let audioPlayer = try AVAudioPlayer(data: audioData)
audioPlayer.prepareToPlay()
audioPlayer.play(atTime: audioPlayer.deviceCurrentTime + delay)
self.audioPlayer = audioPlayer
while audioPlayer.isPlaying {
    // Wait
}
self.audioPlayer = nil // deallocation immediately after isPlaying becomes false causes crash

I’m glad to hear you’re making progress.

Incidentally, it is pretty trivial to force this crash to happen, if it's something that could be worked on and prevented by Apple:

While I’m not particularly au fait with AVFoundation, I think it’s worth you filing a bug about that. Please post your bug number, just for the record.

Share and Enjoy

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

Thanks again for responding. As suggested, I've filed a bug: FB16038126

objc_msgSend crash
 
 
Q