Repository: angr/angr

Issue:
How to get CFGBase.normalized if CFGFast instance and CFGEmulated instance not available?
### Question

When loading pre-stored project file from disk, cfgs can be accessed through `p.kb.cfgs` which result in `CFGModel` instance, and CFGFast or CFGEmulated instance is no longer available (except performing analyses again, which is not what I want.). But the `normalized` attribute is only available in CFGBase. 

How can I check if cfg is normalized or not through CFGModel? I checked source code and could not find any solution.



Discussion:
Maybe through checking `normalized` attribute of all functions?
I actually ran into this issue a few day ago. I asked fish about it and got the response that it was an oversight in the refactor that brought us CFGModel and that we should try to move the normalized attribute to CFGModel.

Resolving patch:
diff --git a/angr/analyses/cfg/cfg_base.py b/angr/analyses/cfg/cfg_base.py
--- a/angr/analyses/cfg/cfg_base.py
+++ b/angr/analyses/cfg/cfg_base.py
@@ -99,8 +99,6 @@ def __init__(self, sort, context_sensitivity_level, normalize=False, binary=None
         self._updated_nonreturning_functions = None
 
         self._normalize = normalize
-        # Flag, whether the CFG has been normalized or not
-        self._normalized = False
 
         # Flag, whether to track memory writes in stack pointer tracking
         self._sp_tracking_track_memory = sp_tracking_track_memory
@@ -191,17 +189,20 @@ def _nodes_by_addr(self):
         return self._model._nodes_by_addr
 
     @property
-    def model(self):
+    def model(self) -> CFGModel:
         """
         Get the CFGModel instance.
         :return:    The CFGModel instance that this analysis currently uses.
-        :rtype:     CFGModel
         """
         return self._model
 
     @property
     def normalized(self):
-        return self._normalized
+        return self._model.normalized
+
+    @normalized.setter
+    def normalized(self, v):
+        self._model.normalized = v
 
     @property
     def context_sensitivity_level(self):
@@ -235,7 +236,7 @@ def _post_analysis(self):
 
         if self._normalize:
 
-            if not self._normalized:
+            if not self.normalized:
                 self.normalize()
 
             # Call normalize() on each function
@@ -1101,7 +1102,7 @@ def normalize(self):
                             smallest_nodes.pop(nodekey_a, None)
                             smallest_nodes.pop(nodekey_b, None)
 
-        self._normalized = True
+        self.normalized = True
 
     def _normalize_core(self, graph: networkx.DiGraph,
                         callstack_key,
diff --git a/angr/knowledge_plugins/cfg/cfg_model.py b/angr/knowledge_plugins/cfg/cfg_model.py
--- a/angr/knowledge_plugins/cfg/cfg_model.py
+++ b/angr/knowledge_plugins/cfg/cfg_model.py
@@ -27,7 +27,7 @@ class CFGModel(Serializable):
     """
 
     __slots__ = ('ident', 'graph', 'jump_tables', 'memory_data', 'insn_addr_to_memory_data', '_nodes_by_addr',
-                 '_nodes', '_cfg_manager', '_iropt_level', '_node_addrs', 'is_arm')
+                 '_nodes', '_cfg_manager', '_iropt_level', '_node_addrs', 'is_arm', 'normalized')
 
     def __init__(self, ident, cfg_manager=None, is_arm=False):
 
@@ -57,6 +57,8 @@ def __init__(self, ident, cfg_manager=None, is_arm=False):
         # addresses of CFGNodes to speed up get_any_node(..., anyaddr=True). Don't serialize
         self._node_addrs: List[int] = []
 
+        self.normalized = False
+
     #
     # Properties
     #
@@ -128,6 +130,8 @@ def serialize_to_cmessage(self):
             memory_data.append(data.serialize_to_cmessage())
         cmsg.memory_data.extend(memory_data)
 
+        cmsg.normalized = self.normalized
+
         return cmsg
 
     @classmethod
@@ -172,6 +176,8 @@ def parse_from_cmessage(cls, cmsg, cfg_manager=None, loader=None):  # pylint:dis
                 md.fill_content(loader)
             model.memory_data[md.addr] = md
 
+        model.normalized = cmsg.normalized
+
         return model
 
     #
diff --git a/angr/protos/cfg_pb2.py b/angr/protos/cfg_pb2.py
--- a/angr/protos/cfg_pb2.py
+++ b/angr/protos/cfg_pb2.py
@@ -2,10 +2,9 @@
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # source: protos/cfg.proto
 """Generated protocol buffer code."""
+from google.protobuf.internal import builder as _builder
 from google.protobuf import descriptor as _descriptor
 from google.protobuf import descriptor_pool as _descriptor_pool
-from google.protobuf import message as _message
-from google.protobuf import reflection as _reflection
 from google.protobuf import symbol_database as _symbol_database
 # @@protoc_insertion_point(imports)
 
@@ -15,44 +14,19 @@
 from . import primitives_pb2 as protos_dot_primitives__pb2
 
 
-DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10protos/cfg.proto\x12\x0b\x61ngr.protos\x1a\x17protos/primitives.proto\"H\n\x07\x43\x46GNode\x12\n\n\x02\x65\x61\x18\x01 \x01(\x04\x12\x0c\n\x04size\x18\x02 \x01(\r\x12\x10\n\x08\x62lock_id\x18\x03 \x03(\x03\x12\x11\n\treturning\x18\x04 \x01(\x08\"\x89\x01\n\x03\x43\x46G\x12\r\n\x05ident\x18\x01 \x01(\t\x12#\n\x05nodes\x18\x02 \x03(\x0b\x32\x14.angr.protos.CFGNode\x12 \n\x05\x65\x64ges\x18\x03 \x03(\x0b\x32\x11.angr.protos.Edge\x12,\n\x0bmemory_data\x18\x04 \x03(\x0b\x32\x17.angr.protos.MemoryData\"\xae\x02\n\nMemoryData\x12\n\n\x02\x65\x61\x18\x01 \x01(\x04\x12\x0c\n\x04size\x18\x02 \x01(\r\x12\x34\n\x04type\x18\x03 \x01(\x0e\x32&.angr.protos.MemoryData.MemoryDataType\"\xcf\x01\n\x0eMemoryDataType\x12\x13\n\x0fUnknownDataType\x10\x00\x12\x0f\n\x0bUnspecified\x10\x01\x12\x0b\n\x07Integer\x10\x02\x12\x10\n\x0cPointerArray\x10\x03\x12\n\n\x06String\x10\x04\x12\x11\n\rUnicodeString\x10\x05\x12\x13\n\x0fSegmentBoundary\x10\x06\x12\x11\n\rCodeReference\x10\x07\x12\x0f\n\x0bGOTPLTEntry\x10\x08\x12\r\n\tELFHeader\x10\t\x12\x11\n\rFloatingPoint\x10\nb\x06proto3')
-
-
-
-_CFGNODE = DESCRIPTOR.message_types_by_name['CFGNode']
-_CFG = DESCRIPTOR.message_types_by_name['CFG']
-_MEMORYDATA = DESCRIPTOR.message_types_by_name['MemoryData']
-_MEMORYDATA_MEMORYDATATYPE = _MEMORYDATA.enum_types_by_name['MemoryDataType']
-CFGNode = _reflection.GeneratedProtocolMessageType('CFGNode', (_message.Message,), {
-  'DESCRIPTOR' : _CFGNODE,
-  '__module__' : 'protos.cfg_pb2'
-  # @@protoc_insertion_point(class_scope:angr.protos.CFGNode)
-  })
-_sym_db.RegisterMessage(CFGNode)
-
-CFG = _reflection.GeneratedProtocolMessageType('CFG', (_message.Message,), {
-  'DESCRIPTOR' : _CFG,
-  '__module__' : 'protos.cfg_pb2'
-  # @@protoc_insertion_point(class_scope:angr.protos.CFG)
-  })
-_sym_db.RegisterMessage(CFG)
-
-MemoryData = _reflection.GeneratedProtocolMessageType('MemoryData', (_message.Message,), {
-  'DESCRIPTOR' : _MEMORYDATA,
-  '__module__' : 'protos.cfg_pb2'
-  # @@protoc_insertion_point(class_scope:angr.protos.MemoryData)
-  })
-_sym_db.RegisterMessage(MemoryData)
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10protos/cfg.proto\x12\x0b\x61ngr.protos\x1a\x17protos/primitives.proto\"H\n\x07\x43\x46GNode\x12\n\n\x02\x65\x61\x18\x01 \x01(\x04\x12\x0c\n\x04size\x18\x02 \x01(\r\x12\x10\n\x08\x62lock_id\x18\x03 \x03(\x03\x12\x11\n\treturning\x18\x04 \x01(\x08\"\x9d\x01\n\x03\x43\x46G\x12\r\n\x05ident\x18\x01 \x01(\t\x12#\n\x05nodes\x18\x02 \x03(\x0b\x32\x14.angr.protos.CFGNode\x12 \n\x05\x65\x64ges\x18\x03 \x03(\x0b\x32\x11.angr.protos.Edge\x12,\n\x0bmemory_data\x18\x04 \x03(\x0b\x32\x17.angr.protos.MemoryData\x12\x12\n\nnormalized\x18\x05 \x01(\x08\"\xae\x02\n\nMemoryData\x12\n\n\x02\x65\x61\x18\x01 \x01(\x04\x12\x0c\n\x04size\x18\x02 \x01(\r\x12\x34\n\x04type\x18\x03 \x01(\x0e\x32&.angr.protos.MemoryData.MemoryDataType\"\xcf\x01\n\x0eMemoryDataType\x12\x13\n\x0fUnknownDataType\x10\x00\x12\x0f\n\x0bUnspecified\x10\x01\x12\x0b\n\x07Integer\x10\x02\x12\x10\n\x0cPointerArray\x10\x03\x12\n\n\x06String\x10\x04\x12\x11\n\rUnicodeString\x10\x05\x12\x13\n\x0fSegmentBoundary\x10\x06\x12\x11\n\rCodeReference\x10\x07\x12\x0f\n\x0bGOTPLTEntry\x10\x08\x12\r\n\tELFHeader\x10\t\x12\x11\n\rFloatingPoint\x10\nb\x06proto3')
 
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'protos.cfg_pb2', globals())
 if _descriptor._USE_C_DESCRIPTORS == False:
 
   DESCRIPTOR._options = None
   _CFGNODE._serialized_start=58
   _CFGNODE._serialized_end=130
   _CFG._serialized_start=133
-  _CFG._serialized_end=270
-  _MEMORYDATA._serialized_start=273
-  _MEMORYDATA._serialized_end=575
-  _MEMORYDATA_MEMORYDATATYPE._serialized_start=368
-  _MEMORYDATA_MEMORYDATATYPE._serialized_end=575
+  _CFG._serialized_end=290
+  _MEMORYDATA._serialized_start=293
+  _MEMORYDATA._serialized_end=595
+  _MEMORYDATA_MEMORYDATATYPE._serialized_start=388
+  _MEMORYDATA_MEMORYDATATYPE._serialized_end=595
 # @@protoc_insertion_point(module_scope)


Test patch: