Skip to content

x86: preserve ZF across BT-family instructions - #88

Open
DORA-B wants to merge 2 commits into
angr:masterfrom
DORA-B:fix/x86_bt_preserve_zf
Open

DORA-B wants to merge 2 commits into
angr:masterfrom
DORA-B:fix/x86_bt_preserve_zf

Conversation

@DORA-B

@DORA-B DORA-B commented Jul 28, 2026 •

Copy link
Copy Markdown

Intel defines ZF as unaffected by BT, BTS, BTR, and BTC. The 32-bit x86 frontend currently replaces the flag state with CF alone in both register-index and immediate-index paths, incorrectly clearing a previously set ZF.

Materialize the prior flags with mk_x86g_calculate_eflags_all(), replace CF with the selected bit, and retain ZF. Both register and memory destinations use these paths.

Current master already fixes the amd64 paths, so this updated PR changes only priv/guest_x86_toIR.c.

Reproducer

import angr

# cmp eax, 1; btc edx, ecx
project = angr.load_shellcode(bytes.fromhex("83f8010fbbca"), "x86", load_address=0x1000)
state = project.factory.blank_state(addr=0x1000)
state.regs.eax = 1
state.regs.edx = 0xdeadbeef
state.regs.ecx = 0x12345678
out = project.factory.successors(state, num_inst=2).flat_successors[0]
print((out.solver.eval(out.regs.eflags) >> 6) & 1)

Before the fix this prints 0; the expected ZF is 1.

Validation

All 64 tested 32-bit x86 cases passed across the four BT-family instructions, register/immediate indices, register/memory destinations, and prior lazy ZF values. Destination values, CF, and ZF matched the reference.

Reference: Intel SDM, Volume 2, BT/BTC/BTR/BTS, Flags Affected.

Resolve integration against current upstream while retaining the instruction
semantics fix and existing source comments.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant