FunctionParser: convert previous_names protobuf field to a plain list. (#6584)

This commit is contained in:
Fish 2026-07-07 14:21:32 -07:00 committed by GitHub
parent 18d7fff834
commit 79ec25dc24
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 66 additions and 1 deletions

View file

@ -216,7 +216,7 @@ class FunctionParser:
obj.info = json.loads(cmsg.info.decode("utf-8")) if cmsg.info else {}
obj.is_default_name = cmsg.is_default_name
obj.ran_cca = cmsg.ran_cca
obj.previous_names = cmsg.previous_names
obj.previous_names = list(cmsg.previous_names)
# signature matched?
if cmsg.matched_from == function_pb2.Function.UNMATCHED:

View file

@ -0,0 +1,65 @@
#!/usr/bin/env python3
# pylint: disable=missing-class-docstring
"""Regression test: parse_from_cmsg assigned the protobuf RepeatedScalarContainer
directly to Function.previous_names instead of converting it to a plain list.
This breaks pickling of Function objects.
"""
from __future__ import annotations
import pickle
import tempfile
import unittest
import angr
from angr.codenode import BlockNode
from angr.knowledge_plugins.functions.function import Function
class TestFunctionParserPreviousNames(unittest.TestCase):
def test_parsed_previous_names_is_plain_list_and_picklable(self):
blob = bytes.fromhex("c3") # ret
addr = 0x400000
with tempfile.NamedTemporaryFile(suffix=".bin", delete=False) as f:
f.write(blob)
blob_path = f.name
proj = angr.Project(
blob_path,
main_opts={
"backend": "blob",
"base_addr": addr,
"arch": "AMD64",
"entry_point": addr,
},
auto_load_libs=False,
)
fm = proj.kb.functions
func = fm.function(addr=addr, create=True)
assert func is not None
func._register_node(True, BlockNode(addr, 1, bytestr=blob))
func.name = "original_name"
func.name = "renamed" # records "original_name" in previous_names
self.assertIn("original_name", func.previous_names)
expected = list(func.previous_names)
cmsg = func.serialize_to_cmessage()
loaded = Function.parse_from_cmessage(cmsg, function_manager=fm, project=proj)
# previous_names must be a plain list, not a live protobuf container
self.assertIs(type(loaded.previous_names), list)
self.assertEqual(loaded.previous_names, expected)
# mutating the parsed function must not alias back into the cmsg
loaded.previous_names.append("another_name")
self.assertEqual(list(cmsg.previous_names), expected)
# the parsed function must round-trip through pickle
unpickled = pickle.loads(pickle.dumps(loaded))
self.assertEqual(unpickled.previous_names, [*expected, "another_name"])
if __name__ == "__main__":
unittest.main()