30
votes

I've noticed that implementing NSURLSessionDataDelegate and starting a task will very occasionally throw an EXC_BAD_ACCESS. The actual calling method that gives the error seems to vary but always comes from CFNetwork. For the most part, the calling method comes from NSURLSession delegate_dataTask:didReceiveData:completionHandler. I've attached two crash logs with different callers below. I've also attached my implementation of NSURLSessionDataDelegate.

Unfortunately I cannot reliably reproduce the error, so I don't have an example script to share. Creating and starting Downloader objects will eventually create the error. It does seem to happen more often with larger files. Have I implemented something wrong here? Is there a good way to debug from this stacktrace?

I've tested on iOS10 and 10.1.1 with the same results.

Implementation:

class Downloader: NSObject, NSURLSessionDataDelegate {
    private let url: String
    var finished = false
    let finishCondition = NSCondition()

    init(url:String) {
        self.url = url
        super.init()
    }

    func start() {
        let config = NSURLSessionConfiguration.defaultSessionConfiguration()
        let session = NSURLSession(configuration: config,
                               delegate: self,
                               delegateQueue: nil)
        guard let u = NSURL(string: url) else {
            return
        }
        let request = NSMutableURLRequest(URL: u)
        let task = session.dataTaskWithRequest(request)
        task.resume()
    }

    func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask,
                    didReceiveData data: NSData) {
    }

    func URLSession(session: NSURLSession,
                    task: NSURLSessionTask,
                    didCompleteWithError error: NSError?) {
        session.invalidateAndCancel()
    }

    func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask,
                    didReceiveResponse response: NSURLResponse,
                                       completionHandler: (NSURLSessionResponseDisposition) -> Void) {
        completionHandler(NSURLSessionResponseDisposition.Allow)
    }

    func waitForFinish() {
        finishCondition.lock()
        while !finished {
            finishCondition.wait()
        }
        finishCondition.unlock()
    }

    func URLSession(session: NSURLSession, didBecomeInvalidWithError error: NSError?) {
        finishCondition.lock()
        finished = true
        finishCondition.broadcast()
        finishCondition.unlock()
    }
}

Crash log #1:

* thread #5: tid = 0x25923, 0x0000000100042e8c libBacktraceRecording.dylib`__gcd_queue_item_enqueue_hook_block_invoke, queue = 'com.apple.NSURLSession-work', stop reason = EXC_BAD_ACCESS (code=1, address=0xf8686a68b98c6ec8)
  * frame #0: 0x0000000100042e8c libBacktraceRecording.dylib`__gcd_queue_item_enqueue_hook_block_invoke
    frame #1: 0x000000010004241c libBacktraceRecording.dylib`gcd_queue_item_enqueue_hook + 232
    frame #2: 0x000000010065dee8 libdispatch.dylib`_dispatch_introspection_queue_item_enqueue_hook + 40
    frame #3: 0x000000010063cba4 libdispatch.dylib`_dispatch_queue_push + 196
    frame #4: 0x000000018ba50500 Foundation`iop_promote_qos_outward + 112
    frame #5: 0x000000018ba4e524 Foundation`-[NSOperation setQualityOfService:] + 168
    frame #6: 0x000000018b9d7714 Foundation`-[NSOperationQueue addOperationWithBlock:] + 76
    frame #7: 0x000000018b73f82c CFNetwork`-[NSURLSession delegate_dataTask:didReceiveData:completionHandler:] + 208
    frame #8: 0x000000018b5a2c5c CFNetwork`-[__NSCFLocalSessionTask _task_onqueue_didReceiveDispatchData:completionHandler:] + 276
    frame #9: 0x000000018b5a5474 CFNetwork`-[__NSCFLocalSessionTask connection:didReceiveData:completion:] + 164
    frame #10: 0x000000018b647bf0 CFNetwork`__48-[__NSCFURLLocalSessionConnection _tick_running]_block_invoke + 120
    frame #11: 0x000000018b647b60 CFNetwork`-[__NSCFURLLocalSessionConnection _tick_running] + 344
    frame #12: 0x000000018b648c74 CFNetwork`-[__NSCFURLLocalSessionConnection _didReceiveData:] + 412
    frame #13: 0x000000018b64af8c CFNetwork`SessionConnectionLoadable::_loaderClientEvent_DidReceiveData(__CFArray const*) + 52
    frame #14: 0x000000018b6f823c CFNetwork`___ZN19URLConnectionLoader19protocolDidLoadDataEPK8__CFDatax_block_invoke_2 + 44
    frame #15: 0x000000018b64b58c CFNetwork`___ZN25SessionConnectionLoadable21withLoaderClientAsyncEU13block_pointerFvP21LoaderClientInterfaceE_block_invoke + 32
    frame #16: 0x000000010063125c libdispatch.dylib`_dispatch_call_block_and_release + 24
    frame #17: 0x000000010063121c libdispatch.dylib`_dispatch_client_callout + 16
    frame #18: 0x000000010063eb54 libdispatch.dylib`_dispatch_queue_serial_drain + 1136
    frame #19: 0x0000000100634ce4 libdispatch.dylib`_dispatch_queue_invoke + 672
    frame #20: 0x0000000100640e6c libdispatch.dylib`_dispatch_root_queue_drain + 584
    frame #21: 0x0000000100640bb8 libdispatch.dylib`_dispatch_worker_thread3 + 140
    frame #22: 0x000000018a01e2b8 libsystem_pthread.dylib`_pthread_wqthread + 1288
    frame #23: 0x000000018a01dda4 libsystem_pthread.dylib`start_wqthread + 4

Crash log #2:

* thread #12: tid = 0x2521f, 0x000000010010ae8c libBacktraceRecording.dylib`__gcd_queue_item_enqueue_hook_block_invoke, queue = 'com.apple.CFNetwork.Connection', stop reason = EXC_BAD_ACCESS (code=1, address=0xd00f524835000200)
      * frame #0: 0x000000010010ae8c libBacktraceRecording.dylib`__gcd_queue_item_enqueue_hook_block_invoke
        frame #1: 0x000000010010a41c libBacktraceRecording.dylib`gcd_queue_item_enqueue_hook + 232
        frame #2: 0x0000000100759ee8 libdispatch.dylib`_dispatch_introspection_queue_item_enqueue_hook + 40
        frame #3: 0x0000000100738ba4 libdispatch.dylib`_dispatch_queue_push + 196
        frame #4: 0x00000001975ccb3c libnetwork.dylib`nw_connection_read + 448
        frame #5: 0x00000001975d938c libnetwork.dylib`tcp_connection_read + 168
        frame #6: 0x000000018b719d54 CFNetwork`TCPIOConnection::read(unsigned long, unsigned long, void (dispatch_data_s*, CFStreamError) block_pointer) + 172
        frame #7: 0x000000018b782af4 CFNetwork`HTTPEngine::_getBodyIntelligently(void (dispatch_data_s*, CFStreamError, bool) block_pointer) + 816
        frame #8: 0x000000018b780d0c CFNetwork`HTTPEngine::_readBodyStartNextRead() + 76
        frame #9: 0x000000018b783664 CFNetwork`___ZN10HTTPEngine21_getBodyIntelligentlyEU13block_pointerFvP15dispatch_data_s13CFStreamErrorbE_block_invoke.56 + 344
        frame #10: 0x000000018b719f64 CFNetwork`___ZN15TCPIOConnection4readEmmU13block_pointerFvP15dispatch_data_s13CFStreamErrorE_block_invoke + 480
        frame #11: 0x000000010072d25c libdispatch.dylib`_dispatch_call_block_and_release + 24
        frame #12: 0x000000010072d21c libdispatch.dylib`_dispatch_client_callout + 16
        frame #13: 0x000000010073ab54 libdispatch.dylib`_dispatch_queue_serial_drain + 1136
        frame #14: 0x0000000100730ce4 libdispatch.dylib`_dispatch_queue_invoke + 672
        frame #15: 0x000000010073ce6c libdispatch.dylib`_dispatch_root_queue_drain + 584
        frame #16: 0x000000010073cbb8 libdispatch.dylib`_dispatch_worker_thread3 + 140
        frame #17: 0x000000018a01e2b8 libsystem_pthread.dylib`_pthread_wqthread + 1288
        frame #18: 0x000000018a01dda4 libsystem_pthread.dylib`start_wqthread + 4

UPDATE: I can now semi-reliably reproduce this error by running the loop pasted below in the iOS Simulator. This does not happen on iOS 9.3. If you run the code below, within a minute you should receive the error. Since it's very likely to occur in the simulator, compared with a device, I would assume it's a concurrency issue that becomes more likely with more processing power/cores. To reproduce error, run this:

var i = 0
while true {
    print("running: \(i)")
    // random url, larger files seem more likely to cause error
    let url = "http://qthttp.apple.com.edgesuite.net/1010qwoeiuryfg/3340/33409.ts"
    let c = Downloader(url: url)
    c.start()
    c.waitForFinish()
    i += 1
}
2
Seeing this in the iOS 10 but not 9.3 simulator as well. Same symptoms, same backtrace, no clues.Icydog
I'm seeing a similar crash on 10.0 simulator * thread #64: tid = 0x5e3688, 0x000000010717deac libBacktraceRecording.dylib`__gcd_queue_item_enqueue_hook_block_invoke + 4, queue = 'com.apple.network.connections', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)yuf
I'm glad I'm not the only one. Is there a good way to debug this?oliveroneill

2 Answers

45
votes

After speaking with Apple Technical Support we confirmed that it's a bug within the libBacktraceRecording.dylib library, which is used for debugging within Xcode. I have filed a bug report and have been told that it won't crash on a users device since this is a debug error that occurs within a library not present on most users' devices.

2
votes

Try running in the Zombies Instrument. My guess is that your Downloader class instance is getting deallocated while the NSURLSession is operating, so when it goes to call your didReceiveData method, the memory formerly occupied by your object contains something else. (That's what a zombie is.)