angr/tests/analyses/decompiler/test_ssa_stack_phi.py

45 lines
2.4 KiB
Python

#!/usr/bin/env python3
# pylint: disable=missing-class-docstring,no-self-use,no-member
from __future__ import annotations
__package__ = __package__ or "tests.analyses.decompiler" # pylint:disable=redefined-builtin
import unittest
import angr
class TestSSAStackPhiSizeConflict(unittest.TestCase):
"""
A stack slot may reach a join point with definitions of different sizes, e.g., a 4-byte definition on one path and
a 1-byte partial write on another path. The narrow definition is fully contained within the wide variable, so a phi
node for the full-width variable must still be generated; otherwise the variable is dropped from the SSA state and
a later wide use has no reaching definition, which raises a KeyError during SSA rewriting under fail_fast.
The blob below is the machine code of a single 32-bit function (from a real binary) that exhibits this pattern:
stack slot [ebp-0x1c] is written 4 bytes near the top, only 1 byte on a later path, and read 4 bytes after the join.
"""
# 32-bit function that writes [ebp-0x1c] as 4 bytes and (on another path) as 1 byte, then reads it as 4 bytes.
FUNC_BYTES = bytes.fromhex(
"558bec83ec40a100f1490033c58945fc53568b750833db8975c48b41045785c0741a8b481885c975038d481c518d4dcc"
"e8f4b4fdff8bfb33db43eb1c33c08d7de4ab6a02ababab8d45e4895df4c745f80f000000885de45f508bcee87db4fdff"
"85ff0f848d0000008b4df883f90f7676803d9056490000743c8b55e48b7df44703fa8d420103c18945c88d420803c183"
"e0f88bd08bc83bf876028bf83955c877038b4dc8515750ff75e4e88ac905008b4df883c4108d41018b4de48945c8894d"
"c43d0010000072158d45c8508d45c450e80b86fdff8b45c859598b4dc45051e87855000059598365f400c745f80f0000"
"00c645e40085db74088d4dcce85fc2fdff8b4dfc8bc65f5e33cd5be87c500000c9c20400"
)
BASE = 0x428F21
def test_stack_slot_conflicting_def_sizes_at_join(self):
proj = angr.load_shellcode(self.FUNC_BYTES, "x86", load_address=self.BASE, start_offset=0)
cfg = proj.analyses.CFGFast(normalize=True, function_starts=[self.BASE])
proj.analyses.CompleteCallingConventions()
func = cfg.functions[self.BASE]
# fail_fast=True: this raised KeyError before the phi-placement fix.
dec = proj.analyses.Decompiler(func, cfg=cfg, fail_fast=True)
assert dec.codegen is not None and dec.codegen.text is not None
if __name__ == "__main__":
unittest.main()