CFGFast: Delete the function that starts inside an instruction, not another one

drop_bad_functions() collects the address of every function that starts in the
middle of an instruction, then deletes `func_addr` instead of `node_addr`.
`func_addr` is whatever the loop above left behind, so the function that starts
inside an instruction stays in the function manager with its node removed, and an
unrelated function is deleted in its place. FunctionManager.__delitem__ checks
membership before deleting, so the second and later iterations do nothing and the
mistake is silent.

On x86_64/windows/50e5f670... the scan lands at 0x4249f4, inside the instruction
at 0x4249f1, and 0x424cc0 goes instead.
This commit is contained in:
Yan 2026-08-17 12:38:11 +00:00
parent 503b1be066
commit 478ae3c96c
2 changed files with 17 additions and 1 deletions

View file

@ -4775,7 +4775,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int, object], CFGBase):
if cfg_node is not None:
self.model.remove_node_and_graph_node(cfg_node)
if self.kb.functions.contains_addr(node_addr):
del self.kb.functions[func_addr]
del self.kb.functions[node_addr]
def _analyze_all_function_features(self, all_funcs_completed=False):
"""

View file

@ -1031,6 +1031,22 @@ class TestCfgfast(unittest.TestCase):
assert len(cfg.kb.functions) < 150, f"32 KB of random data produced {len(cfg.kb.functions)} functions"
def test_function_starting_inside_an_instruction_is_the_one_dropped(self):
# 0x4249f4 is where the prologue scan landed inside the `mov dword ptr [esp + 0x50], edx` at 0x4249f1,
# so drop_bad_functions() collects it. The deletion then ran on the wrong address and took 0x424cc0,
# an ordinary `push edi; call ...` entry, with it.
path = os.path.join(
test_location,
"x86_64",
"windows",
"50e5f670700243535f8ff558831dbbc314b215092f523355aa7a1c26205ece37",
)
proj = angr.Project(path, auto_load_libs=False)
cfg = proj.analyses.CFGFast(normalize=True)
assert 0x4249F4 not in cfg.kb.functions
assert 0x424CC0 in cfg.kb.functions
if __name__ == "__main__":
unittest.main()