Reputation: 1848
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?
Upvotes: 3
Views: 159
Reputation: 69346
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
.
Upvotes: 2
Reputation: 47169
The -z relro
option only does partial read-only (In partial RELRO
, the non-PLT part of the GOT
section (.got from readelf output) is read only but .got.plt is still writeable. Whereas in complete RELRO, the entire GOT (.got and .got.plt both) is marked as read-only.
If you want to have full readonly use
-z, now
as well.
↳ Hardening ELF binaries using Relocation
Upvotes: 1