Underflow with NSURLProtocol in GC environment

Originator:charles.parnot
Number:rdar://8087384 Date Originated:06/12/2010
Status:Duplicate Resolved:
Product:Mac OS X Product Version:10.6
Classification:Crash Reproducible:Always
 
### Summary

In a garbage-collected environment, the use of a costum NSURLProtocol subclass triggers an underflow error, which can also lead to a crash when the overreleased object is accessed after it was finalized. This problem does not occur in a non-GC environment.


### Steps to reproduce

* Start with Apple's example SpecialPictureProtocol, that can be downloaded from Apple's web site, at the following address:

		http://developer.apple.com/mac/library/samplecode/SpecialPictureProtocol/Introduction/Intro.html

* In the build settings, change the target OS to 10.6 so that it builds with Xcode 3.2

* In the build settings, turn on garbage-collection to `required`

* Build and run, type various strings and here is what happens:

	* In the console, messages appear as follows:
	
			malloc: reference count underflow for 0xXXXXXXXXX,
			break on auto_refcount_underflow_error to debug.`
		
		with the address corresponding to the underflowed/overreleased object
	
	* Eventually, it will crash, either with an `EXC_BAD_ACCESS` or an exception pointing to a method call not implemented by the target, both a sign of memory corruption and/or accessing an object that does not exist anymore.


### Diagnostics

* The bug has been reported also on these web pages and mailing lists:
	* http://stackoverflow.com/questions/1112869/how-to-avoid-reference-count-underflow-in-nscfurlprotocolbridge-in-custom-nsurlp
	* http://lists.apple.com/archives/cocoa-dev/2008/May/msg01272.html
	* http://www.cocoabuilder.com/archive/cocoa/195056-options-for-working-around-bug.html#195056

* Upon further inspection of the address of the underflow, it appears this address corresponds to the `client` opaque object passed to the method `-initWithRequest:cachedResponse:client:` when the costum NSURLProtocol instance is init-ed.

* We had the chance to discuss this bug directly to an Apple engineer at the WebKit lab at WWDC. The engineer identified a bug in OS X code, consistent with the above description and observations. It appears the client object is passed to some other internal classes using a `void *` argument which is then not properly handled by the garbage collector. Depending on the timing, the client is then randomly finalized at some future point, which may lead to the underflow message and/or a crash.

### Workaround 1

* The first workaround is to simply compile and run under non-GC environment, but that's not an option for our actual application, of course.


### Workaround 2 under GC

* With the Apple engineer contacted at WWDC, we devised  a simple workaround to avoid the underflow and the potential crash. It is found in the attached project, which is identical to the original Apple sample project SpecialPictureProtocol except for overriding the `-initWithRequest:cachedResponse:client:` in the NSURLProtocol subclass. In this method, we can retain the client, which is a Core Foundation object, as follows:

		- (id)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id <NSURLProtocolClient>)client
		{
			// under GC, and unless we retaint the client, we observe the following issues;
			//  - error messages in the console 'malloc: reference count underflow for XXX, break on auto_refcount_underflow_error to debug.', where XXX is the address of the cient
			//  - crashes on EXC_BAD_ACCESS or exceptions on invocations that could not be done on an object, in both cases indicative of overreleased obejct. The app does not always crash, one needs to hammer it quite hard sometimes
	
			// workaround to avoid underflow and crashes
			// BUT THE OBJECT WILL LEAK!!
			//CFRetain(client);
	
			return [super initWithRequest:request cachedResponse:cachedResponse client:client];
		}

* A problem remains: we could not find yet a good place to release the client, which means the above workaround results in leaking the `client` object, and in turn, due to the object graph, also retain the NSURLProtocol instance. The retain appears in particular to create a retain cycle between the client object and the NSURLProtocol instance.

Comments

Duplicate

Marked as duplicate, original Bug ID# 8070298.

By charles.parnot at June 15, 2010, 10:52 p.m. (reply...)

Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!