Search code examples
cgdbgotreverse-debugging

Are changes made to the GOT expected to be reversed during reverse debugging?


Is it expected that changes made to a program's address space would not be reverted during reverse debugging?

I have been debugging a program which segfaults when a pointer to strlen in the GOT becomes corrupted during the course of execution. Thanks to advice stemming from comments to this question, I made the GOT of this program read-only by linking with the -z relro option; however, this does not prevent the pointer in question from being overwritten. Namely, I can start the program in gdb, step to the first occurrence of strlen, verify that the pointer is valid (for example: x/g 0x5555555d10a8 ==> 0x5555555d10a8 <strlen@got.plt>: 0x00007ffff7e8d1e0), continue running, and wait for the pointer to become invalid (pointing to a nonsensical address outside the address space of the program; for example: x/g 0x5555555d10a8 ==> 0x5555555d10a8 <strlen@got.plt>: 0x0000000000002156), prompting a segv.

However, if I record full the entire execution (from the first line until the program segfaults), and then awatch the address with the pointer to strlen during reverse-continue, the watchpoint never triggers. And when the program has finally gotten back to instruction #0, the pointer is still pointing to the invalid address that it had when it segfaulted.

This leads to two questions. First, why is the GOT mutable despite the -z relro linker option? Second, is it expected for a location in memory (the pointer to strlen) that is altered during program execution to not be restored to its original value during reverse execution?


Solution

  • First, why is the GOT mutable despite the -z relro linker option?

    What you are applying with -Wl,-z,relro is only partial RELRO protection. This option has the main purpose of moving the GOT before the BSS to avoid corruption of entries due to overflows from buffers in the BSS, and also makes a small portion of the GOT read-only. What you want is full RELRO, which is obtained with -Wl,-z,relro,-z,now. The -z now linker flag makes the loader resolve all symbols at startup, and then remap the segment containing the GOT as read-only, before starting the program.

    Second, is it expected for a location in memory (the pointer to strlen) that is altered during program execution to not be restored to its original value during reverse execution?

    You probably forgot to set target record-full before starting debugging, but I'm not sure that has any effect since you already use record full. Reverse execution is not easy, the debugger might decide not to reverse the memory modifications of the GOT (or other sections) for performance reasons (since those should normally not be touched anyway). GDB's record feature also has some other limitations, like not supporting AVX instructions for example. I don't know much more to give a better answer. You can try your luck with rr.