Search code examples
iosobjective-cxcodeexc-bad-access

EXC_BAD_ACCESS but NSZombies never triggered, how to debug?


We have some very random bugs happening and throwing EXC_BAD_ACCESS, or malloc_error_break, or abort. There is no consistency to the exceptions, and enabling NSZombies doesn't cause any zombies to be triggered ever.

In fact, running with zombies enabled causes the crashes never to occur. I believe there is a subtle memory error in this codebase, and after spending many hours cleaning up what could be minor issues, we still have not solved the issue.

It make sense that a bad pointer may be overwriting a piece of memory, which is then later dereferenced and crashes the app. But what are other ways to isolate the underlying issue?

We have used all the diagnostic memory tools which will run with the device attached as well (this application uses a peripheral, so cannot be fully debugged in the simulator).


Solution

  • NSZombie is just a mechanism to poison space used by objects instead of freeing them, similar to Address Sanitizer's Memory Poisoning. By using various instrumentation such as NSZombie or the above mentioned ASAN, your stack and heap allocation will be laid out differently, leading to undefined behavior in a condition where a crash would likely be the best case scenario.

    EXC_BAD_ACCESS means you tried to access an invalid address or tried to read or write to a memory region you do not have such permissions for. The inconsistencies you're running into are likely consequences of a nasty stack or heap corruption, like sometimes overwriting a variable that just holds data and sometimes overwriting a pointer being used by the program.

    Data layout matters a lot for what happens and heap layout is often randomized in non-debug builds which adds even more room for inconsistent crashes. In addition any changes to program source code or build settings may/will inevitably cause data layout changes.

    I would recommend:

    1. Build in debug mode (-g compiler flag) and run with debugger attached. When you get a crash, gdb or lldb (the latter being the default for Xcode tooling) will stop execution and let you do things, from there get a stack trace using bt and that may let you work out the deeper cause of the problem.
    2. Use ASAN, this page explains about its usage within Xcode tooling. It's generally an excellent tool for dealing with memory issues. Beware that using it with shared libraries built without support for it may cause anomalies but it usually tells you of them and generally tries to hold your hand as much as it can.
    3. "printf debugging" can help, something like #define TRACE printf("%s %s:%d\n", __func__, __FILE__, __LINE__); and scattering these across the likely problem point can actually be helpful.

    In general, I would suggest using a debugger first, without NSZombie or anything, just do a bunch of runs to a point of a crash and get stack traces, register states etc. Having those samples across multiple crashes can help you narrow down the problem.