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 <[email protected]>: 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 <[email protected]>: 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?
What you are applying with
-Wl,-z,relrois 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 nowlinker flag makes the loader resolve all symbols at startup, and then remap the segment containing the GOT as read-only, before starting the program.You probably forgot to set
target record-fullbefore starting debugging, but I'm not sure that has any effect since you already userecord 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 withrr.