angr 9.2.123__py3-none-manylinux2014_aarch64.whl → 9.2.124__py3-none-manylinux2014_aarch64.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 (83) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/cfg/indirect_jump_resolvers/mips_elf_fast.py +11 -8
  3. angr/analyses/cfg/indirect_jump_resolvers/mips_elf_got.py +2 -2
  4. angr/analyses/decompiler/ail_simplifier.py +16 -19
  5. angr/analyses/decompiler/callsite_maker.py +8 -7
  6. angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +24 -2
  7. angr/analyses/decompiler/clinic.py +27 -1
  8. angr/analyses/decompiler/condition_processor.py +10 -3
  9. angr/analyses/decompiler/decompilation_cache.py +2 -0
  10. angr/analyses/decompiler/decompiler.py +50 -8
  11. angr/analyses/decompiler/dephication/graph_vvar_mapping.py +10 -2
  12. angr/analyses/decompiler/dephication/rewriting_engine.py +64 -1
  13. angr/analyses/decompiler/expression_narrower.py +5 -1
  14. angr/analyses/decompiler/optimization_passes/div_simplifier.py +4 -1
  15. angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +7 -0
  16. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +23 -4
  17. angr/analyses/decompiler/optimization_passes/optimization_pass.py +3 -1
  18. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +8 -5
  19. angr/analyses/decompiler/optimization_passes/return_duplicator_high.py +10 -5
  20. angr/analyses/decompiler/optimization_passes/return_duplicator_low.py +18 -7
  21. angr/analyses/decompiler/optimization_passes/switch_default_case_duplicator.py +6 -0
  22. angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +2 -0
  23. angr/analyses/decompiler/peephole_optimizations/const_mull_a_shift.py +2 -0
  24. angr/analyses/decompiler/peephole_optimizations/remove_cascading_conversions.py +8 -2
  25. angr/analyses/decompiler/region_identifier.py +36 -0
  26. angr/analyses/decompiler/region_simplifiers/loop.py +2 -8
  27. angr/analyses/decompiler/region_simplifiers/switch_cluster_simplifier.py +9 -3
  28. angr/analyses/decompiler/ssailification/rewriting.py +5 -2
  29. angr/analyses/decompiler/ssailification/rewriting_engine.py +151 -25
  30. angr/analyses/decompiler/ssailification/rewriting_state.py +1 -0
  31. angr/analyses/decompiler/ssailification/ssailification.py +17 -9
  32. angr/analyses/decompiler/ssailification/traversal.py +3 -1
  33. angr/analyses/decompiler/ssailification/traversal_engine.py +35 -8
  34. angr/analyses/decompiler/ssailification/traversal_state.py +1 -0
  35. angr/analyses/decompiler/structured_codegen/c.py +42 -4
  36. angr/analyses/decompiler/structuring/phoenix.py +3 -0
  37. angr/analyses/propagator/engine_ail.py +10 -3
  38. angr/analyses/reaching_definitions/engine_ail.py +10 -15
  39. angr/analyses/s_propagator.py +16 -9
  40. angr/analyses/s_reaching_definitions/s_rda_view.py +127 -63
  41. angr/analyses/variable_recovery/engine_ail.py +14 -0
  42. angr/analyses/variable_recovery/engine_base.py +11 -0
  43. angr/engines/light/engine.py +12 -0
  44. angr/knowledge_plugins/__init__.py +2 -0
  45. angr/knowledge_plugins/decompilation.py +45 -0
  46. angr/knowledge_plugins/key_definitions/atoms.py +8 -0
  47. angr/procedures/definitions/parse_win32json.py +2 -1
  48. angr/storage/memory_mixins/actions_mixin.py +7 -7
  49. angr/storage/memory_mixins/address_concretization_mixin.py +5 -5
  50. angr/storage/memory_mixins/bvv_conversion_mixin.py +1 -1
  51. angr/storage/memory_mixins/clouseau_mixin.py +3 -3
  52. angr/storage/memory_mixins/conditional_store_mixin.py +3 -3
  53. angr/storage/memory_mixins/default_filler_mixin.py +3 -3
  54. angr/storage/memory_mixins/memory_mixin.py +45 -34
  55. angr/storage/memory_mixins/paged_memory/page_backer_mixins.py +15 -14
  56. angr/storage/memory_mixins/paged_memory/paged_memory_mixin.py +27 -16
  57. angr/storage/memory_mixins/paged_memory/pages/cooperation.py +18 -9
  58. angr/storage/memory_mixins/paged_memory/pages/ispo_mixin.py +5 -5
  59. angr/storage/memory_mixins/paged_memory/pages/multi_values.py +89 -55
  60. angr/storage/memory_mixins/paged_memory/pages/mv_list_page.py +16 -25
  61. angr/storage/memory_mixins/paged_memory/pages/permissions_mixin.py +11 -9
  62. angr/storage/memory_mixins/paged_memory/pages/ultra_page.py +23 -7
  63. angr/storage/memory_mixins/paged_memory/privileged_mixin.py +1 -1
  64. angr/storage/memory_mixins/regioned_memory/region_meta_mixin.py +9 -7
  65. angr/storage/memory_mixins/regioned_memory/regioned_memory_mixin.py +9 -9
  66. angr/storage/memory_mixins/regioned_memory/static_find_mixin.py +1 -0
  67. angr/storage/memory_mixins/simple_interface_mixin.py +2 -2
  68. angr/storage/memory_mixins/simplification_mixin.py +2 -2
  69. angr/storage/memory_mixins/size_resolution_mixin.py +1 -1
  70. angr/storage/memory_mixins/slotted_memory.py +3 -3
  71. angr/storage/memory_mixins/smart_find_mixin.py +1 -0
  72. angr/storage/memory_mixins/underconstrained_mixin.py +5 -5
  73. angr/storage/memory_mixins/unwrapper_mixin.py +4 -4
  74. angr/storage/memory_object.py +4 -3
  75. angr/utils/constants.py +1 -1
  76. angr/utils/graph.py +15 -0
  77. angr/vaults.py +2 -2
  78. {angr-9.2.123.dist-info → angr-9.2.124.dist-info}/METADATA +6 -6
  79. {angr-9.2.123.dist-info → angr-9.2.124.dist-info}/RECORD +83 -82
  80. {angr-9.2.123.dist-info → angr-9.2.124.dist-info}/WHEEL +1 -1
  81. {angr-9.2.123.dist-info → angr-9.2.124.dist-info}/LICENSE +0 -0
  82. {angr-9.2.123.dist-info → angr-9.2.124.dist-info}/entry_points.txt +0 -0
  83. {angr-9.2.123.dist-info → angr-9.2.124.dist-info}/top_level.txt +0 -0
@@ -13,7 +13,7 @@ l = logging.getLogger(__name__)
13
13
 
14
14
  class DefaultFillerMixin(MemoryMixin):
15
15
  def _default_value(
16
- self, addr, size, name=None, inspect=True, events=True, key=None, fill_missing: bool = True, **kwargs
16
+ self, addr, size, *, name=None, inspect=True, events=True, key=None, fill_missing: bool = True, **kwargs
17
17
  ):
18
18
  if self.state.project and self.state.project.concrete_target:
19
19
  mem = self.state.project.concrete_target.read_memory(addr, size)
@@ -115,7 +115,7 @@ class SpecialFillerMixin(MemoryMixin):
115
115
  super().__init__(**kwargs)
116
116
  self._special_memory_filler = special_memory_filler
117
117
 
118
- def _default_value(self, addr, size, name=None, **kwargs):
118
+ def _default_value(self, addr, size, *, name=None, **kwargs):
119
119
  if (
120
120
  options.SPECIAL_MEMORY_FILL in self.state.options
121
121
  and self.state._special_memory_filler is not None
@@ -135,7 +135,7 @@ class ExplicitFillerMixin(MemoryMixin):
135
135
  super().__init__(**kwargs)
136
136
  self._uninitialized_read_handler = uninitialized_read_handler
137
137
 
138
- def _default_value(self, addr, size, inspect=True, events=True, **kwargs):
138
+ def _default_value(self, addr, size, *, inspect=True, events=True, **kwargs):
139
139
  if self._uninitialized_read_handler is not None:
140
140
  return self._uninitialized_read_handler(addr, size, inspect=inspect, events=events)
141
141
  return super()._default_value(addr, size, inspect=inspect, events=events, **kwargs)
@@ -1,7 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from collections.abc import Iterable
4
- from typing import Any
4
+ from typing import Any, Generic, TypeVar
5
+ from typing_extensions import Self
5
6
 
6
7
  import claripy
7
8
 
@@ -9,33 +10,44 @@ from angr.errors import SimMemoryError
9
10
  from angr.state_plugins.plugin import SimStatePlugin
10
11
 
11
12
 
12
- class MemoryMixin(SimStatePlugin):
13
+ InData = TypeVar("InData")
14
+ OutData = TypeVar("OutData")
15
+ Addr = TypeVar("Addr")
16
+
17
+
18
+ class MemoryMixin(Generic[InData, OutData, Addr], SimStatePlugin):
13
19
  """
14
- Base class for memory mixins. In angr, all memory objects are made by
15
- subclassing one or more MemoryMixins, each adding some functionality to the
16
- memory object.
20
+ MemoryMixin is the base class for the memory model in angr. It provides a
21
+ set of methods that should be implemented by memory models. This is done
22
+ using mixins, where each mixin handles some specific feature of the memory
23
+ model, only overriding methods that it needs to implement its function. The
24
+ memory model class itself then combines a set of mixins using inheritence to
25
+ form the final memory model class.
17
26
  """
18
27
 
19
- SUPPORTS_CONCRETE_LOAD = False
28
+ # pylint: disable=no-self-use,unused-argument
20
29
 
21
- def __init__(self, memory_id=None, endness="Iend_BE"):
30
+ SUPPORTS_CONCRETE_LOAD: bool = False
31
+
32
+ def __init__(self, memory_id: str | None = None, endness: str = "Iend_BE"):
22
33
  super().__init__()
23
34
  self.id = memory_id
24
35
  self.endness = endness
25
36
 
26
- def copy(self, memo): # pylint:disable=unused-argument
37
+ def copy(self, memo) -> Self:
27
38
  o = type(self).__new__(type(self))
28
39
  o.id = self.id
29
40
  o.endness = self.endness
30
41
  return o
31
42
 
32
43
  @property
33
- def category(self):
44
+ def category(self) -> str:
34
45
  """
35
46
  Return the category of this SimMemory instance. It can be one of the three following categories: reg, mem,
36
47
  or file.
37
48
  """
38
49
 
50
+ assert isinstance(self.id, str)
39
51
  if self.id in ("reg", "mem"):
40
52
  return self.id
41
53
 
@@ -48,46 +60,46 @@ class MemoryMixin(SimStatePlugin):
48
60
  raise SimMemoryError(f'Unknown SimMemory category for memory_id "{self.id}"')
49
61
 
50
62
  @property
51
- def variable_key_prefix(self):
63
+ def variable_key_prefix(self) -> tuple[Any, ...]:
52
64
  s = self.category
53
65
  if s == "file":
54
66
  return (s, self.id)
55
67
  return (s,)
56
68
 
57
- def find(self, addr, data, max_search, **kwargs):
58
- pass
69
+ def find(
70
+ self, addr: Addr, data: InData, max_search: int, **kwargs
71
+ ) -> tuple[Addr, list[claripy.ast.Bool], list[int]]: ...
59
72
 
60
- def _add_constraints(self, c, add_constraints=True, condition=None, **kwargs): # pylint:disable=unused-argument
73
+ def _add_constraints(self, c, *, add_constraints=True, condition=None, **kwargs):
61
74
  if add_constraints:
62
75
  to_add = c & condition | ~condition if condition is not None else c
63
76
  self.state.add_constraints(to_add)
64
77
 
65
- def load(self, addr, size=None, **kwargs):
66
- pass
78
+ def load(self, addr: Addr, size: InData | None = None, **kwargs) -> OutData: ...
67
79
 
68
- def store(self, addr, data, **kwargs):
69
- pass
80
+ def store(self, addr: Addr, data: InData, size: InData | None = None, **kwargs) -> None: ...
70
81
 
71
- def merge(self, others, merge_conditions, common_ancestor=None) -> bool:
72
- pass
82
+ def merge(
83
+ self, others: list[Self], merge_conditions: list[claripy.ast.Bool], common_ancestor: Self | None = None
84
+ ) -> bool: ...
73
85
 
74
- def compare(self, other) -> bool:
75
- pass
86
+ def compare(self, other: Self) -> bool: ...
76
87
 
77
- def widen(self, others):
78
- pass
88
+ def widen(self, others: list[Self]) -> bool: ...
79
89
 
80
- def permissions(self, addr, permissions=None, **kwargs):
81
- pass
90
+ def permissions(self, addr: Addr, permissions: int | claripy.ast.BV | None = None, **kwargs) -> claripy.ast.BV: ...
82
91
 
83
- def map_region(self, addr, length, permissions, init_zero=False, **kwargs):
92
+ def map_region(
93
+ self, addr: Addr, length: int, permissions: int | claripy.ast.BV, *, init_zero: bool = False, **kwargs
94
+ ):
84
95
  pass
85
96
 
86
- def unmap_region(self, addr, length, **kwargs):
97
+ def unmap_region(self, addr: Addr, length: int, **kwargs):
87
98
  pass
88
99
 
89
100
  # Optional interface:
90
- def concrete_load(self, addr, size, writing=False, **kwargs) -> memoryview:
101
+ # TODO type this correctly. maybe split the bitmap version into another function?
102
+ def concrete_load(self, addr, size, writing=False, **kwargs) -> Any:
91
103
  """
92
104
  Set SUPPORTS_CONCRETE_LOAD to True and implement concrete_load if reading concrete bytes is faster in this
93
105
  memory model.
@@ -99,7 +111,7 @@ class MemoryMixin(SimStatePlugin):
99
111
  """
100
112
  raise NotImplementedError
101
113
 
102
- def erase(self, addr, size=None, **kwargs) -> None:
114
+ def erase(self, addr: Addr, size: int | None = None, **kwargs) -> None:
103
115
  """
104
116
  Set [addr:addr+size) to uninitialized. In many cases this will be faster than overwriting those locations with
105
117
  new values. This is commonly used during static data flow analysis.
@@ -110,9 +122,7 @@ class MemoryMixin(SimStatePlugin):
110
122
  """
111
123
  raise NotImplementedError
112
124
 
113
- def _default_value( # pylint:disable=too-many-positional-arguments
114
- self, addr, size, name=None, inspect=True, events=True, key=None, **kwargs
115
- ):
125
+ def _default_value(self, addr, size, *, name=None, inspect=True, events=True, key=None, **kwargs) -> OutData:
116
126
  """
117
127
  Override this method to provide default values for a variety of edge cases and base cases.
118
128
 
@@ -123,8 +133,9 @@ class MemoryMixin(SimStatePlugin):
123
133
 
124
134
  The ``inspect``, ``events``, and ``key`` parameters are for ``state.solver.Unconstrained``, if it is used.
125
135
  """
136
+ raise NotImplementedError
126
137
 
127
- def _merge_values(self, values: Iterable[tuple[Any, Any]], merged_size: int, **kwargs) -> Any | None:
138
+ def _merge_values(self, values: Iterable[tuple[Any, Any]], merged_size: int, **kwargs) -> OutData | None:
128
139
  """
129
140
  Override this method to provide value merging support.
130
141
 
@@ -149,7 +160,7 @@ class MemoryMixin(SimStatePlugin):
149
160
  def _replace_all(self, addrs: Iterable[int], old: claripy.ast.BV, new: claripy.ast.BV):
150
161
  raise NotImplementedError
151
162
 
152
- def copy_contents(self, dst, src, size, condition=None, **kwargs):
163
+ def copy_contents(self, dst: Addr, src: Addr, size: InData, condition: claripy.ast.Bool | None = None, **kwargs):
153
164
  """
154
165
  Override this method to provide faster copying of large chunks of data.
155
166
 
@@ -7,8 +7,6 @@ import logging
7
7
  import claripy
8
8
  import cle
9
9
 
10
- from .paged_memory_mixin import PagedMemoryMixin
11
-
12
10
  l = logging.getLogger(__name__)
13
11
 
14
12
  BackerType = Union[bytes, bytearray, list[int]]
@@ -29,6 +27,9 @@ class NotMemoryview:
29
27
  memoryview(self.obj)[self.offset : self.offset + self.size][k] = v
30
28
 
31
29
 
30
+ from .paged_memory_mixin import PagedMemoryMixin
31
+
32
+
32
33
  class ClemoryBackerMixin(PagedMemoryMixin):
33
34
  def __init__(self, cle_memory_backer: None | cle.Loader | cle.Clemory = None, **kwargs):
34
35
  super().__init__(**kwargs)
@@ -49,9 +50,9 @@ class ClemoryBackerMixin(PagedMemoryMixin):
49
50
  o._cle_loader = self._cle_loader
50
51
  return o
51
52
 
52
- def _initialize_page(self, pageno, force_default=False, **kwargs):
53
+ def _initialize_page(self, pageno, permissions=None, *, force_default=False, **kwargs):
53
54
  if self._clemory_backer is None or force_default:
54
- return super()._initialize_page(pageno, **kwargs)
55
+ return super()._initialize_page(pageno, permissions=permissions, **kwargs)
55
56
 
56
57
  addr = pageno * self.page_size
57
58
 
@@ -59,10 +60,10 @@ class ClemoryBackerMixin(PagedMemoryMixin):
59
60
  backer_iter: BackerIterType = self._clemory_backer.backers(addr)
60
61
  backer_start, backer = next(backer_iter)
61
62
  except StopIteration:
62
- return super()._initialize_page(pageno, **kwargs)
63
+ return super()._initialize_page(pageno, permissions=permissions, **kwargs)
63
64
 
64
65
  if backer_start >= addr + self.page_size:
65
- return super()._initialize_page(pageno, **kwargs)
66
+ return super()._initialize_page(pageno, permissions=permissions, **kwargs)
66
67
 
67
68
  # Load data from backere
68
69
  data = self._data_from_backer(addr, backer, backer_start, backer_iter)
@@ -184,9 +185,9 @@ class ClemoryBackerMixin(PagedMemoryMixin):
184
185
 
185
186
 
186
187
  class ConcreteBackerMixin(ClemoryBackerMixin):
187
- def _initialize_page(self, pageno, force_default=False, **kwargs):
188
+ def _initialize_page(self, pageno, permissions=None, *, force_default=False, **kwargs):
188
189
  if self._clemory_backer is None or force_default:
189
- return super()._initialize_page(pageno, **kwargs)
190
+ return super()._initialize_page(pageno, permissions=permissions, **kwargs)
190
191
 
191
192
  addr = pageno * self.page_size
192
193
 
@@ -194,10 +195,10 @@ class ConcreteBackerMixin(ClemoryBackerMixin):
194
195
  backer_iter = self._clemory_backer.backers(addr)
195
196
  backer_start, backer = next(backer_iter)
196
197
  except StopIteration:
197
- return super()._initialize_page(pageno, **kwargs)
198
+ return super()._initialize_page(pageno, permissions=permissions, **kwargs)
198
199
 
199
200
  if backer_start >= addr + self.page_size:
200
- return super()._initialize_page(pageno, **kwargs)
201
+ return super()._initialize_page(pageno, permissions=permissions, **kwargs)
201
202
 
202
203
  if self.state.project.concrete_target:
203
204
  l.debug("Fetching data from concrete target")
@@ -207,7 +208,7 @@ class ConcreteBackerMixin(ClemoryBackerMixin):
207
208
  )
208
209
  else:
209
210
  # the concrete backer only is here to support concrete loading, defer back to the CleMemoryBacker
210
- return super()._initialize_page(pageno, **kwargs)
211
+ return super()._initialize_page(pageno, permissions=permissions, **kwargs)
211
212
 
212
213
  permissions = self._cle_permissions_lookup(addr)
213
214
 
@@ -237,11 +238,11 @@ class DictBackerMixin(PagedMemoryMixin):
237
238
  o._dict_memory_backer = self._dict_memory_backer
238
239
  return o
239
240
 
240
- def _initialize_page(self, pageno: int, force_default=False, **kwargs):
241
+ def _initialize_page(self, pageno: int, permissions=None, *, force_default=False, **kwargs):
241
242
  page_addr = pageno * self.page_size
242
243
 
243
244
  if self._dict_memory_backer is None or force_default:
244
- return super()._initialize_page(pageno, **kwargs)
245
+ return super()._initialize_page(pageno, permissions=permissions, **kwargs)
245
246
 
246
247
  new_page = None
247
248
 
@@ -261,6 +262,6 @@ class DictBackerMixin(PagedMemoryMixin):
261
262
  )
262
263
 
263
264
  if new_page is None:
264
- return super()._initialize_page(pageno, **kwargs)
265
+ return super()._initialize_page(pageno, permissions=permissions, **kwargs)
265
266
 
266
267
  return new_page
@@ -1,12 +1,13 @@
1
1
  from __future__ import annotations
2
2
  import cffi
3
- from typing import Any
3
+ from typing import Any, Generic, Literal, overload
4
4
  from collections.abc import Iterable
5
5
  import logging
6
6
  from collections import defaultdict
7
7
 
8
8
  import claripy
9
9
 
10
+ from angr.state_plugins.sim_action_object import SimActionObject
10
11
  from angr.storage.memory_mixins.memory_mixin import MemoryMixin
11
12
  from angr.storage.memory_mixins.paged_memory.pages import PageType, ListPage, UltraPage, MVListPage
12
13
  from angr.errors import SimMemoryError
@@ -17,14 +18,17 @@ ffi = cffi.FFI()
17
18
  l = logging.getLogger(__name__)
18
19
 
19
20
 
20
- class PagedMemoryMixin(MemoryMixin):
21
+ class PagedMemoryMixin(
22
+ Generic[PageType],
23
+ MemoryMixin[int | claripy.ast.BV | SimActionObject, claripy.ast.BV, int | claripy.ast.BV | SimActionObject],
24
+ ):
21
25
  """
22
26
  A bottom-level storage mechanism. Dispatches reads to individual pages, the type of which is the PAGE_TYPE class
23
27
  variable.
24
28
  """
25
29
 
26
30
  SUPPORTS_CONCRETE_LOAD = True
27
- PAGE_TYPE: type[PageType] = None # must be provided in subclass
31
+ PAGE_TYPE: type[PageType] # must be provided in subclass
28
32
 
29
33
  def __init__(self, page_size=0x1000, default_permissions=3, permissions_map=None, page_kwargs=None, **kwargs):
30
34
  super().__init__(**kwargs)
@@ -84,7 +88,7 @@ class PagedMemoryMixin(MemoryMixin):
84
88
  kwargs["allow_default"] = True
85
89
  return PagedMemoryMixin._initialize_page(self, pageno, permissions=permissions, **kwargs)
86
90
 
87
- def _initialize_page(self, pageno: int, permissions=None, allow_default=True, **kwargs) -> PageType:
91
+ def _initialize_page(self, pageno: int, permissions=None, *, allow_default=True, **kwargs) -> PageType:
88
92
  if not allow_default:
89
93
  raise SimMemoryError("I have been instructed not to create a default page")
90
94
 
@@ -110,7 +114,7 @@ class PagedMemoryMixin(MemoryMixin):
110
114
  def _divide_addr(self, addr: int) -> tuple[int, int]:
111
115
  return divmod(addr, self.page_size)
112
116
 
113
- def load(self, addr: int, size: int | None = None, endness=None, **kwargs):
117
+ def load(self, addr: int, size: int | None = None, *, endness=None, **kwargs):
114
118
  if endness is None:
115
119
  endness = self.endness
116
120
 
@@ -164,7 +168,7 @@ class PagedMemoryMixin(MemoryMixin):
164
168
  l.debug("%s.load(%#x, %d, %s) = %s", self.id, addr, size, endness, out)
165
169
  return out
166
170
 
167
- def store(self, addr: int, data, size: int | None = None, endness=None, **kwargs):
171
+ def store(self, addr: int, data, size: int | None = None, *, endness=None, **kwargs):
168
172
  if endness is None:
169
173
  endness = self.endness
170
174
 
@@ -253,7 +257,7 @@ class PagedMemoryMixin(MemoryMixin):
253
257
  pageno = (pageno + 1) % max_pageno
254
258
  pageoff = 0
255
259
 
256
- def merge(self, others: Iterable[PagedMemoryMixin], merge_conditions, common_ancestor=None) -> bool:
260
+ def merge(self, others, merge_conditions, common_ancestor=None):
257
261
  changed_pages_and_offsets: dict[int, set[int] | None] = {}
258
262
  for o in others:
259
263
  for changed_page, changed_offsets in self.changed_pages(o).items():
@@ -315,7 +319,7 @@ class PagedMemoryMixin(MemoryMixin):
315
319
  return True
316
320
 
317
321
  def permissions(self, addr, permissions=None, **kwargs):
318
- if type(addr) is not int:
322
+ if not isinstance(addr, int):
319
323
  raise TypeError("addr must be an int in paged memory")
320
324
  pageno, _ = self._divide_addr(addr)
321
325
  try:
@@ -323,15 +327,15 @@ class PagedMemoryMixin(MemoryMixin):
323
327
  except SimMemoryError as e:
324
328
  raise SimMemoryError(f"{addr:#x} is not mapped") from e
325
329
 
326
- if type(permissions) is int:
330
+ if isinstance(permissions, int):
327
331
  permissions = claripy.BVV(permissions, 3)
328
332
 
329
- result = page.permissions
333
+ result = page.permission_bits
330
334
  if permissions is not None:
331
- page.permissions = permissions
335
+ page.permission_bits = permissions
332
336
  return result
333
337
 
334
- def map_region(self, addr, length, permissions, init_zero=False, **kwargs):
338
+ def map_region(self, addr, length, permissions, *, init_zero=False, **kwargs):
335
339
  if type(addr) is not int:
336
340
  raise TypeError("addr must be an int in paged memory")
337
341
  pageno, pageoff = self._divide_addr(addr)
@@ -399,6 +403,11 @@ class PagedMemoryMixin(MemoryMixin):
399
403
  else:
400
404
  return True
401
405
 
406
+ @overload
407
+ def _load_to_memoryview(self, addr, size, with_bitmap: Literal[True]) -> tuple[memoryview, memoryview]: ...
408
+ @overload
409
+ def _load_to_memoryview(self, addr, size, with_bitmap: Literal[False]) -> memoryview: ...
410
+
402
411
  def _load_to_memoryview(self, addr, size, with_bitmap):
403
412
  result = self.load(addr, size, endness="Iend_BE")
404
413
  if result.op == "BVV":
@@ -469,7 +478,7 @@ class PagedMemoryMixin(MemoryMixin):
469
478
  return memoryview(bytes(size)), memoryview(b"\x01" * size)
470
479
  return memoryview(b"")
471
480
 
472
- def concrete_load(self, addr, size, writing=False, with_bitmap=False, **kwargs):
481
+ def concrete_load(self, addr, size, writing=False, *, with_bitmap: bool = False, **kwargs):
473
482
  pageno, offset = self._divide_addr(addr)
474
483
  subsize = min(size, self.page_size - offset)
475
484
  try:
@@ -481,9 +490,11 @@ class PagedMemoryMixin(MemoryMixin):
481
490
 
482
491
  if not page.SUPPORTS_CONCRETE_LOAD:
483
492
  # the page does not support concrete_load
484
- return self._load_to_memoryview(addr, size, with_bitmap)
493
+ if with_bitmap:
494
+ return self._load_to_memoryview(addr, size, True)
495
+ return self._load_to_memoryview(addr, size, False)
485
496
 
486
- data, bitmap = page.concrete_load(offset, subsize, **kwargs)
497
+ data, bitmap = page.concrete_load(offset, subsize, with_bitmap=True, **kwargs)
487
498
  if with_bitmap:
488
499
  return data, bitmap
489
500
 
@@ -513,7 +524,7 @@ class PagedMemoryMixin(MemoryMixin):
513
524
  except (SimMemoryError, AttributeError):
514
525
  break
515
526
  else:
516
- newdata, bitmap = concrete_load(offset, subsize, **kwargs)
527
+ newdata, bitmap = concrete_load(offset, subsize, with_bitmap=True, **kwargs)
517
528
  for i, byte in enumerate(bitmap):
518
529
  if byte != 0:
519
530
  break
@@ -1,26 +1,32 @@
1
1
  from __future__ import annotations
2
- from typing import Any
2
+ from abc import abstractmethod
3
+ from typing import Generic, TypeVar
4
+ from collections.abc import Generator
3
5
 
4
6
  import claripy
5
7
 
6
8
  from angr.storage.memory_object import SimMemoryObject, SimLabeledMemoryObject
7
9
  from .multi_values import MultiValues
8
10
 
11
+ T = TypeVar("T")
9
12
 
10
- class CooperationBase:
13
+
14
+ class CooperationBase(Generic[T]):
11
15
  """
12
16
  Any given subclass of this class which is not a subclass of MemoryMixin should have the property that any subclass
13
17
  it which *is* a subclass of MemoryMixin should all work with the same datatypes
14
18
  """
15
19
 
16
20
  @classmethod
17
- def _compose_objects(cls, objects, size, endness, **kwargs):
21
+ @abstractmethod
22
+ def _compose_objects(cls, objects: list[list[tuple[int, T]]], size: str, endness: str, **kwargs) -> T:
18
23
  """
19
24
  Provide this a list of the result of several load calls, and it will compose them into a single result.
20
25
  """
21
26
 
22
27
  @classmethod
23
- def _decompose_objects(cls, addr, data, endness, **kwargs) -> tuple[Any, int, int]:
28
+ @abstractmethod
29
+ def _decompose_objects(cls, addr, data, endness, **kwargs) -> Generator[tuple[T, int, int], int]:
24
30
  """
25
31
  A bidirectional generator. No idea if this is overengineered. Usage is that you send it a size to use
26
32
  and it yields a tuple of three elements: the object to store for the next n bytes, the base address of the
@@ -28,13 +34,14 @@ class CooperationBase:
28
34
  """
29
35
 
30
36
  @classmethod
31
- def _zero_objects(cls, addr, size, **kwargs):
37
+ @abstractmethod
38
+ def _zero_objects(cls, addr, size, **kwargs) -> Generator[tuple[T, int, int], int]:
32
39
  """
33
40
  Like decompose objects, but with a size to zero-fill instead of explicit data
34
41
  """
35
42
 
36
43
  @classmethod
37
- def _force_store_cooperation(cls, addr, data, size, endness, **kwargs):
44
+ def _force_store_cooperation(cls, addr: int, data: T, size: int, endness: str, **kwargs):
38
45
  if data is not None:
39
46
  sub_gen = cls._decompose_objects(addr, data, endness, **kwargs)
40
47
  else:
@@ -50,7 +57,7 @@ class CooperationBase:
50
57
  return cls._compose_objects([results], size, endness, **kwargs)
51
58
 
52
59
 
53
- class MemoryObjectMixin(CooperationBase):
60
+ class MemoryObjectMixin(CooperationBase[SimMemoryObject]):
54
61
  """
55
62
  Uses SimMemoryObjects in region storage.
56
63
  With this, load will return a list of tuple (address, MO) and store will take a MO.
@@ -123,11 +130,12 @@ class MemoryObjectMixin(CooperationBase):
123
130
  else:
124
131
  memory_object = SimLabeledMemoryObject(data, cur_addr, endness, byte_width=byte_width, label=label)
125
132
 
133
+ next_elem_size_left = 0
134
+ next_elem_index = 0
126
135
  if data.symbolic and data.op == "Concat":
127
136
  next_elem_size_left = data.args[0].size() // 8
128
- next_elem_index = 0
129
137
 
130
- size = yield
138
+ size = yield NotImplemented, NotImplemented, NotImplemented
131
139
  max_size = kwargs.get("max_size", size)
132
140
  while True:
133
141
  if data.symbolic and data.op == "Concat" and data.size() > max_size:
@@ -235,6 +243,7 @@ class MemoryObjectSetMixin(CooperationBase):
235
243
  mv.add_value(start_offset, prev_value)
236
244
  prev_value = ...
237
245
 
246
+ assert next(mv.values(), None) is not None, "MultiValues may not be empty"
238
247
  return mv
239
248
 
240
249
  @classmethod
@@ -14,7 +14,7 @@ class ISPOMixin(MemoryMixin):
14
14
  def set_state(self, state):
15
15
  raise Exception("Cannot set state on this stateless object")
16
16
 
17
- def _default_value(self, *args, memory=None, **kwargs):
17
+ def _default_value(self, *args, memory, **kwargs):
18
18
  try:
19
19
  func = memory._default_value
20
20
  except AttributeError as e:
@@ -22,7 +22,7 @@ class ISPOMixin(MemoryMixin):
22
22
  else:
23
23
  return func(*args, **kwargs)
24
24
 
25
- def _add_constraints(self, *args, memory=None, **kwargs):
25
+ def _add_constraints(self, *args, memory, **kwargs):
26
26
  try:
27
27
  func = memory._add_constraints
28
28
  except AttributeError as e:
@@ -30,7 +30,7 @@ class ISPOMixin(MemoryMixin):
30
30
  else:
31
31
  return func(*args, **kwargs)
32
32
 
33
- def _merge_values(self, *args, memory=None, **kwargs):
33
+ def _merge_values(self, *args, memory, **kwargs):
34
34
  try:
35
35
  func = memory._merge_values
36
36
  except AttributeError as ex:
@@ -38,7 +38,7 @@ class ISPOMixin(MemoryMixin):
38
38
  else:
39
39
  return func(*args, **kwargs)
40
40
 
41
- def _merge_labels(self, *args, memory=None, **kwargs):
41
+ def _merge_labels(self, *args, memory, **kwargs):
42
42
  try:
43
43
  func = memory._merge_labels
44
44
  except AttributeError as ex:
@@ -46,7 +46,7 @@ class ISPOMixin(MemoryMixin):
46
46
  else:
47
47
  return func(*args, **kwargs)
48
48
 
49
- def _update_mappings(self, *args, memory=None, **kwargs):
49
+ def _update_mappings(self, *args, memory, **kwargs):
50
50
  try:
51
51
  func = memory._update_mappings
52
52
  except AttributeError as ex: