1
0
mirror of https://github.com/angr/pyvex synced 2026-06-21 13:47:01 +00:00
Files
angr-pyvex/tests/test_data_refs_64bit_target.py
Brian Caswell 6ef0032cf4 pyvex_c: widen HashHW value slot to ULong (fix 64-bit guest-pointer truncation on 32-bit hosts) (#541)
* pyvex_c: widen HashHW value slot to ULong

HashHW (lifted from libvex/ir_opt.c) caches (IR-temp -> resolved
constant) bindings during data_ref extraction. Values were typed
HWord (= unsigned long, host-pointer-width). On 32-bit hosts that's
4 bytes, but the guest can be 64-bit (AMD64, AArch64), so cached
guest pointers were silently truncated.

Symptom on wasm32-emscripten with an AMD64 guest: pyvex.lift returns
data_addr=0x6320 for a load whose actual target is 0x100006320 (high
32 bits dropped). Native x86-64 SysV hides the bug because long is
64-bit there.

Fix: store HashHW values as ULong unconditionally. Keys are
pyvex-internal IR-temp identifiers and can stay HWord.

Adds tests/test_data_refs_64bit_target.py to lock in the expected
value.

Fixes #539

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* tests: drop archinfo dep, add class docstring

archinfo isn't a pyvex test dependency, so the new test failed to
collect on all CI runners. Use the bundled pyvex.ARCH_AMD64 constant
instead. Also add the class docstring that the ecosystem lint flagged.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Brian Caswell <brian@demoray.net>
2026-05-18 20:03:44 -07:00

52 lines
1.8 KiB
Python

"""Regression test for HashHW value-slot truncation on 32-bit hosts.
See pyvex_c/analysis.c::HashHW. Before the fix, the cached register
value flowed through a `HWord` (= `unsigned long`, 32-bit on
wasm32-emscripten and other ILP32 hosts), silently dropping the high
32 bits of any guest pointer. The PoC below uses an AMD64 guest LEA +
memory load to land a 64-bit pointer in the HashHW.
https://github.com/angr/pyvex/issues/539
"""
import unittest
import pyvex
class TestDataRefs64BitTarget(unittest.TestCase):
"""Regression tests for HashHW value-slot width on 32-bit hosts."""
def test_64bit_guest_pointer_survives_hashhw_roundtrip(self):
# 5 AMD64 instructions, 25 bytes. The mov at +0xe loads from
# rdi - 4 where rdi was set by the previous lea to 0x100006324,
# so the load target should be 0x100006320.
blob = bytes.fromhex(
"488d3d39b0feff" # lea rdi, [rip - 0x14fc7]
"bd14000000" # mov ebp, 0x14
"488d542420" # lea rdx, [rsp + 0x20]
"8b4ffc" # mov ecx, dword ptr [rdi - 4]
"e873300100" # call rel32
)
irsb = pyvex.lift(
blob,
0x10001B2E4,
pyvex.ARCH_AMD64,
max_inst=5,
collect_data_refs=True,
opt_level=1,
cross_insn_opt=False,
)
sized_refs = [r for r in (irsb.data_refs or []) if r.ins_addr == 0x10001B2F5 and r.data_size == 4]
assert sized_refs, "expected one size=4 ref from `mov ecx, [rdi - 4]`"
self.assertEqual(
sized_refs[0].data_addr,
0x100006320,
f"data_addr=0x{sized_refs[0].data_addr:x}; "
"high 32 bits dropped — see pyvex_c/analysis.c HashHW value width",
)
if __name__ == "__main__":
unittest.main()