angr 9.2.138__py3-none-manylinux2014_x86_64.whl → 9.2.140__py3-none-manylinux2014_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of angr might be problematic. Click here for more details.

Files changed (100) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/calling_convention/calling_convention.py +48 -21
  3. angr/analyses/calling_convention/fact_collector.py +59 -12
  4. angr/analyses/calling_convention/utils.py +2 -2
  5. angr/analyses/cfg/cfg_base.py +13 -0
  6. angr/analyses/cfg/cfg_fast.py +23 -4
  7. angr/analyses/decompiler/ail_simplifier.py +79 -53
  8. angr/analyses/decompiler/block_simplifier.py +0 -2
  9. angr/analyses/decompiler/callsite_maker.py +80 -14
  10. angr/analyses/decompiler/clinic.py +99 -80
  11. angr/analyses/decompiler/condition_processor.py +2 -2
  12. angr/analyses/decompiler/decompiler.py +19 -7
  13. angr/analyses/decompiler/dephication/rewriting_engine.py +16 -7
  14. angr/analyses/decompiler/expression_narrower.py +1 -1
  15. angr/analyses/decompiler/optimization_passes/__init__.py +3 -0
  16. angr/analyses/decompiler/optimization_passes/condition_constprop.py +149 -0
  17. angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +8 -7
  18. angr/analyses/decompiler/optimization_passes/deadblock_remover.py +12 -3
  19. angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +1 -1
  20. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +21 -13
  21. angr/analyses/decompiler/optimization_passes/optimization_pass.py +21 -12
  22. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +17 -9
  23. angr/analyses/decompiler/optimization_passes/return_duplicator_high.py +7 -10
  24. angr/analyses/decompiler/peephole_optimizations/eager_eval.py +12 -1
  25. angr/analyses/decompiler/peephole_optimizations/remove_redundant_conversions.py +61 -25
  26. angr/analyses/decompiler/peephole_optimizations/remove_redundant_shifts.py +50 -1
  27. angr/analyses/decompiler/presets/fast.py +2 -0
  28. angr/analyses/decompiler/presets/full.py +2 -0
  29. angr/analyses/decompiler/region_simplifiers/expr_folding.py +259 -108
  30. angr/analyses/decompiler/region_simplifiers/region_simplifier.py +28 -9
  31. angr/analyses/decompiler/ssailification/rewriting_engine.py +20 -2
  32. angr/analyses/decompiler/ssailification/traversal_engine.py +4 -3
  33. angr/analyses/decompiler/structured_codegen/c.py +10 -3
  34. angr/analyses/decompiler/structuring/dream.py +28 -19
  35. angr/analyses/decompiler/structuring/phoenix.py +253 -89
  36. angr/analyses/decompiler/structuring/recursive_structurer.py +1 -0
  37. angr/analyses/decompiler/structuring/structurer_base.py +121 -46
  38. angr/analyses/decompiler/structuring/structurer_nodes.py +6 -1
  39. angr/analyses/decompiler/utils.py +60 -1
  40. angr/analyses/deobfuscator/api_obf_finder.py +13 -5
  41. angr/analyses/deobfuscator/api_obf_type2_finder.py +166 -0
  42. angr/analyses/deobfuscator/string_obf_finder.py +105 -18
  43. angr/analyses/forward_analysis/forward_analysis.py +1 -1
  44. angr/analyses/propagator/top_checker_mixin.py +6 -6
  45. angr/analyses/reaching_definitions/__init__.py +2 -1
  46. angr/analyses/reaching_definitions/dep_graph.py +1 -12
  47. angr/analyses/reaching_definitions/engine_vex.py +36 -31
  48. angr/analyses/reaching_definitions/function_handler.py +15 -2
  49. angr/analyses/reaching_definitions/rd_state.py +1 -37
  50. angr/analyses/reaching_definitions/reaching_definitions.py +13 -24
  51. angr/analyses/s_propagator.py +129 -87
  52. angr/analyses/s_reaching_definitions/s_rda_model.py +7 -1
  53. angr/analyses/s_reaching_definitions/s_rda_view.py +2 -2
  54. angr/analyses/s_reaching_definitions/s_reaching_definitions.py +3 -1
  55. angr/analyses/stack_pointer_tracker.py +36 -22
  56. angr/analyses/typehoon/simple_solver.py +45 -7
  57. angr/analyses/typehoon/typeconsts.py +18 -5
  58. angr/analyses/variable_recovery/engine_ail.py +1 -1
  59. angr/analyses/variable_recovery/engine_base.py +62 -67
  60. angr/analyses/variable_recovery/engine_vex.py +1 -1
  61. angr/analyses/variable_recovery/irsb_scanner.py +2 -2
  62. angr/block.py +69 -107
  63. angr/callable.py +14 -7
  64. angr/calling_conventions.py +81 -10
  65. angr/distributed/__init__.py +1 -1
  66. angr/engines/__init__.py +7 -8
  67. angr/engines/engine.py +3 -138
  68. angr/engines/failure.py +2 -2
  69. angr/engines/hook.py +2 -2
  70. angr/engines/light/engine.py +5 -10
  71. angr/engines/pcode/emulate.py +2 -2
  72. angr/engines/pcode/engine.py +2 -14
  73. angr/engines/pcode/lifter.py +2 -2
  74. angr/engines/procedure.py +2 -2
  75. angr/engines/soot/engine.py +2 -2
  76. angr/engines/soot/statements/switch.py +1 -1
  77. angr/engines/successors.py +123 -17
  78. angr/engines/syscall.py +2 -2
  79. angr/engines/unicorn.py +3 -3
  80. angr/engines/vex/heavy/heavy.py +3 -15
  81. angr/engines/vex/lifter.py +2 -2
  82. angr/engines/vex/light/light.py +2 -2
  83. angr/factory.py +4 -19
  84. angr/knowledge_plugins/cfg/cfg_model.py +3 -2
  85. angr/knowledge_plugins/key_definitions/atoms.py +8 -4
  86. angr/knowledge_plugins/key_definitions/live_definitions.py +41 -103
  87. angr/knowledge_plugins/labels.py +2 -2
  88. angr/knowledge_plugins/obfuscations.py +1 -0
  89. angr/knowledge_plugins/xrefs/xref_manager.py +4 -0
  90. angr/sim_type.py +19 -17
  91. angr/state_plugins/plugin.py +19 -4
  92. angr/storage/memory_mixins/memory_mixin.py +1 -1
  93. angr/storage/memory_mixins/paged_memory/pages/multi_values.py +10 -5
  94. angr/utils/ssa/__init__.py +119 -4
  95. {angr-9.2.138.dist-info → angr-9.2.140.dist-info}/METADATA +6 -6
  96. {angr-9.2.138.dist-info → angr-9.2.140.dist-info}/RECORD +100 -98
  97. {angr-9.2.138.dist-info → angr-9.2.140.dist-info}/LICENSE +0 -0
  98. {angr-9.2.138.dist-info → angr-9.2.140.dist-info}/WHEEL +0 -0
  99. {angr-9.2.138.dist-info → angr-9.2.140.dist-info}/entry_points.txt +0 -0
  100. {angr-9.2.138.dist-info → angr-9.2.140.dist-info}/top_level.txt +0 -0
@@ -3,7 +3,7 @@ from enum import Enum, auto
3
3
 
4
4
  import claripy
5
5
  import ailment
6
- from archinfo import Arch, RegisterOffset
6
+ from archinfo import Arch, Endness, RegisterOffset
7
7
 
8
8
  from angr.calling_conventions import SimFunctionArgument, SimRegArg, SimStackArg
9
9
  from angr.engines.light import SpOffset
@@ -123,7 +123,7 @@ class Atom:
123
123
  register = reg
124
124
 
125
125
  @staticmethod
126
- def mem(addr: SpOffset | HeapAddress | int, size: int, endness: str | None = None) -> MemoryLocation:
126
+ def mem(addr: SpOffset | HeapAddress | int, size: int, endness: Endness | None = None) -> MemoryLocation:
127
127
  """
128
128
  Create a MemoryLocation atom,
129
129
 
@@ -251,7 +251,11 @@ class VirtualVariable(Atom):
251
251
  )
252
252
 
253
253
  def __init__(
254
- self, varid: int, size: int, category: ailment.Expr.VirtualVariableCategory, oident: str | int | None = None
254
+ self,
255
+ varid: int,
256
+ size: int,
257
+ category: ailment.Expr.VirtualVariableCategory,
258
+ oident: str | int | tuple | None = None,
255
259
  ):
256
260
  super().__init__(size)
257
261
 
@@ -310,7 +314,7 @@ class MemoryLocation(Atom):
310
314
  "endness",
311
315
  )
312
316
 
313
- def __init__(self, addr: SpOffset | HeapAddress | int, size: int, endness: str | None = None):
317
+ def __init__(self, addr: SpOffset | HeapAddress | int, size: int, endness: Endness | None = None):
314
318
  """
315
319
  :param int addr: The address of the beginning memory location slice.
316
320
  :param int size: The size of the represented memory location, in bytes.
@@ -1,26 +1,26 @@
1
1
  from __future__ import annotations
2
2
  from typing import Any, TYPE_CHECKING, cast, overload
3
+ from collections import defaultdict
3
4
  from collections.abc import Iterable, Generator
5
+ from enum import Enum, auto
4
6
  import weakref
5
7
  import logging
6
- from enum import Enum, auto
7
-
8
- from collections import defaultdict
9
8
 
10
9
  import claripy
11
10
  from claripy.annotation import Annotation
12
11
  import archinfo
13
12
 
14
- from angr.misc.ux import deprecated
15
13
  from angr.errors import SimMemoryMissingError, SimMemoryError
16
14
  from angr.storage.memory_mixins import MultiValuedMemory
17
15
  from angr.storage.memory_mixins.paged_memory.pages.multi_values import MVType, MultiValues
18
16
  from angr.knowledge_plugins.key_definitions.definition import A
19
17
  from angr.engines.light import SpOffset
20
18
  from angr.code_location import CodeLocation, ExternalCodeLocation
19
+ from angr.utils.constants import is_alignment_mask
21
20
  from .atoms import Atom, Register, MemoryLocation, Tmp, ConstantSrc
22
21
  from .definition import Definition, Tag
23
22
  from .heap_address import HeapAddress
23
+ from .undefined import Undefined
24
24
  from .uses import Uses
25
25
 
26
26
  if TYPE_CHECKING:
@@ -207,26 +207,6 @@ class LiveDefinitions:
207
207
 
208
208
  self.uses_by_codeloc: dict[CodeLocation, set[Definition]] = defaultdict(set)
209
209
 
210
- @property
211
- @deprecated("registers")
212
- def register_definitions(self) -> MultiValuedMemory:
213
- return self.registers
214
-
215
- @property
216
- @deprecated("stack")
217
- def stack_definitions(self) -> MultiValuedMemory:
218
- return self.stack
219
-
220
- @property
221
- @deprecated("memory")
222
- def memory_definitions(self) -> MultiValuedMemory:
223
- return self.memory
224
-
225
- @property
226
- @deprecated("heap")
227
- def heap_definitions(self) -> MultiValuedMemory:
228
- return self.heap
229
-
230
210
  def __repr__(self):
231
211
  ctnt = "LiveDefs"
232
212
  if self.tmps:
@@ -359,6 +339,19 @@ class LiveDefinitions:
359
339
  return off0 - off1
360
340
  return None
361
341
 
342
+ @staticmethod
343
+ def _simplify_sp_alignment(data: MultiValues) -> MultiValues:
344
+ """
345
+ Eliminate trivial stack pointer alignment, e.g.: {sp & ~0xf} -> {sp}
346
+ """
347
+ value = data.one_value()
348
+ if value is not None and "stack_base" in value.variables and value.op == "__and__" and len(value.args) == 2:
349
+ if value.args[1].concrete and is_alignment_mask(value.args[1].concrete_value):
350
+ return MultiValues(value.args[0])
351
+ if value.args[0].concrete and is_alignment_mask(value.args[0].concrete_value):
352
+ return MultiValues(value.args[1])
353
+ return data
354
+
362
355
  @staticmethod
363
356
  def annotate_with_def(symvar: MVType, definition: Definition) -> MVType:
364
357
  """
@@ -521,7 +514,8 @@ class LiveDefinitions:
521
514
  else:
522
515
  l.warning("Skip stack storing since the stack offset is None.")
523
516
  elif isinstance(atom.addr, HeapAddress):
524
- self.heap.erase(atom.addr.value, size=atom.size)
517
+ if not isinstance(atom.addr.value, Undefined):
518
+ self.heap.erase(atom.addr.value, size=atom.size)
525
519
  elif isinstance(atom.addr, int):
526
520
  self.memory.erase(atom.addr, size=atom.size)
527
521
  elif isinstance(atom.addr, claripy.ast.Base):
@@ -571,6 +565,11 @@ class LiveDefinitions:
571
565
 
572
566
  # set_object() replaces kill (not implemented) and add (add) in one step
573
567
  if isinstance(atom, Register):
568
+
569
+ # Tolerate simple stack pointer alignments
570
+ if atom.reg_offset == self.arch.sp_offset:
571
+ d = self._simplify_sp_alignment(d)
572
+
574
573
  try:
575
574
  self.registers.store(
576
575
  atom.reg_offset,
@@ -611,7 +610,7 @@ class LiveDefinitions:
611
610
  return None
612
611
  elif isinstance(atom, Tmp):
613
612
  if self.track_tmps:
614
- self.tmps[atom.tmp_idx] = {definition}
613
+ self.tmps[atom.tmp_idx] = {Definition(atom, code_loc, dummy=dummy, tags=tags)}
615
614
  else:
616
615
  self.tmps[atom.tmp_idx] = self.uses_by_codeloc[code_loc]
617
616
  return None
@@ -686,6 +685,8 @@ class LiveDefinitions:
686
685
  if isinstance(thing.addr, SpOffset):
687
686
  return self.get_stack_definitions(thing.addr.offset, thing.size)
688
687
  if isinstance(thing.addr, HeapAddress):
688
+ if isinstance(thing.addr.value, Undefined):
689
+ return set()
689
690
  return self.get_heap_definitions(thing.addr.value, size=thing.size)
690
691
  if isinstance(thing.addr, int):
691
692
  return self.get_memory_definitions(thing.addr, thing.size)
@@ -747,72 +748,9 @@ class LiveDefinitions:
747
748
 
748
749
  return LiveDefinitions.extract_defs_from_annotations(annotations)
749
750
 
750
- @deprecated("get_definitions")
751
- def get_definitions_from_atoms(self, atoms: Iterable[A]) -> Iterable[Definition]:
752
- result = set()
753
- for atom in atoms:
754
- result |= self.get_definitions(atom)
755
- return result
756
-
757
- @deprecated("get_values")
758
- def get_value_from_definition(self, definition: Definition) -> MultiValues | None:
759
- return self.get_value_from_atom(definition.atom)
760
-
761
- @deprecated("get_one_value")
762
- def get_one_value_from_definition(self, definition: Definition) -> claripy.ast.bv.BV | None:
763
- return self.get_one_value_from_atom(definition.atom)
764
-
765
- @deprecated("get_concrete_value")
766
- def get_concrete_value_from_definition(self, definition: Definition) -> int | None:
767
- return self.get_concrete_value_from_atom(definition.atom)
768
-
769
- @deprecated("get_values")
770
- def get_value_from_atom(self, atom: Atom) -> MultiValues | None:
771
- if isinstance(atom, Register):
772
- try:
773
- return self.registers.load(atom.reg_offset, size=atom.size)
774
- except SimMemoryMissingError:
775
- return None
776
- elif isinstance(atom, MemoryLocation):
777
- if isinstance(atom.addr, SpOffset):
778
- stack_addr = self.stack_offset_to_stack_addr(atom.addr.offset)
779
- try:
780
- return self.stack.load(stack_addr, size=atom.size, endness=atom.endness)
781
- except SimMemoryMissingError:
782
- return None
783
- elif isinstance(atom.addr, HeapAddress):
784
- try:
785
- return self.heap.load(atom.addr.value, size=atom.size, endness=atom.endness)
786
- except SimMemoryMissingError:
787
- return None
788
- elif isinstance(atom.addr, int):
789
- try:
790
- return self.memory.load(atom.addr, size=atom.size, endness=atom.endness)
791
- except SimMemoryMissingError:
792
- return None
793
- else:
794
- # ignore RegisterOffset
795
- return None
796
- else:
797
- return None
798
-
799
- @deprecated("get_one_value")
800
- def get_one_value_from_atom(self, atom: Atom) -> claripy.ast.bv.BV | None:
801
- r = self.get_value_from_atom(atom)
802
- if r is None:
803
- return None
804
- return r.one_value()
805
-
806
- @deprecated("get_concrete_value")
807
- def get_concrete_value_from_atom(self, atom: Atom) -> int | None:
808
- r = self.get_one_value_from_atom(atom)
809
- if r is None:
810
- return None
811
- if r.symbolic:
812
- return None
813
- return r.concrete_value
814
-
815
- def get_values(self, spec: A | Definition[A] | Iterable[A] | Iterable[Definition[A]]) -> MultiValues | None:
751
+ def get_values(
752
+ self, spec: A | Definition[A] | Iterable[A] | Iterable[Definition[A]], endness: archinfo.Endness | None = None
753
+ ) -> MultiValues | None:
816
754
  if isinstance(spec, Definition):
817
755
  atom = spec.atom
818
756
  elif isinstance(spec, Atom):
@@ -820,7 +758,7 @@ class LiveDefinitions:
820
758
  else:
821
759
  result = None
822
760
  for atom in spec:
823
- r = self.get_values(atom)
761
+ r = self.get_values(atom, endness)
824
762
  if r is None:
825
763
  continue
826
764
  result = r if result is None else result.merge(r)
@@ -831,27 +769,29 @@ class LiveDefinitions:
831
769
  return self.registers.load(atom.reg_offset, size=atom.size)
832
770
  except SimMemoryMissingError:
833
771
  return None
834
- elif isinstance(atom, MemoryLocation):
772
+
773
+ if isinstance(atom, MemoryLocation):
774
+ endness = endness or atom.endness
835
775
  if isinstance(atom.addr, SpOffset):
836
776
  stack_addr = self.stack_offset_to_stack_addr(atom.addr.offset)
837
777
  try:
838
- return self.stack.load(stack_addr, size=atom.size, endness=atom.endness)
778
+ return self.stack.load(stack_addr, size=atom.size, endness=endness)
839
779
  except SimMemoryMissingError:
840
780
  return None
841
781
  elif isinstance(atom.addr, HeapAddress):
842
782
  try:
843
- return self.heap.load(atom.addr.value, size=atom.size, endness=atom.endness)
783
+ return self.heap.load(atom.addr.value, size=atom.size, endness=endness)
844
784
  except SimMemoryMissingError:
845
785
  return None
846
786
  elif isinstance(atom.addr, int):
847
787
  try:
848
- return self.memory.load(atom.addr, size=atom.size, endness=atom.endness)
788
+ return self.memory.load(atom.addr, size=atom.size, endness=endness)
849
789
  except SimMemoryMissingError:
850
790
  pass
851
791
  if self.project is not None:
852
792
  try:
853
793
  bytestring = self.project.loader.memory.load(atom.addr, atom.size)
854
- if atom.endness == archinfo.Endness.LE:
794
+ if endness == archinfo.Endness.LE:
855
795
  bytestring = bytes(reversed(bytestring))
856
796
  return MultiValues(
857
797
  self.annotate_with_def(claripy.BVV(bytestring), Definition(atom, ExternalCodeLocation()))
@@ -877,14 +817,12 @@ class LiveDefinitions:
877
817
 
878
818
  @overload
879
819
  def get_concrete_value(
880
- self, spec: A | Definition[A] | Iterable[A] | Iterable[Definition[A]], cast_to: type[int] = ...
820
+ self, spec: A | Definition[A] | Iterable[A] | Iterable[Definition[A]], cast_to: type[int]
881
821
  ) -> int | None: ...
882
822
 
883
823
  @overload
884
824
  def get_concrete_value(
885
- self,
886
- spec: A | Definition[A] | Iterable[A] | Iterable[Definition[A]],
887
- cast_to: type[bytes] = ...,
825
+ self, spec: A | Definition[A] | Iterable[A] | Iterable[Definition[A]], cast_to: type[bytes]
888
826
  ) -> bytes | None: ...
889
827
 
890
828
  def get_concrete_value(
@@ -991,7 +929,7 @@ class LiveDefinitions:
991
929
 
992
930
  def deref(self, pointer, size, endness=archinfo.Endness.BE):
993
931
  if isinstance(pointer, (Atom, Definition)):
994
- pointer = self.get_values(pointer)
932
+ pointer = self.get_values(pointer, self.arch.memory_endness)
995
933
  if pointer is None:
996
934
  return set()
997
935
 
@@ -95,14 +95,14 @@ class Labels(KnowledgeBasePlugin):
95
95
  :return: A unique label name.
96
96
  """
97
97
 
98
- if label not in self._labels:
98
+ if label not in self._reverse_labels:
99
99
  return label
100
100
 
101
101
  # use it as the prefix
102
102
  i = 1
103
103
  while True:
104
104
  new_label = f"{label}_{i}"
105
- if new_label not in self._labels:
105
+ if new_label not in self._reverse_labels:
106
106
  return new_label
107
107
  i += 1
108
108
 
@@ -20,6 +20,7 @@ class Obfuscations(KnowledgeBasePlugin):
20
20
 
21
21
  self.obfuscated_apis_analyzed: bool = False
22
22
  self.type1_deobfuscated_apis: dict[int, tuple[str, str]] = {}
23
+ self.type2_deobfuscated_apis: dict[int, str] = {}
23
24
 
24
25
  def copy(self):
25
26
  o = Obfuscations(self._kb)
@@ -24,6 +24,10 @@ class XRefManager(KnowledgeBasePlugin, Serializable):
24
24
  xm.xrefs_by_dst = self.xrefs_by_dst.copy()
25
25
  return xm
26
26
 
27
+ def clear(self):
28
+ self.xrefs_by_ins_addr = defaultdict(set)
29
+ self.xrefs_by_dst = defaultdict(set)
30
+
27
31
  def add_xref(self, xref):
28
32
  to_remove = set()
29
33
  # Overwrite existing "offset" refs
angr/sim_type.py CHANGED
@@ -20,10 +20,8 @@ from .misc.ux import deprecated
20
20
 
21
21
  if TYPE_CHECKING:
22
22
  from angr.procedures.definitions import SimTypeCollection
23
- from angr.storage.memory_mixins import _Coerce
24
-
25
- StoreType = _Coerce
26
23
 
24
+ StoreType = int | claripy.ast.BV
27
25
 
28
26
  l = logging.getLogger(name=__name__)
29
27
 
@@ -318,6 +316,8 @@ class SimTypeReg(SimType):
318
316
  return f"reg{self.size}_t"
319
317
 
320
318
  def store(self, state, addr, value: StoreType):
319
+ if self.size is None:
320
+ raise TypeError("Need a size to store")
321
321
  store_endness = state.arch.memory_endness
322
322
  with contextlib.suppress(AttributeError):
323
323
  value = value.ast # type: ignore
@@ -366,7 +366,7 @@ class SimTypeNum(SimType):
366
366
  def extract(self, state, addr, concrete: Literal[False] = ...) -> claripy.ast.BV: ...
367
367
 
368
368
  @overload
369
- def extract(self, state, addr, concrete: Literal[True] = ...) -> int: ...
369
+ def extract(self, state, addr, concrete: Literal[True]) -> int: ...
370
370
 
371
371
  def extract(self, state, addr, concrete=False):
372
372
  out = state.memory.load(addr, self.size // state.arch.byte_width, endness=state.arch.memory_endness)
@@ -445,7 +445,7 @@ class SimTypeInt(SimTypeReg):
445
445
  def extract(self, state, addr, concrete: Literal[False] = ...) -> claripy.ast.BV: ...
446
446
 
447
447
  @overload
448
- def extract(self, state, addr, concrete: Literal[True] = ...) -> int: ...
448
+ def extract(self, state, addr, concrete: Literal[True]) -> int: ...
449
449
 
450
450
  def extract(self, state, addr, concrete=False):
451
451
  out = state.memory.load(addr, self.size // state.arch.byte_width, endness=state.arch.memory_endness)
@@ -575,7 +575,7 @@ class SimTypeChar(SimTypeReg):
575
575
  def extract(self, state, addr, concrete: Literal[False] = ...) -> claripy.ast.BV: ...
576
576
 
577
577
  @overload
578
- def extract(self, state, addr, concrete: Literal[True] = ...) -> bytes: ...
578
+ def extract(self, state, addr, concrete: Literal[True]) -> bytes: ...
579
579
 
580
580
  def extract(self, state, addr, concrete: bool = False) -> claripy.ast.BV | bytes:
581
581
  # FIXME: This is a hack.
@@ -665,7 +665,7 @@ class SimTypeBool(SimTypeReg):
665
665
  def extract(self, state, addr, concrete: Literal[False] = ...) -> claripy.ast.Bool: ...
666
666
 
667
667
  @overload
668
- def extract(self, state, addr, concrete: Literal[True] = ...) -> bool: ...
668
+ def extract(self, state, addr, concrete: Literal[True]) -> bool: ...
669
669
 
670
670
  def extract(self, state, addr, concrete=False):
671
671
  ver = super().extract(state, addr, concrete)
@@ -715,7 +715,7 @@ class SimTypeFd(SimTypeReg):
715
715
  def extract(self, state, addr, concrete: Literal[False] = ...) -> claripy.ast.BV: ...
716
716
 
717
717
  @overload
718
- def extract(self, state, addr, concrete: Literal[True] = ...) -> int: ...
718
+ def extract(self, state, addr, concrete: Literal[True]) -> int: ...
719
719
 
720
720
  def extract(self, state, addr, concrete=False):
721
721
  # TODO: EDG says this looks dangerously closed-minded. Just in case...
@@ -788,7 +788,7 @@ class SimTypePointer(SimTypeReg):
788
788
  def extract(self, state, addr, concrete: Literal[False] = ...) -> claripy.ast.BV: ...
789
789
 
790
790
  @overload
791
- def extract(self, state, addr, concrete: Literal[True] = ...) -> int: ...
791
+ def extract(self, state, addr, concrete: Literal[True]) -> int: ...
792
792
 
793
793
  def extract(self, state, addr, concrete=False):
794
794
  # TODO: EDG says this looks dangerously closed-minded. Just in case...
@@ -848,7 +848,7 @@ class SimTypeReference(SimTypeReg):
848
848
  def extract(self, state, addr, concrete: Literal[False] = ...) -> claripy.ast.BV: ...
849
849
 
850
850
  @overload
851
- def extract(self, state, addr, concrete: Literal[True] = ...) -> int: ...
851
+ def extract(self, state, addr, concrete: Literal[True]) -> int: ...
852
852
 
853
853
  def extract(self, state, addr, concrete=False):
854
854
  # TODO: EDG says this looks dangerously closed-minded. Just in case...
@@ -984,7 +984,7 @@ class SimTypeString(NamedTypeMixin, SimType):
984
984
  def extract(self, state, addr, concrete: Literal[False] = ...) -> claripy.ast.BV: ...
985
985
 
986
986
  @overload
987
- def extract(self, state, addr, concrete: Literal[True] = ...) -> bytes: ...
987
+ def extract(self, state, addr, concrete: Literal[True]) -> bytes: ...
988
988
 
989
989
  def extract(self, state: SimState, addr, concrete=False):
990
990
  if self.length is None:
@@ -1585,7 +1585,7 @@ class SimStructValue:
1585
1585
  for name in self._struct.fields:
1586
1586
  value = self._values[name]
1587
1587
  try:
1588
- f = value.__indented_repr__
1588
+ f = value.__indented_repr__ # type: ignore[reportAttributeAccessIssue]
1589
1589
  s = f(indent=indent + 2)
1590
1590
  except AttributeError:
1591
1591
  s = repr(value)
@@ -1620,7 +1620,7 @@ class SimStructValue:
1620
1620
  class SimUnion(NamedTypeMixin, SimType):
1621
1621
  fields = ("members", "name")
1622
1622
 
1623
- def __init__(self, members, name=None, label=None):
1623
+ def __init__(self, members: dict[str, SimType], name=None, label=None):
1624
1624
  """
1625
1625
  :param members: The members of the union, as a mapping name -> type
1626
1626
  :param name: The name of the union
@@ -1630,6 +1630,8 @@ class SimUnion(NamedTypeMixin, SimType):
1630
1630
 
1631
1631
  @property
1632
1632
  def size(self):
1633
+ if self._arch is None:
1634
+ raise ValueError("Can't tell my size without an arch!")
1633
1635
  member_sizes = [ty.size for ty in self.members.values() if not isinstance(ty, SimTypeBottom)]
1634
1636
  # fall back to word size in case all members are SimTypeBottom
1635
1637
  return max(member_sizes) if member_sizes else self._arch.bytes
@@ -1722,7 +1724,7 @@ class SimUnionValue:
1722
1724
  fields = []
1723
1725
  for name, value in self._values.items():
1724
1726
  try:
1725
- f = value.__indented_repr__
1727
+ f = value.__indented_repr__ # type: ignore[reportAttributeAccessIssue]
1726
1728
  s = f(indent=indent + 2)
1727
1729
  except AttributeError:
1728
1730
  s = repr(value)
@@ -1820,7 +1822,7 @@ class SimCppClassValue(SimStructValue):
1820
1822
  for name in self._class.fields:
1821
1823
  value = self._values[name]
1822
1824
  try:
1823
- f = value.__indented_repr__
1825
+ f = value.__indented_repr__ # type: ignore[reportAttributeAccessIssue]
1824
1826
  s = f(indent=indent + 2)
1825
1827
  except AttributeError:
1826
1828
  s = repr(value)
@@ -1864,10 +1866,10 @@ class SimTypeNumOffset(SimTypeNum):
1864
1866
  self.offset = offset
1865
1867
 
1866
1868
  @overload
1867
- def extract(self, state, addr, concrete: Literal[False] = ...) -> claripy.ast.BV: ...
1869
+ def extract(self, state: SimState, addr, concrete: Literal[False] = ...) -> claripy.ast.BV: ...
1868
1870
 
1869
1871
  @overload
1870
- def extract(self, state, addr, concrete: Literal[True] = ...) -> int: ...
1872
+ def extract(self, state: SimState, addr, concrete: Literal[True]) -> int: ...
1871
1873
 
1872
1874
  def extract(self, state: SimState, addr, concrete=False):
1873
1875
  if state.arch.memory_endness != Endness.LE:
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import cast
3
+ from typing import Any, Generic, cast, TypeVar, Protocol
4
+ from collections.abc import Callable
4
5
 
5
6
  import logging
6
7
 
@@ -9,6 +10,18 @@ from angr.misc.ux import once
9
10
 
10
11
  l = logging.getLogger(name=__name__)
11
12
 
13
+ T = TypeVar("T")
14
+ S_co = TypeVar("S_co", covariant=True)
15
+
16
+
17
+ class _CopyFunc(Protocol, Generic[S_co]):
18
+ """
19
+ Function wrapping copy method for memo tracking.
20
+ """
21
+
22
+ @staticmethod
23
+ def __call__(memo: dict[int, Any] | None = None) -> S_co: ...
24
+
12
25
 
13
26
  class SimStatePlugin:
14
27
  """
@@ -55,12 +68,12 @@ class SimStatePlugin:
55
68
  return o
56
69
 
57
70
  @staticmethod
58
- def memo(f):
71
+ def memo(f: Callable[[T, dict[int, Any]], S_co]) -> _CopyFunc[S_co]:
59
72
  """
60
73
  A decorator function you should apply to ``copy``
61
74
  """
62
75
 
63
- def inner(self, memo=None, **kwargs):
76
+ def inner(self, memo: dict[int, Any] | None = None, **kwargs: Any) -> S_co:
64
77
  if memo is None:
65
78
  memo = {}
66
79
  if id(self) in memo:
@@ -69,7 +82,9 @@ class SimStatePlugin:
69
82
  memo[id(self)] = c
70
83
  return c
71
84
 
72
- return inner
85
+ # Type-checking fails here because we can't express the `self` partial-application with a Protocol
86
+ # and we can't express the optional `memo` parameter without a Protocol
87
+ return inner # type: ignore
73
88
 
74
89
  def merge(self, others, merge_conditions, common_ancestor=None): # pylint:disable=unused-argument
75
90
  """
@@ -80,7 +80,7 @@ class MemoryMixin(Generic[InData, OutData, Addr], SimStatePlugin):
80
80
  def store(self, addr: Addr, data: InData, size: InData | None = None, **kwargs) -> None: ...
81
81
 
82
82
  def merge(
83
- self, others: list[Self], merge_conditions: list[claripy.ast.Bool], common_ancestor: Self | None = None
83
+ self, others: list[Self], merge_conditions: list[claripy.ast.Bool] | None, common_ancestor: Self | None = None
84
84
  ) -> bool: ...
85
85
 
86
86
  def compare(self, other: Self) -> bool: ...
@@ -5,7 +5,6 @@ import archinfo
5
5
 
6
6
  import claripy
7
7
 
8
- from angr.errors import AngrTypeError
9
8
  from angr.storage.memory_object import bv_slice
10
9
 
11
10
  MVType = TypeVar("MVType", bound=claripy.ast.BV | claripy.ast.FP)
@@ -91,8 +90,10 @@ class MultiValues(Generic[MVType]):
91
90
  value_end = offset + value.size() // 8
92
91
  if value_end > succ_offset:
93
92
  if isinstance(value, claripy.ast.FP):
94
- raise AngrTypeError("Unsupported case. How do we handle floating point values overlapping?")
93
+ # no precise floating point support; it directly goes to TOP
94
+ value = cast(MVType, claripy.BVS("top", value.size()))
95
95
  # value is too long. we need to break value into two
96
+ assert isinstance(value, claripy.ast.BV)
96
97
  mid_value_size = succ_offset - offset
97
98
  remaining_value = cast(MVType, value[value.size() - mid_value_size * 8 - 1 : 0])
98
99
  # update value
@@ -107,7 +108,8 @@ class MultiValues(Generic[MVType]):
107
108
  remaining_values = set()
108
109
  for v in self._values[offset]:
109
110
  if isinstance(v, claripy.ast.FP):
110
- raise AngrTypeError("Unsupported case. How do we handle floating point values overlapping?")
111
+ # no precise floating point support; it directly goes to TOP
112
+ v = claripy.BVS("top", v.size())
111
113
 
112
114
  new_curr_values.add(v[v.size() - 1 : v.size() - value.size()])
113
115
  remaining_values.add(v[v.size() - value.size() - 1 : 0])
@@ -116,9 +118,11 @@ class MultiValues(Generic[MVType]):
116
118
  self.add_value(offset + value.size() // 8, v)
117
119
  elif curr_value_size < value.size() // 8:
118
120
  if isinstance(value, claripy.ast.FP):
119
- raise AngrTypeError("Unsupported case. How do we handle floating point values overlapping?")
121
+ # no precise floating point support; it directly goes to TOP
122
+ value = cast(MVType, claripy.BVS("top", value.size()))
120
123
 
121
124
  # value is too long. we need to break value into two
125
+ assert isinstance(value, claripy.ast.BV)
122
126
  remaining_value = cast(MVType, value[value.size() - curr_value_size * 8 - 1 : 0])
123
127
  # update value
124
128
  value = cast(MVType, value[value.size() - 1 : value.size() - curr_value_size * 8])
@@ -137,7 +141,8 @@ class MultiValues(Generic[MVType]):
137
141
  remaining_values = set()
138
142
  for v in pre_values:
139
143
  if isinstance(v, claripy.ast.FP):
140
- raise AngrTypeError("Unsupported case. How do we handle floating point values overlapping?")
144
+ # no precise floating point support; it directly goes to TOP
145
+ v = claripy.BVS("top", v.size())
141
146
  new_pre_values.add(v[v.size() - 1 : v.size() - new_pre_value_size * 8])
142
147
  remaining_values.add(v[v.size() - new_pre_value_size * 8 - 1 : 0])
143
148
  self._values[pre_offset] = new_pre_values