DrReg EFLAG restoration problem.
Problem:
DrReg does not properly restore eflags when spilled in dead XAX.
Details
The sequence of the bug is as follows:
- DrReg spills eflags to XAX, where the XAX register is dead at this stage.
- App Instruction writes to XAX thus clobbering stored eflag content. Since XAX is dead, no eflag restoration occurs at this point.
- After the app instruction is executed, DrReg incorrectly restores clobbered (app) data to eflags.
I am running DynamoRIO against PHP, and as an example I am presenting the following log:
instrument_basic_block ******************
before instrumentation:
TAG 0xb733f6be
+0 L3 58 pop eax
+1 L3 8d 83 10 be ec ff lea eax, [ebx-0x001341f0]
+7 L3 5a pop edx
+8 L3 50 push eax
+9 L3 ff b3 4c 65 00 00 push dword ptr [ebx+0x0000654c]
+15 L3 e8 7e 72 03 00 call 0xb7376950
END 0xb733f6be
after instrumentation:
TAG 0xb733f6be
+43 m4 @0x47c88f48 9f lahf
+44 m4 @0x473a78b0 0f 90 c0 seto al
_<my instrumentation code>_
+164 L3 58 pop eax
_<my instrumentation code>_
+356 m4 @0x473a5760 3c 81 cmp al, 0x81
+358 m4 @0x470abd9c 9e sahf
+359 L3 8d 83 10 be ec ff lea eax, [ebx-0x001341f0]
Notice how flags are not restored at pop because eax is dead, but are so when lea is executed because at that point eax is indeed live.
Cause
Take a look at the following code in drreg_event_bb_insert_late
:
/* After each app write, update spilled app values: */
for (reg = DR_REG_START_GPR; reg <= DR_REG_STOP_GPR; reg++) {
if (pt->reg[GPR_IDX(reg)].in_use) {
if (instr_writes_to_reg(inst, reg, DR_QUERY_INCLUDE_ALL) &&
/* Don't bother if reg is dead beyond this write */
(ops.conservative || pt->live_idx == 0 ||
drvector_get_entry(&pt->reg[GPR_IDX(reg)].live, pt->live_idx - 1) ==
REG_LIVE)) {
uint tmp_slot = MAX_SPILLS;
if (pt->aflags.xchg == reg) {
/* Bail on keeping the flags in the reg. */
drreg_move_aflags_from_reg(drcontext, bb, inst, pt, true);
continue;
}
...
The issue is that the handling of eflags occurs only if XAX is live at pt->live_idx - 1
. If XAX is dead, drreg_move_aflags_from_reg
is not called.
I'll provide a fix