'MacOS LLDB | Thread 1: EXC_BAD_ACCESS (code=1 & 2, address=0x123456789)

So to start, I am not too knowledageble in C++, I know very little from a class I took a while back. Almost everything I have so far is from this very generous and knowledgable community.

I have to use a wack piece of custom software for work on MacOS and it doesn't have any form autosave and it occasionally likes to crash. Though I regularly save, its annoying loosing any amount of work because it crashes.

What I am trying to do is use the save function address to save and repeat every 5 or so minutes. So far I have the process id, the runtime base memory address for the process, the offset of the save function address(I have subtracted the disassembler base address from the address I found with debugging and disassembling), and the arguments it requires. When I try to run the save function I get the lldb error Thread 1: EXC_BAD_ACCESS (code=1, address=0x10a106ecf) and sometimes it changes to Thread 1: EXC_BAD_ACCESS (code=2, address=0x10a106ecf). I have found other questions about this error on this website, but it either doesn't apply to my situation, or due to my limited knowledge I just don't understand how to solve it for my case. If anyone could explain in detail what I am doing wrong in detail along with a solution, it would be much appreciated because I would like to get an understanding and learn.

The project is a Command Line Utility in Xcode. I'm not sure if it would need to be a .dylib and added into the process, but if it could be an external command line utility, that would be more optimal for me.

Here is the problem code:

mach_vm_address_t runtimeBase = 0; // Base process memory address. Set in the getProc() function
mach_vm_address_t funcBase = 0x973ECF; // Function Address from disassembling. (0x100973ECF - 0x100000000 = 0x973ECF)

int main()
{
    if (!getProc()) throw std::logic_error("Failed to get Process.");
    
    typedef mach_vm_address_t(__cdecl* autoSave)(int saveType, const char* saveMessage);
    autoSave save = (autoSave)(runtimeBase + funcBase);
    for(;;)
    {
        save(0, "AutoSave");//    <-- Error Here: Thread 1: EXC_BAD_ACCESS (code=1/2, address=0x10a106ecf)
        //sleep(5); //add sleep after I get save to work.
    }
}

/*
 saveType:
    0 = Save local.
    1 = Save server.
 
 saveMessage: Message for history/save log.
 */

More detail I get from code=1 error is:

error: memory read failed for 0x10a106e00  <-- Thread 1: EXC_BAD_ACCESS (code=1, address=0x10a106ecf)

And for code=2 is:

0x10a106ecf: addb   %al, (%rax)            <-- Thread 1: EXC_BAD_ACCESS (code=2, address=0x10a106ecf)
0x10a106ed1: addb   %al, (%rax)
0x10a106ed3: addb   %al, (%rax)
0x10a106ed5: addb   %al, (%rax)
0x10a106ed7: addb   %al, (%rax)
0x10a106ed9: addb   %al, (%rax)
0x10a106edb: addb   %al, (%rax)
0x10a106edd: addb   %al, (%rax)
0x10a106edf: addb   %al, (%rax)
0x10a106ee1: addb   %al, (%rax)
0x10a106ee3: addb   %al, (%rax)
0x10a106ee5: addb   %al, (%rax)
0x10a106ee7: addb   %al, (%rax)
0x10a106ee9: addb   %al, (%rax)
0x10a106eeb: addb   %al, (%rax)
0x10a106eed: addb   %al, (%rax)


Solution 1:[1]

With assembly explanations from @Peter Cordes I managed to solve my issue.

@Peter Cordes: addb %al, (%rax) is how 00 00 decodes. Crashing there indicates that execution jumped to some memory that's all zeros, perhaps due to overwriting a return address or function pointer. Or if you have any hand-written asm, due to getting something wrong with the stack.

@Peter Cordes: The EXC_BAD_ACCESS (code=2, address=0x10a106ecf) is from trying to deref whatever garbage is in RAX, for read+write. The 00 00 garbage machine code decodes as instructions that try to access that "bad pointer". I haven't used LLDB much, or MacOS for development at all, but I'm guessing code=1 vs 2 might be read vs. write. (And I haven't really looked at the details of your question, just wanted to help out by explaining that seeing execution in a whole block of addb %al, (%rax) instructions means you jumped to a region full of zeros; any mem access it attempts is already bogus.)


So, as @Peter Cordes said addb %al, (%rax) is how 00 00 decodes. Crashing there indicates that execution jumped to some memory that's all zeros.

From this we can infer that when Thread 1: EXC_BAD_ACCESS (code=2, address=0x10a106ecf) happens it is because we jumped to a place in memory that is all zeros. But why are we jumping to a place in memory with all zeros? I am thinking it's because of how MacOS handles memory. Each process has its own memory space, and I was trying to jump to the address 0x10a106e00 in MY OWN process' memory, which does not exist. There are a multitude of resources explain how process memory works on modern OS's, just do a quick google search if you want to learn more.

So what we know now is:

  • Thread 1: EXC_BAD_ACCESS (code=1, address=0x10a106ecf) happens because my program fails to read the memory for the specified address.
  • Thread 1: EXC_BAD_ACCESS (code=2, address=0x10a106ecf) happens because we are jumping an invalid place in memory, and for the wrong process.

There was also some issues with the code, I was using code better suited for Windows, To create a function based on the memory address I did,

typedef mach_vm_address_t(__cdecl* autoSave)(int saveType, const char* saveMessage);
    autoSave save = (autoSave)(runtimeBase + funcBase);

And this is what I'm thinking a better/correct way of creating a function based on the memory address is:

int (*autoSave)(int saveType, const char* saveMessage) = (int (*)(int , const char *))(runtimeBase + funcBase);

The last thing I did was create a Dylib instead of a Command Line Utility. I did this because it is MUCH easier to access the target process' memory. Though what I am trying to achieve is probably possible through a Command Line Utility(CLU) and attaching to the target process as if the CLU was a debugger or some other method, for me it was much simpler and easier to just create a dylib.

If anyone reading this has any questions about my explanation, or want more detailed explanation/example of my solution. Feel free to send me a message or comment.

I once again want to thank @Peter Cordes for his explanations. They helped even though they said `I haven't used LLDB much, or MacOS for development at all'. The explanations were still extremely helpful and were explained in an easy to understand way.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1