KERN_PROTECTION_FAILURE (stack overflow) in libobjc dealloc - ios

I recently received a crash report that looks like this:
0 libobjc.A.dylib 0x0000000193dfea88 object_cxxDestructFromClass(objc_object*, objc_class*) + 0
1 libobjc.A.dylib 0x0000000193e0bf34 objc_destructInstance + 88
2 libobjc.A.dylib 0x0000000193e0bf8c object_dispose + 24
3 My App 0x00000001000d88fc -[CCAction dealloc] + 44
4 My App 0x00000001000e6bf8 -[CCActionSequence .cxx_destruct] + 36
5 libobjc.A.dylib 0x0000000193dfeb18 object_cxxDestructFromClass(objc_object*, objc_class*) + 144
6 libobjc.A.dylib 0x0000000193e0bf34 objc_destructInstance + 88
7 libobjc.A.dylib 0x0000000193e0bf8c object_dispose + 24
8 My App 0x00000001000d88fc -[CCAction dealloc] + 44
9 My App 0x00000001000e6bf8 -[CCActionSequence .cxx_destruct] + 36
10 libobjc.A.dylib 0x0000000193dfeb18 object_cxxDestructFromClass(objc_object*, objc_class*) + 144
11 libobjc.A.dylib 0x0000000193e0bf34 objc_destructInstance + 88
12 libobjc.A.dylib 0x0000000193e0bf8c object_dispose + 24
13 My App 0x00000001000d88fc -[CCAction dealloc] + 44
14 My App 0x00000001000e6bf8 -[CCActionSequence .cxx_destruct] + 36
...and it continues on like this forever. Some kind of infinite recursion happening in dealloc that is causing a stack overflow.
There are only 2 method calls that are visible to me and one of them is .cxx_destruct which is an internal private method used by ARC. The other is the dealloc method of CCAction which only has a log statement in it:
-(void) dealloc {
CCLOGINFO(#"cocos2d: deallocing %#", self);
}
The crash is really hard to reproduce; I've only seen it once. However my app has not been released to the public yet so I'd really like to figure out what is causing it. Any help debugging this would be greatly appreciated.
I am using Cocos2D 3.3.0 and iOS 8.1

If you are using ARC, sending messages to self in dealloc doesn't work like how it used to in manual memory management. ARC does all of the cleanup/disposing in .cxx_destruct BEFORE calling dealloc, so sending messages to self in dealloc will cause undefined behavior - most probably crashes. Unlike in manual memory management where you can send messages to self in dealloc before calling [super dealloc].
From your code, it looks like CCLogInfo is sending a message to self, most probably [self description] (assuming CCLogInfo parameters are just a format string with arguments). This will cause a crash, but I'm not sure why it is causing recursion - can you share the code to CCLogInfo?

Related

Possible reason for EXC_BAD_ACCESS?

I have a legacy app which utilizes Apple's example SimplePing. There is a source file SimplePing.m which contains the next method:
- (void)sendPingWithData:(NSData *)data {
id<SimplePingDelegate> strongDelegate;
...
strongDelegate = self.delegate;
if (...) {
[strongDelegate simplePing:self didSendPacket:...];
}
self.nextSequenceNumber += 1; // CRASH
if (self.nextSequenceNumber == 0) {
self.nextSequenceNumberHasWrapped = YES;
}
}
Crashlytics reports dozens of crashes with stack trace:
Crashed: com.apple.main-thread
EXC_BAD_ACCESS KERN_INVALID_ADDRESS 0x00000009de3bbeb8
libobjc.A.dylib objc_msgSend + 16
MyApp SimplePing.m line 313 -[SimplePing sendPingWithData:]
Foundation __NSFireTimer + 88
CoreFoundation __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 28
CoreFoundation __CFRunLoopDoTimer + 856
CoreFoundation __CFRunLoopDoTimers + 244
CoreFoundation __CFRunLoopRun + 1484
CoreFoundation CFRunLoopRunSpecific + 424
GraphicsServices GSEventRunModal + 100
UIKit UIApplicationMain + 208
MyApp main.swift line 10
libdyld.dylib start + 4
I have not managed to reprodice it yet, and I know a little about this app so far. But I have to begin researching it somehow. I looked through realization of delegates (btw, they are in Swift - if it's relevant), bit did not find anything criminal so far.
As I know, EXC_BAD_ACCESS normally fires when one tries to access deallocated memory. In this concrete case it could mean that [strongDelegate simplePing:self didSendPacket:...] has somehow deallocated self. But as self is a strong reference, it just could not happen at all - am I right?
May you guys drop me some probable scenarios on how could it crash with EXC_BAD_ACCESS on that line? The only my idea is that some memory overwrite have happened.
UPDATE
I was completely wrong with my assumption "But as self is a strong reference ...". self in nether strong, nor weak. It is just unsafe_unretained, as a smart guy explained: https://stackoverflow.com/a/18011581/674548.

EXC_BAD_ACCESS when object released mid-function

I have a class with a property
#property (nonatomic, copy) MyObject *currentObject;
and a function that looks like this:
- (void)handleExternalChange {
#synchronized (self) {
MyObject *newObject = [self.externalStore getObject];
//Business logic...
self.currentObject = newObject;
}
}
I am seeing a crash objc_msgSend() selector name: copyWithZone: occasionally when i hit the self.currentObject = newObject line.
I presume this is because the externalStore released the object returned by getObject on another thread and by the time it got down to the setter it was gone.
Does this conclusion seem right? If so is there a recommended way to fix this?
UPDATE:
Here's some of the stack trace
Application Specific Information:
objc_msgSend() selector name: copyWithZone:
0 libobjc.A.dylib 0x000000018149ef30 objc_msgSend + 16
1 libobjc.A.dylib 0x000000018149c2d4 objc_setProperty_nonatomic_copy + 44
2 MyApp 0x0000000100115cb4 -[MyClass handleExternalChange] (MyClass.m:117)
3 CoreFoundation 0x00000001829ee22c __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 16
4 CoreFoundation 0x00000001829ed930 _CFXRegistrationPost + 396
5 CoreFoundation 0x00000001829ed6ac ___CFXNotificationPost_block_invoke + 56
6 CoreFoundation 0x0000000182a5cb9c -[_CFXNotificationRegistrar find:object:observer:enumerator:] + 1500
7 CoreFoundation 0x000000018292fbf4 _CFXNotificationPost + 372
8 Foundation 0x00000001834366bc -[NSNotificationCenter postNotificationName:object:userInfo:] + 64
MyObject *newObject = [self.externalStore getObject];
That method shouldn't be named get* anything, but that isn't likely to be the cause of the issue. Nor is it likely to be a threading issue (at least not directly).
This sounds more like there is some state related to the copyWithZone: that isn't handled correctly. How is the copy method implemented? Specifically, is it correctly copying all state and bumping reference counts or does it try to cheat by doing some kind of a byte copy?

iOS app sometimes crashes when dereferencing weak reference

I have a very simple class:
#interface WORef : NSObject
#property(nonatomic,weak) NSObject * object;
#end
Instances are stored in an NSArray and from time to time (only on the main thread) this array is iterated and I access the "object" property.
All works fine when testing or debugging, but in the production version of my app on the store I sometimes get crash reports when dereferencing the "object" property (the stack trace actually shows the line number of the property definition).
Here is an example of such a call stack:
Thread : Crashed: com.apple.main-thread
0 libsystem_platform.dylib 0x35180518 _os_lock_recursive_abort + 18446744073709552000
1 libsystem_platform.dylib 0x3518050f _os_lock_handoff_lock_slow + 90
2 libobjc.A.dylib 0x34adac3f objc_object::sidetable_release_slow((anonymous namespace)::SideTable*, bool) + 22
3 libobjc.A.dylib 0x34adad2f objc_object::sidetable_release(bool) + 118
4 Foundation 0x27f56f01 -[NSOperationQueue dealloc] + 72
5 libobjc.A.dylib 0x34adad5f objc_object::sidetable_release(bool) + 166
6 libobjc.A.dylib 0x34ac14d9 _class_initialize + 536
7 libobjc.A.dylib 0x34ac705f lookUpImpOrForward + 254
8 libobjc.A.dylib 0x34ac6f1b lookUpImpOrNil + 26
9 libobjc.A.dylib 0x34abfab3 class_getMethodImplementation + 34
10 libobjc.A.dylib 0x34ada583 weak_read_no_lock + 58
11 libobjc.A.dylib 0x34ada871 objc_loadWeakRetained + 92
12 MyApp 0x000c5983 -[WORef object] (WeakRef.m:12)
Anyone know what might be causing this?
// consider this method
-(void)populateArray {
WORef *ref = [WORef new];
ref.object = [YourObject new];
return #[ref];
// once you get out of this scope, the memory space occupied by [YourObject new]
// can be re-allocate to something else and your ref.object would still point at
// that memory space. You would get random crashes depending on when this memory space get re-allocated.
// The reason being is that ref.object hold a weak reference to [YourObject new]
// so it reference count doesn't get incremented.
// Change your property declaration to (nonatomic,strong) instead
}

Very strange crash, may related to ARC and appears on iPhone 5s only

Thread : Crashed: com.apple.main-thread
0 libobjc.A.dylib 0x000000019a5639d0 objc_msgSend + 16
1 UIKit 0x00000001915af1d0 -[UISearchDisplayController _cleanUpSearchBar] + 196
2 UIKit 0x00000001915af0a0 -[UISearchBar willMoveToSuperview:] + 68
3 UIKit 0x0000000191788ea4 __UIViewWillBeRemovedFromSuperview + 192
4 UIKit 0x00000001914b83f4 -[UIView(Hierarchy) removeFromSuperview] + 72
5 UIKit 0x00000001914bb4fc -[UIView dealloc] + 424
6 UIKit 0x000000019159f354 -[UIScrollView dealloc] + 972
7 UIKit 0x000000019165fc40 -[UITableView dealloc] + 1304
8 UIKit 0x000000019159ed60 -[UIScrollView removeFromSuperview] + 80
9 UIKit 0x00000001914bb4fc -[UIView dealloc] + 424
10 UIKit 0x000000019165010c -[UIViewController dealloc] + 464
11 Gogobot 0x000000010017e560 -[GBListPageViewController dealloc] (GBListPageViewController.m:288)
12 Gogobot 0x0000000100189f84 __destroy_helper_block_982 (GBListPageViewController.m)
13 libsystem_blocks.dylib 0x000000019ab7f908 _Block_release + 256
14 libsystem_blocks.dylib 0x000000019ab7f908 _Block_release + 256
15 libsystem_blocks.dylib 0x000000019ab7f908 _Block_release + 256
16 Foundation 0x000000018f198b78 __destroy_helper_block_165 + 28
17 libsystem_blocks.dylib 0x000000019ab7f908 _Block_release + 256
18 Foundation 0x000000018f0a138c -[NSBlockOperation dealloc] + 68
19 libdispatch.dylib 0x000000019ab383e0 _dispatch_client_callout + 16
20 libdispatch.dylib 0x000000019ab3b56c _dispatch_main_queue_callback_4CF + 344
21 CoreFoundation 0x000000018e5aad64 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12
22 CoreFoundation 0x000000018e5a90a4 __CFRunLoopRun + 1452
23 CoreFoundation 0x000000018e4e9b38 CFRunLoopRunSpecific + 452
24 GraphicsServices 0x0000000193f0f830 GSEventRunModal + 168
25 UIKit 0x00000001915280e8 UIApplicationMain + 1156
26 Gogobot 0x0000000100202c20 main (main.m:16)
27 libdyld.dylib 0x000000019ab53aa0 start + 4
Here are the crash log i got from crashlytics. From this two lines, seems like something wrong with the dealloc,
11 Gogobot 0x000000010017e560 -[GBListPageViewController dealloc] (GBListPageViewController.m:288)
12 Gogobot
but i am using ARC, and didn't even implement anything in dealloc. It never happen to me when i test, but we got the same crash report from Crashlytcis, 10-20 times a day. And some user has 30% free ram. Doesn't look like memory issue. But all the report are coming from iPhone 5s.
I have been trying to reproduce that on my iphone 5s and 5. can't recreate at all... any idea would be appreciated.
here is my dealloc in my GBListPageViewController:
- (void)dealloc
{
[[NSNotificationCenter defaultCenter]removeObserver:self name:kGBListPageVCSortSelected object:nil];
[[NSNotificationCenter defaultCenter]removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil];
}
======================================================================
New Edit:
we try to remove delegate and observers in dealloc, but seems like that didn't solve the problem. And from our crash reports, seems like all the crashes happen on iphone5s + 7.0.x OS.
here is the code we used to remove delegate, pretty simply.
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
self.searchDisplayController.searchBar.delegate = nil;
self.searchDisplayController.delegate = nil;
}
Anyone has the same issue?...
My bet would be on the view controller being released before the view, while the view still has a reference to the delegate and tries to call a delegate method. I've seen many of these in iOS 7.X.
Set your search bar's and search display controller's delegates to nil in the view controller's dealloc.
I've had this problem as well. My workaround was rather unorthodox; in the segue's destination view controller, I set a property to hold a pointer to the source view controller, then set it to nil in viewWillAppear:animated. Like I said, it's unorthodox, but it works, giving the source view controller enough time to clean up before deallocating it.
Edit:
#property (nonatomic, strong) id vcToHoldForARCDeallocBug;
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.vcToHoldForARCDeallocBug = nil;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
self.vcToHoldForARCDeallocBug = segue.destinationViewController;
}
It sets a pointer to the destination view controller when it's presented. Once the user navigates back, in viewDidAppear it clears the pointer and allows ARC to deallocate the destination view controller since everything has been cleaned up by the time viewDidAppear is called.

Can i make an Xcode breakpoint when a certain class gets added to autorelease pool?

I'm trying to debug some ARC code, and it'd be really helpful if i could find out when an object of a certain class is added to the autorelease pool (not when it is actually autoreleased down the track).
Is this possible, eg with a breakpoint? Or by overwriting the 'autorelease' method and putting a breakpoint in it? Any suggestions?
-- edit --
The problem is that i've got an infrequent crash occurring where a custom subclass of UIView is autoreleased on a background thread, which crashes because UIView's cannot be dealloc'd on a background thread. The trace looks like below:
0 libsystem_kernel.dylib __pthread_kill + 8
1 libsystem_c.dylib pthread_kill + 54
2 libsystem_c.dylib abort + 94
3 libc++abi.dylib abort_message + 46
4 libc++abi.dylib default_terminate() + 24
5 libobjc.A.dylib _objc_terminate + 146
6 libc++abi.dylib safe_handler_caller(void (*)()) + 76
7 libc++abi.dylib operator delete(void*)
8 libc++abi.dylib __cxa_throw + 122
9 libobjc.A.dylib objc_exception_throw + 94
10 CoreFoundation +[NSException raise:format:]
11 Foundation -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 90
12 MYAPP MySuperclass.m line 156 -[MySuperclass dealloc]
13 MYAPP MyClass.m line 41 -[MyClass dealloc]
14 ... libobjc.A.dylib _objc_rootRelease + 36
15 libobjc.A.dylib objc_release + 38
16 libobjc.A.dylib (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 224
17 libobjc.A.dylib _objc_autoreleasePoolPop + 12
18 CoreFoundation _CFAutoreleasePoolPop + 18
19 libdispatch.dylib _dispatch_worker_thread2 + 338
20 libsystem_c.dylib _pthread_wqthread + 294
This might not help with your problem, but I think it answers your original question:
You can add a symbolic breakpoint on [NSObject autorelease] and then set a condition to match your class. If your running on a device $r0 should hold the pointer to the receiving object. You need to do some casting to make the condition work: (BOOL)[(id)$r0 isKindOfClass:[NSArray class]] breaks whenever an NSArray is added to the autoreleasepool. Note that everything will be running very slow as the debugger has to break on every autorelease and check the condition.

Resources