tmandry/AXSwift

Crash in internalInfoCallback

Closed this issue · 5 comments

choco commented

I've got this crash more than a couple of times in an app, Shifty, using AXSwift

Crashed Thread:        0  Dispatch queue: com.apple.main-thread

Exception Type:        EXC_BAD_ACCESS (SIGSEGV)
Exception Codes:       EXC_I386_GPFLT
Exception Note:        EXC_CORPSE_NOTIFY

Termination Signal:    Segmentation fault: 11
Termination Reason:    Namespace SIGNAL, Code 0xb
Terminating Process:   exc handler [576]

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   libswiftCore.dylib            	0x00000001093e14e1 swift::RefCounts<swift::RefCountBitsT<(swift::RefCountInlinedness)1> >::incrementSlow(swift::RefCountBitsT<(swift::RefCountInlinedness)1>, unsigned int) + 33
1   libswiftCore.dylib            	0x00000001093c9984 _swift_retain_(swift::HeapObject*) + 68
2   org.cocoapods.AXSwift         	0x0000000108f04078 specialized internalInfoCallback(_:axElement:notification:cfInfo:userData:) + 552
3   org.cocoapods.AXSwift         	0x0000000108f034ee @objc internalInfoCallback(_:axElement:notification:cfInfo:userData:) + 78
4   com.apple.HIServices          	0x00007fff4b190672 _XXMIGPostNotification + 821
5   com.apple.HIServices          	0x00007fff4b19ddac _XPostNotification + 351
6   com.apple.HIServices          	0x00007fff4b172abe mshMIGPerform + 212
7   com.apple.CoreFoundation      	0x00007fff4c9cc6a9 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 41
8   com.apple.CoreFoundation      	0x00007fff4c9cc607 __CFRunLoopDoSource1 + 527
9   com.apple.CoreFoundation      	0x00007fff4c9b4689 __CFRunLoopRun + 2574
10  com.apple.CoreFoundation      	0x00007fff4c9b3a28 CFRunLoopRunSpecific + 463
11  com.apple.HIToolbox           	0x00007fff4bc4cb35 RunCurrentEventLoopInMode + 293
12  com.apple.HIToolbox           	0x00007fff4bc4c774 ReceiveNextEventCommon + 371
13  com.apple.HIToolbox           	0x00007fff4bc4c5e8 _BlockUntilNextEventMatchingListInModeWithFilter + 64
14  com.apple.AppKit              	0x00007fff49f08eb7 _DPSNextEvent + 997
15  com.apple.AppKit              	0x00007fff49f07c56 -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 1362
16  com.apple.AppKit              	0x00007fff49f01cb9 -[NSApplication run] + 699
17  com.apple.AppKit              	0x00007fff49ef13f7 NSApplicationMain + 780
18  io.natethompson.Shifty        	0x0000000108e022a9 0x108e00000 + 8873
19  libdyld.dylib                 	0x00007fff79b9608d start + 1

It doesn't always has the same backtrace, but internalInfoCallback seems to be the cause for all of them.
Looking at the disassembly the crash seems to be caused by the line

    observer.callbackWithInfo!(observer, element, notif, info)

The observer, or the callback aren't valid I think.
This is probably because I'm not using it correctly in the app Shifty (I would actually ask you what is the correct way to stop an observer), but we should probably not crash either way, and probably check if the observer or the callback are valid?

The offending code in Shifty is in this file https://github.com/thompsonate/Shifty/blob/5d58e021827b77fb00b850eb522fa6b73c158626/Shifty/BrowserManager.swift in the stopBrowserWatcher. I wonder if I should remove the notifications instead of calling stop.

Hmm, we should have called stop() on Observer deinit. Merging a fix soon. Thanks for your report!

Fixed in #12.

I'm still getting this issue in Shifty with AXSwift 0.2.3.

Looks like the offending line is still Observer.swift:162: observer.callbackWithInfo!(observer, element, notif, info) in Observer.swift. Not sure if it's the root cause, but is there any reason to be force unwrapping there?

Here's BrowserManager.swift:171 referenced in my crash report.

Here's the stack trace from my crash reporter:

Shifty                closure #1 (AXSwift.Observer, AXSwift.UIElement, AXSwift.AXNotification, [Swift.String : Swift.AnyObject]?) -> () in static Shifty.BrowserManager.(startBrowserWatcher in _BCA98A1A69929549465A957C57870AC7)(_: Swift.Int32, callback: () -> ()) throws -> () BrowserManager.swift:171
Shifty                partial apply forwarder for closure #1 (AXSwift.Observer, AXSwift.UIElement, AXSwift.AXNotification, [Swift.String : Swift.AnyObject]?) -> () in static Shifty.BrowserManager.(startBrowserWatcher in _BCA98A1A69929549465A957C57870AC7)(_: Swift.Int32, callback: () -> ()) throws -> () <compiler-generated>:0
AXSwift               function signature specialization <Arg[0] = Dead> of AXSwift.(internalInfoCallback in _1C4E6C7EFC73FEB542EA8C20A2603D9E)(_: __C.AXObserverRef, axElement: __C.AXUIElementRef, notification: __C.CFStringRef, cfInfo: __C.CFDictionaryRef, userData: Swift.UnsafeMutableRawPointer?) -> () Observer.swift:162
AXSwift               @objc AXSwift.(internalInfoCallback in _1C4E6C7EFC73FEB542EA8C20A2603D9E)(_: __C.AXObserverRef, axElement: __C.AXUIElementRef, notification: __C.CFStringRef, cfInfo: __C.CFDictionaryRef, userData: Swift.UnsafeMutableRawPointer?) -> () <compiler-generated>:0
HIServices            0x7fff2cbe7000 + 145281
HIServices            0x7fff2cbe7000 + 200325
HIServices            0x7fff2cbe7000 + 23777
CoreFoundation        0x7fff2e34b000 + 338679
CoreFoundation        0x7fff2e34b000 + 338517
CoreFoundation        0x7fff2e34b000 + 240188
CoreFoundation        0x7fff2e34b000 + 237070
HIToolbox             0x7fff2d667000 + 43483
HIToolbox             0x7fff2d667000 + 42773
HIToolbox             0x7fff2d667000 + 42150
AppKit                0x7fff2b9f1000 + 110587
AppKit                0x7fff2b9f1000 + 105875
AppKit                0x7fff2b9f1000 + 81584
AppKit                0x7fff2b9f1000 + 13296
Shifty                main AppDelegate.swift:21
libdyld.dylib         0x7fff5a7cc000 + 91093

The issue isn't Observer.swift:162; from the backtrace, it's within your handler. Possibly one of the force casts or unwraps in attribute() is failing. These aren't supposed to fail (obviously).

In UIElement.swift in AXSwift, try replacing this line:

return (unpackAXValue(value!) as! T)

with:

        guard let unwrapped = value else {
            fatalError("attribute \(attribute) of \(self) was nil")
        }

        let unpacked = unpackAXValue(unwrapped)
        guard let casted = unpacked as? T else {
            fatalError("attribute \(attribute) of \(self) was unexpected type: \(String(describing: value))")
        }
        return casted

That way if you hit it again, you'll get more useful errors. Or you'll rule out that as the source of the crash.

To answer your question, the reason the code force-unwraps in Observer.swift is because if we are receiving events, the observer must still be valid. If it's not, checking one of its fields isn't going to help. Checking whether a field on it has gone nil that should NEVER go nil means we just noticed we were reading invalid memory, and quietly ignored it, instead of failing loudly which we should be doing.

If you do get anything from the code I pasted above, please open a new issue and report all the output you get. That would definitely be a bug in AXSwift and we should be handling these cases more gracefully if they do happen.

Re-closing for now because I think the original issue here is fixed.