NSURLSession causing EXC_BAD_ACCESS
Asked Answered
S

2

30

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
}
Salvatoresalvay answered 2/11, 2016 at 2:24 Comment(3)
Seeing this in the iOS 10 but not 9.3 simulator as well. Same symptoms, same backtrace, no clues.Broglie
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)Karnak
I'm glad I'm not the only one. Is there a good way to debug this?Salvatoresalvay
S
45

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.

Salvatoresalvay answered 10/11, 2016 at 21:5 Comment(10)
Can you share a link or more information about the bug report? So other can view its progress? And curious how exactly did you get in touch with an Apple Technical support? Are they responsive?Charlet
I haven't put it on openradar yet, but Apple generally gives very little feedback on progress anyway. You can contact Apple for developer technical support through developer.apple.com/support/technical. They usually replied by the next business day, so pretty responsive.Salvatoresalvay
I get the exact same crash on the Mac in Sierra; same symptoms, (EXC_BAD_ACCESS deep in the NSURLConnectionLoader code, but only from Xcode debug sessions); any of guard Malloc, address sanitizer, etc etc all fix it. The same dylib is used on the MacKenrick
@user1479585 Have you received any update on this bug from Apple? I seem to have this exact same problem with Xcode 8.2.Planchet
No update. When speaking to Apple Dev support they said that I should not be creating new NSURLSession objects very often, they are meant to be long living objects. So I implemented a multiplexer so that multiple delegates can run within the same NSURLSession and this reduced the amount of times the error occurred by a lotSalvatoresalvay
thanks a million for posting this QA, @user1479585. it's a real pain.Skirr
say @Kenrick - is there a chance you could expand on what you mean by an address sanitizer in this context? So, looking at the final block of code posted by 147 ("To reproduce error, run this:") .. how do you mean? Thanks in advance ...............Skirr
I've been having this same issue on 10.11.6 using Xcode 8.2.1 for several months now. I just updated to 10.12.4 and Xcode 8.3, but the crash in libBacktraceRecording.dylib still happens. Anyone heard any good news about a fix from Apple?Comely
Thank you so much. Mine wasn't URL related but to do with dispatch_async's and I've been pulling my hair out over a week!Yirinec
@oliveroneil We've actually been getting production crashes (a different crash) due to creating too many sessions, and the fix was also to use one session object: forums.developer.apple.com/thread/76987 I wish they'd update their docs with this tidbit.Karnak
B
2

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.)

Berryberryhill answered 2/11, 2016 at 11:47 Comment(3)
I was unable to recreate the error within the Zombie Instrument. However turning on "Zombie Objects" from "Edit Scheme" did cause the same error without outputting any zombie object warnings. Is there a difference between these two?Salvatoresalvay
I'm seeing a similar crash and I did have zombies on when it happened, but it didn't catch anything.Karnak
I can confirm that this still happens as of Xcode 9, beta 5 on Sierra 10.12.5.Mahau

© 2022 - 2024 — McMap. All rights reserved.