angr 9.2.122__py3-none-manylinux2014_x86_64.whl → 9.2.124__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 (95) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/calling_convention.py +6 -1
  3. angr/analyses/cfg/indirect_jump_resolvers/mips_elf_fast.py +11 -8
  4. angr/analyses/cfg/indirect_jump_resolvers/mips_elf_got.py +2 -2
  5. angr/analyses/decompiler/ail_simplifier.py +38 -342
  6. angr/analyses/decompiler/callsite_maker.py +8 -7
  7. angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +24 -2
  8. angr/analyses/decompiler/clinic.py +30 -3
  9. angr/analyses/decompiler/condition_processor.py +10 -3
  10. angr/analyses/decompiler/decompilation_cache.py +2 -0
  11. angr/analyses/decompiler/decompiler.py +50 -8
  12. angr/analyses/decompiler/dephication/graph_vvar_mapping.py +10 -2
  13. angr/analyses/decompiler/dephication/rewriting_engine.py +65 -2
  14. angr/analyses/decompiler/expression_narrower.py +206 -6
  15. angr/analyses/decompiler/optimization_passes/div_simplifier.py +4 -1
  16. angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +7 -0
  17. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +34 -11
  18. angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +10 -1
  19. angr/analyses/decompiler/optimization_passes/optimization_pass.py +3 -1
  20. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +8 -5
  21. angr/analyses/decompiler/optimization_passes/return_duplicator_high.py +10 -5
  22. angr/analyses/decompiler/optimization_passes/return_duplicator_low.py +18 -7
  23. angr/analyses/decompiler/optimization_passes/switch_default_case_duplicator.py +6 -0
  24. angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +2 -0
  25. angr/analyses/decompiler/peephole_optimizations/const_mull_a_shift.py +75 -42
  26. angr/analyses/decompiler/peephole_optimizations/remove_cascading_conversions.py +8 -2
  27. angr/analyses/decompiler/region_identifier.py +36 -0
  28. angr/analyses/decompiler/region_simplifiers/expr_folding.py +4 -0
  29. angr/analyses/decompiler/region_simplifiers/loop.py +2 -8
  30. angr/analyses/decompiler/region_simplifiers/switch_cluster_simplifier.py +9 -3
  31. angr/analyses/decompiler/sequence_walker.py +20 -4
  32. angr/analyses/decompiler/ssailification/rewriting.py +5 -2
  33. angr/analyses/decompiler/ssailification/rewriting_engine.py +151 -25
  34. angr/analyses/decompiler/ssailification/rewriting_state.py +1 -0
  35. angr/analyses/decompiler/ssailification/ssailification.py +17 -9
  36. angr/analyses/decompiler/ssailification/traversal.py +3 -1
  37. angr/analyses/decompiler/ssailification/traversal_engine.py +35 -8
  38. angr/analyses/decompiler/ssailification/traversal_state.py +1 -0
  39. angr/analyses/decompiler/structured_codegen/c.py +42 -4
  40. angr/analyses/decompiler/structuring/phoenix.py +3 -0
  41. angr/analyses/propagator/engine_ail.py +10 -3
  42. angr/analyses/reaching_definitions/engine_ail.py +10 -15
  43. angr/analyses/s_propagator.py +26 -15
  44. angr/analyses/s_reaching_definitions/s_rda_view.py +127 -63
  45. angr/analyses/variable_recovery/engine_ail.py +14 -0
  46. angr/analyses/variable_recovery/engine_base.py +11 -0
  47. angr/calling_conventions.py +2 -2
  48. angr/engines/light/engine.py +24 -2
  49. angr/engines/soot/expressions/instanceOf.py +4 -1
  50. angr/engines/successors.py +1 -1
  51. angr/engines/vex/heavy/concretizers.py +47 -47
  52. angr/engines/vex/heavy/dirty.py +4 -4
  53. angr/knowledge_plugins/__init__.py +2 -0
  54. angr/knowledge_plugins/decompilation.py +45 -0
  55. angr/knowledge_plugins/key_definitions/atoms.py +8 -0
  56. angr/procedures/definitions/parse_win32json.py +2 -1
  57. angr/procedures/java_lang/getsimplename.py +4 -1
  58. angr/procedures/linux_kernel/iovec.py +5 -2
  59. angr/sim_type.py +3 -1
  60. angr/storage/memory_mixins/actions_mixin.py +7 -7
  61. angr/storage/memory_mixins/address_concretization_mixin.py +5 -5
  62. angr/storage/memory_mixins/bvv_conversion_mixin.py +1 -1
  63. angr/storage/memory_mixins/clouseau_mixin.py +3 -3
  64. angr/storage/memory_mixins/conditional_store_mixin.py +3 -3
  65. angr/storage/memory_mixins/default_filler_mixin.py +3 -3
  66. angr/storage/memory_mixins/memory_mixin.py +45 -34
  67. angr/storage/memory_mixins/paged_memory/page_backer_mixins.py +15 -14
  68. angr/storage/memory_mixins/paged_memory/paged_memory_mixin.py +27 -16
  69. angr/storage/memory_mixins/paged_memory/pages/cooperation.py +18 -9
  70. angr/storage/memory_mixins/paged_memory/pages/ispo_mixin.py +5 -5
  71. angr/storage/memory_mixins/paged_memory/pages/multi_values.py +89 -55
  72. angr/storage/memory_mixins/paged_memory/pages/mv_list_page.py +16 -25
  73. angr/storage/memory_mixins/paged_memory/pages/permissions_mixin.py +11 -9
  74. angr/storage/memory_mixins/paged_memory/pages/ultra_page.py +23 -7
  75. angr/storage/memory_mixins/paged_memory/privileged_mixin.py +1 -1
  76. angr/storage/memory_mixins/regioned_memory/region_meta_mixin.py +9 -7
  77. angr/storage/memory_mixins/regioned_memory/regioned_memory_mixin.py +9 -9
  78. angr/storage/memory_mixins/regioned_memory/static_find_mixin.py +1 -0
  79. angr/storage/memory_mixins/simple_interface_mixin.py +2 -2
  80. angr/storage/memory_mixins/simplification_mixin.py +2 -2
  81. angr/storage/memory_mixins/size_resolution_mixin.py +1 -1
  82. angr/storage/memory_mixins/slotted_memory.py +3 -3
  83. angr/storage/memory_mixins/smart_find_mixin.py +1 -0
  84. angr/storage/memory_mixins/underconstrained_mixin.py +5 -5
  85. angr/storage/memory_mixins/unwrapper_mixin.py +4 -4
  86. angr/storage/memory_object.py +4 -3
  87. angr/utils/constants.py +1 -1
  88. angr/utils/graph.py +15 -0
  89. angr/vaults.py +2 -2
  90. {angr-9.2.122.dist-info → angr-9.2.124.dist-info}/METADATA +7 -6
  91. {angr-9.2.122.dist-info → angr-9.2.124.dist-info}/RECORD +95 -94
  92. {angr-9.2.122.dist-info → angr-9.2.124.dist-info}/WHEEL +1 -1
  93. {angr-9.2.122.dist-info → angr-9.2.124.dist-info}/LICENSE +0 -0
  94. {angr-9.2.122.dist-info → angr-9.2.124.dist-info}/entry_points.txt +0 -0
  95. {angr-9.2.122.dist-info → angr-9.2.124.dist-info}/top_level.txt +0 -0
@@ -1,13 +1,17 @@
1
1
  from __future__ import annotations
2
2
  from collections.abc import Iterator
3
+ from typing import Any, Generic, TypeGuard, TypeVar, cast
3
4
  import archinfo
4
5
 
5
6
  import claripy
6
7
 
8
+ from angr.errors import AngrTypeError
7
9
  from angr.storage.memory_object import bv_slice
8
10
 
11
+ MVType = TypeVar("MVType", bound=claripy.ast.BV | claripy.ast.FP)
9
12
 
10
- class MultiValues:
13
+
14
+ class MultiValues(Generic[MVType]):
11
15
  """
12
16
  Represents a byte vector where each byte can have one or multiple values.
13
17
 
@@ -20,27 +24,31 @@ class MultiValues:
20
24
  "_single_value",
21
25
  )
22
26
 
23
- _single_value: claripy.ast.Bits | None
24
- _values: dict[int, set[claripy.ast.Bits]] | None
27
+ _single_value: MVType | None
28
+ _values: dict[int, set[MVType]] | None
25
29
 
26
30
  def __init__(
27
31
  self,
28
- v: claripy.ast.Bits | MultiValues | None | dict[int, set[claripy.ast.Bits]] = None,
29
- offset_to_values=None,
32
+ v: MVType | MultiValues[MVType] | None | dict[int, set[MVType]] = None,
33
+ offset_to_values: dict[int, set[MVType]] | None = None,
30
34
  ):
31
35
  if v is not None and offset_to_values is not None:
32
36
  raise TypeError("You cannot specify v and offset_to_values at the same time!")
33
37
 
34
- self._single_value = (
35
- None
36
- if v is None
37
- else v if isinstance(v, claripy.ast.Bits) else v._single_value if isinstance(v, MultiValues) else None
38
- )
39
- self._values = (
40
- offset_to_values
41
- if offset_to_values is not None
42
- else v if isinstance(v, dict) else v._values if isinstance(v, MultiValues) else None
43
- )
38
+ if v is None:
39
+ self._single_value = None
40
+ self._values = offset_to_values
41
+ elif isinstance(v, (claripy.ast.BV, claripy.ast.FP)):
42
+ self._single_value = v
43
+ self._values = None
44
+ elif isinstance(v, MultiValues):
45
+ self._single_value = v._single_value
46
+ self._values = v._values
47
+ elif isinstance(v, dict):
48
+ self._single_value = None
49
+ self._values = v
50
+ else:
51
+ raise TypeError(type(v))
44
52
 
45
53
  if self._single_value is None and self._values is None:
46
54
  self._values = {}
@@ -56,7 +64,7 @@ class MultiValues:
56
64
  if not isinstance(vs, set):
57
65
  raise TypeError("Each value in offset_to_values must be a set!")
58
66
 
59
- def add_value(self, offset: int, value: claripy.ast.Bits) -> None:
67
+ def add_value(self, offset: int, value: MVType) -> None:
60
68
  if len(value) == 0:
61
69
  return
62
70
  if self._single_value is not None:
@@ -80,32 +88,40 @@ class MultiValues:
80
88
 
81
89
  succ_offset = self._adjacent_offset(offset, before=False)
82
90
  if succ_offset is not None:
83
- value_end = offset + value.length // 8
91
+ value_end = offset + value.size() // 8
84
92
  if value_end > succ_offset:
93
+ if isinstance(value, claripy.ast.FP):
94
+ raise AngrTypeError("Unsupported case. How do we handle floating point values overlapping?")
85
95
  # value is too long. we need to break value into two
86
96
  mid_value_size = succ_offset - offset
87
- remaining_value = value[value.length - mid_value_size * 8 - 1 : 0]
97
+ remaining_value = cast(MVType, value[value.size() - mid_value_size * 8 - 1 : 0])
88
98
  # update value
89
- value = value[value.length - 1 : value.length - mid_value_size * 8]
99
+ value = cast(MVType, value[value.size() - 1 : value.size() - mid_value_size * 8])
90
100
  self.add_value(succ_offset, remaining_value)
91
101
 
92
102
  if self._values[offset]:
93
- curr_value_size = next(iter(self._values[offset])).length // 8
94
- if curr_value_size > value.length // 8:
103
+ curr_value_size = next(iter(self._values[offset])).size() // 8
104
+ if curr_value_size > value.size() // 8:
95
105
  # we need to break existing values
96
106
  new_curr_values = set()
97
107
  remaining_values = set()
98
108
  for v in self._values[offset]:
99
- new_curr_values.add(v[v.length - 1 : v.length - value.length])
100
- remaining_values.add(v[v.length - value.length - 1 : 0])
109
+ if isinstance(v, claripy.ast.FP):
110
+ raise AngrTypeError("Unsupported case. How do we handle floating point values overlapping?")
111
+
112
+ new_curr_values.add(v[v.size() - 1 : v.size() - value.size()])
113
+ remaining_values.add(v[v.size() - value.size() - 1 : 0])
101
114
  self._values[offset] = new_curr_values
102
115
  for v in remaining_values:
103
- self.add_value(offset + value.length // 8, v)
104
- elif curr_value_size < value.length // 8:
116
+ self.add_value(offset + value.size() // 8, v)
117
+ elif curr_value_size < value.size() // 8:
118
+ if isinstance(value, claripy.ast.FP):
119
+ raise AngrTypeError("Unsupported case. How do we handle floating point values overlapping?")
120
+
105
121
  # value is too long. we need to break value into two
106
- remaining_value = value[value.length - curr_value_size * 8 - 1 : 0]
122
+ remaining_value = cast(MVType, value[value.size() - curr_value_size * 8 - 1 : 0])
107
123
  # update value
108
- value = value[value.length - 1 : value.length - curr_value_size * 8]
124
+ value = cast(MVType, value[value.size() - 1 : value.size() - curr_value_size * 8])
109
125
  self.add_value(offset + curr_value_size, remaining_value)
110
126
 
111
127
  self._values[offset].add(value)
@@ -113,26 +129,28 @@ class MultiValues:
113
129
  pre_offset = self._adjacent_offset(offset, before=True)
114
130
  if pre_offset is not None:
115
131
  pre_values = self._values[pre_offset]
116
- pre_values_size = next(iter(pre_values)).length // 8
132
+ pre_values_size = next(iter(pre_values)).size() // 8
117
133
  if pre_offset + pre_values_size > offset:
118
134
  # we need to break the preceding values
119
135
  new_pre_value_size = offset - pre_offset
120
136
  new_pre_values = set()
121
137
  remaining_values = set()
122
138
  for v in pre_values:
123
- new_pre_values.add(v[v.length - 1 : v.length - new_pre_value_size * 8])
124
- remaining_values.add(v[v.length - new_pre_value_size * 8 - 1 : 0])
139
+ if isinstance(v, claripy.ast.FP):
140
+ raise AngrTypeError("Unsupported case. How do we handle floating point values overlapping?")
141
+ new_pre_values.add(v[v.size() - 1 : v.size() - new_pre_value_size * 8])
142
+ remaining_values.add(v[v.size() - new_pre_value_size * 8 - 1 : 0])
125
143
  self._values[pre_offset] = new_pre_values
126
144
  for v in remaining_values:
127
145
  self.add_value(offset, v)
128
146
 
129
- def one_value(self, strip_annotations: bool = False) -> claripy.ast.Bits | None:
147
+ def one_value(self, strip_annotations: bool = False) -> MVType | None:
130
148
  if self._single_value is not None:
131
149
  return self._single_value
132
150
 
133
151
  assert self._values is not None
134
152
 
135
- if len(self._values) == 1 and len(self._values[0]) == 1:
153
+ if len(self._values) == 1 and 0 in self._values and len(self._values[0]) == 1:
136
154
  return next(iter(self._values[0]))
137
155
  if strip_annotations:
138
156
  all_values_wo_annotations = {
@@ -144,7 +162,7 @@ class MultiValues:
144
162
 
145
163
  def __len__(self) -> int:
146
164
  if self._single_value is not None:
147
- return self._single_value.length
165
+ return self._single_value.size()
148
166
 
149
167
  assert self._values is not None
150
168
 
@@ -155,7 +173,7 @@ class MultiValues:
155
173
  max_len = max(x.size() for x in self._values[max_offset])
156
174
  return max_offset * 8 + max_len # FIXME: we are assuming byte_width of 8
157
175
 
158
- def merge(self, mv: MultiValues) -> MultiValues:
176
+ def merge(self, mv: MultiValues[MVType]) -> MultiValues[MVType]:
159
177
  new_values = {k: set(v) for k, v in self.items()}
160
178
  for off, vs in mv.items():
161
179
  if off not in new_values:
@@ -187,47 +205,46 @@ class MultiValues:
187
205
  def __contains__(self, offset: int) -> bool:
188
206
  if self._single_value is not None:
189
207
  return offset == 0
190
- return False if not self._values else offset in self._values
208
+ assert self._values is not None
209
+ return offset in self._values
191
210
 
192
- def __getitem__(self, offset: int) -> set[claripy.ast.Bits]:
211
+ def __getitem__(self, offset: int) -> set[MVType]:
193
212
  if self._single_value is not None:
194
213
  if offset == 0:
195
214
  return {self._single_value}
196
- raise KeyError
197
- if not self._values:
198
- raise KeyError
215
+ raise KeyError(offset)
216
+ assert self._values is not None
199
217
  return self._values[offset]
200
218
 
201
219
  def keys(self) -> set[int]:
202
220
  if self._single_value is not None:
203
221
  return {0}
204
- return set() if not self._values else set(self._values.keys())
222
+ assert self._values is not None
223
+ return set(self._values.keys())
205
224
 
206
- def values(self) -> Iterator[set[claripy.ast.Bits]]:
225
+ def values(self) -> Iterator[set[MVType]]:
207
226
  if self._single_value is not None:
208
227
  yield {self._single_value}
209
228
  else:
210
- if self._values is None:
211
- return
229
+ assert self._values is not None
212
230
  yield from self._values.values()
213
231
 
214
- def items(self) -> Iterator[tuple[int, set[claripy.ast.Bits]]]:
232
+ def items(self) -> Iterator[tuple[int, set[MVType]]]:
215
233
  if self._single_value is not None:
216
234
  yield 0, {self._single_value}
217
235
  else:
218
- if self._values is None:
219
- yield 0, set()
220
- else:
221
- yield from self._values.items()
236
+ assert self._values is not None
237
+ yield from self._values.items()
222
238
 
223
239
  def count(self) -> int:
224
240
  if self._single_value is not None:
225
241
  return 1
226
- if self._values is None:
227
- return 0
242
+ assert self._values is not None
228
243
  return len(self._values)
229
244
 
230
- def extract(self, offset: int, length: int, endness: str) -> MultiValues:
245
+ def extract(
246
+ self: MultiValues[claripy.ast.BV], offset: int, length: int, endness: str
247
+ ) -> MultiValues[claripy.ast.BV]:
231
248
  end = offset + length
232
249
  result = MultiValues(claripy.BVV(b""))
233
250
  for obj_offset, values in self.items():
@@ -240,16 +257,19 @@ class MultiValues:
240
257
 
241
258
  slice_start = max(0, offset - obj_offset)
242
259
  slice_end = min(obj_length, end - obj_offset)
243
- sliced = bv_slice(value, slice_start, slice_end - slice_start, endness == archinfo.Endness.LE, 8)
260
+ bv_value: claripy.ast.BV = claripy.fpToIEEEBV(value) if isinstance(value, claripy.ast.FP) else value
261
+ sliced = bv_slice(bv_value, slice_start, slice_end - slice_start, endness == archinfo.Endness.LE, 8)
244
262
  if len(sliced):
245
263
  result.add_value(max(0, obj_offset - offset), sliced)
246
264
 
247
265
  return result
248
266
 
249
- def concat(self, other: MultiValues | claripy.ast.Bits | bytes) -> MultiValues:
250
- if isinstance(other, bytes):
267
+ def concat(
268
+ self: MultiValues[claripy.ast.BV], other: MultiValues[claripy.ast.BV] | claripy.ast.BV | bytes
269
+ ) -> MultiValues[claripy.ast.BV]:
270
+ if isinstance(other, (bytes, bytearray, memoryview)):
251
271
  other = claripy.BVV(other)
252
- if isinstance(other, claripy.ast.Bits):
272
+ if isinstance(other, claripy.ast.BV):
253
273
  other = MultiValues(other)
254
274
  offset = len(self) // 8
255
275
  result = MultiValues(self)
@@ -283,3 +303,17 @@ class MultiValues:
283
303
  # we missed it...
284
304
  return None
285
305
  return None
306
+
307
+
308
+ def mv_is_bv(mv: MultiValues[Any]) -> TypeGuard[MultiValues[claripy.ast.BV]]:
309
+ if mv._single_value is not None:
310
+ return isinstance(mv._single_value, claripy.ast.BV)
311
+ assert mv._values is not None
312
+ return all(isinstance(value, claripy.ast.BV) for valueset in mv._values.values() for value in valueset)
313
+
314
+
315
+ def mv_is_fp(mv: MultiValues[Any]) -> TypeGuard[MultiValues[claripy.ast.FP]]:
316
+ if mv._single_value is not None:
317
+ return isinstance(mv._single_value, claripy.ast.FP)
318
+ assert mv._values is not None
319
+ return all(isinstance(value, claripy.ast.FP) for valueset in mv._values.values() for value in valueset)
@@ -4,6 +4,7 @@ import logging
4
4
  from typing import Union, Any
5
5
  from collections.abc import Callable
6
6
 
7
+ from angr.storage.memory_mixins.memory_mixin import MemoryMixin
7
8
  from angr.utils.dynamic_dictlist import DynamicDictList
8
9
  from angr.storage.memory_object import SimMemoryObject, SimLabeledMemoryObject
9
10
  from . import PageBase
@@ -20,10 +21,9 @@ class MVListPage(
20
21
  PageBase,
21
22
  ):
22
23
  """
23
- MVListPage allows storing multiple values at the same location, thus allowing weak updates.
24
+ MVListPage allows storing multiple values at the same location.
24
25
 
25
- Each store() may take a value or multiple values, and a "weak" parameter to specify if this store is a weak update
26
- or not.
26
+ Each store() may take a value or multiple values.
27
27
  Each load() returns an iterator of all values stored at that location.
28
28
  """
29
29
 
@@ -104,8 +104,8 @@ class MVListPage(
104
104
  self.stored_offset.add(subaddr)
105
105
  result[-1] = (global_start_addr, new_item)
106
106
 
107
- def store(self, addr, data, size=None, endness=None, memory=None, cooperate=False, weak=False, **kwargs):
108
- super().store(addr, data, size=size, endness=endness, memory=memory, cooperate=cooperate, weak=weak, **kwargs)
107
+ def store(self, addr, data, size=None, endness=None, memory=None, cooperate=False, **kwargs):
108
+ super().store(addr, data, size=size, endness=endness, memory=memory, cooperate=cooperate, **kwargs)
109
109
 
110
110
  if not cooperate:
111
111
  data = self._force_store_cooperation(addr, data, size, endness, memory=memory, **kwargs)
@@ -117,22 +117,12 @@ class MVListPage(
117
117
  self.content = DynamicDictList(max_size=len(self.content))
118
118
  self.stored_offset = set()
119
119
  else:
120
- if not weak:
121
- if len(data) == 1:
122
- # unpack
123
- data: _MOTYPE = next(iter(data))
124
- for subaddr in range(addr, addr + size):
125
- self.content[subaddr] = data
126
- self.stored_offset.add(subaddr)
127
- else:
128
- for subaddr in range(addr, addr + size):
129
- if self.content[subaddr] is None:
130
- self.content[subaddr] = data
131
- elif type(self.content[subaddr]) is set:
132
- self.content[subaddr] |= data
133
- else:
134
- self.content[subaddr] = {self.content[subaddr]} | data
135
- self.stored_offset.add(subaddr)
120
+ if len(data) == 1:
121
+ # unpack
122
+ data: _MOTYPE = next(iter(data))
123
+ for subaddr in range(addr, addr + size):
124
+ self.content[subaddr] = data
125
+ self.stored_offset.add(subaddr)
136
126
 
137
127
  def erase(self, addr, size=None, **kwargs) -> None:
138
128
  for off in range(size):
@@ -143,8 +133,9 @@ class MVListPage(
143
133
  others: list[MVListPage],
144
134
  merge_conditions,
145
135
  common_ancestor=None,
146
- page_addr: int | None = None,
147
- memory=None,
136
+ *,
137
+ page_addr: int,
138
+ memory: MemoryMixin,
148
139
  changed_offsets: set[int] | None = None,
149
140
  ):
150
141
  if changed_offsets is None:
@@ -230,7 +221,7 @@ class MVListPage(
230
221
  # new_object = self._replace_memory_object(our_mo, merged_val, page_addr, memory.page_size)
231
222
 
232
223
  new_mos = {SimMemoryObject(v, mo_base, endness=the_endness) for v in merged_val}
233
- self.store(b, new_mos, size=size, cooperate=True, weak=False)
224
+ self.store(b, new_mos, size=size, cooperate=True)
234
225
 
235
226
  merged_offsets.add(b)
236
227
 
@@ -271,7 +262,7 @@ class MVListPage(
271
262
  continue
272
263
 
273
264
  new_mos = {SimMemoryObject(v, page_addr + b, endness="Iend_BE") for v in merged_val}
274
- self.store(b, new_mos, size=min_size, cooperate=True, weak=False)
265
+ self.store(b, new_mos, size=min_size, cooperate=True)
275
266
  merged_offsets.add(b)
276
267
 
277
268
  self.stored_offset |= merged_offsets
@@ -6,29 +6,31 @@ from angr.storage.memory_mixins.memory_mixin import MemoryMixin
6
6
 
7
7
  class PermissionsMixin(MemoryMixin):
8
8
  """
9
- This mixin adds a permissions field and properties for extracting the read/write/exec permissions. It does NOT add
10
- permissions checking.
9
+ This mixin adds a permissions_bits field and properties for extracting the read/write/exec permissions. It does NOT
10
+ add permissions checking.
11
11
  """
12
12
 
13
- def __init__(self, permissions=None, **kwargs):
13
+ def __init__(self, permissions: int | claripy.ast.BV | None = None, **kwargs):
14
14
  super().__init__(**kwargs)
15
- if type(permissions) is int:
15
+ if permissions is None:
16
+ permissions = 7
17
+ if isinstance(permissions, int):
16
18
  permissions = claripy.BVV(permissions, 3)
17
- self.permissions = permissions
19
+ self.permission_bits = permissions
18
20
 
19
21
  def copy(self, memo):
20
22
  o = super().copy(memo)
21
- o.permissions = self.permissions
23
+ o.permission_bits = self.permission_bits
22
24
  return o
23
25
 
24
26
  @property
25
27
  def perm_read(self):
26
- return self.permissions & 1
28
+ return self.permission_bits & 1
27
29
 
28
30
  @property
29
31
  def perm_write(self):
30
- return self.permissions & 2
32
+ return self.permission_bits & 2
31
33
 
32
34
  @property
33
35
  def perm_exec(self):
34
- return self.permissions & 4
36
+ return self.permission_bits & 4
@@ -343,13 +343,26 @@ class UltraPage(MemoryObjectMixin, PageBase):
343
343
 
344
344
  return merged_offsets
345
345
 
346
- def concrete_load(self, addr, size, **kwargs): # pylint: disable=arguments-differ
347
- if type(self.concrete_data) is bytearray:
348
- return (
349
- memoryview(self.concrete_data)[addr : addr + size],
350
- memoryview(self.symbolic_bitmap)[addr : addr + size],
351
- )
352
- return self.concrete_data[addr : addr + size], memoryview(self.symbolic_bitmap)[addr : addr + size]
346
+ def concrete_load(self, addr, size, writing=False, with_bitmap=False, **kwargs): # pylint: disable=arguments-differ
347
+ assert self.concrete_data is not None
348
+ assert self.symbolic_bitmap is not None
349
+ mv_data = (
350
+ self.concrete_data
351
+ if isinstance(self.concrete_data, (memoryview, NotMemoryview))
352
+ else memoryview(self.concrete_data)
353
+ )
354
+ mv_bitm = (
355
+ self.symbolic_bitmap
356
+ if isinstance(self.symbolic_bitmap, (memoryview, NotMemoryview))
357
+ else memoryview(self.symbolic_bitmap)
358
+ )
359
+ result = (
360
+ mv_data[addr : addr + size],
361
+ mv_bitm[addr : addr + size],
362
+ )
363
+ if with_bitmap:
364
+ return result
365
+ return result[0]
353
366
 
354
367
  def changed_bytes(self, other, page_addr=None) -> set[int]:
355
368
  changed_candidates = super().changed_bytes(other)
@@ -489,3 +502,6 @@ class UltraPage(MemoryObjectMixin, PageBase):
489
502
  self._update_mappings(b, old.object, new.object, memory=memory)
490
503
 
491
504
  return new
505
+
506
+
507
+ from angr.storage.memory_mixins.paged_memory.page_backer_mixins import NotMemoryview
@@ -26,7 +26,7 @@ class PrivilegedPagingMixin(PagedMemoryMixin):
26
26
 
27
27
  return page
28
28
 
29
- def _initialize_page(self, pageno: int, priv: bool = False, **kwargs):
29
+ def _initialize_page(self, pageno: int, *, priv: bool = False, **kwargs):
30
30
  if self.category == "mem" and not priv and o.STRICT_PAGE_ACCESS in self.state.options:
31
31
  raise SimSegfaultException(pageno * self.page_size, "unmapped")
32
32
 
@@ -148,7 +148,7 @@ class MemoryRegionMetaMixin(MemoryMixin):
148
148
 
149
149
  @MemoryMixin.memo
150
150
  def copy(self, memo):
151
- r: MemoryRegionMetaMixin = super().copy(memo)
151
+ r = super().copy(memo)
152
152
  r.alocs = copy.deepcopy(self.alocs)
153
153
  r._related_function_addr = self._related_function_addr
154
154
  r._is_stack = self._is_stack
@@ -187,7 +187,7 @@ class MemoryRegionMetaMixin(MemoryMixin):
187
187
 
188
188
  return ret
189
189
 
190
- def store(self, addr, data, bbl_addr=None, stmt_id=None, ins_addr=None, endness=None, **kwargs):
190
+ def store(self, addr, data, size=None, *, bbl_addr=None, stmt_id=None, ins_addr=None, endness=None, **kwargs):
191
191
  # It comes from a SimProcedure. We'll use bbl_addr as the aloc_id
192
192
  aloc_id = ins_addr if ins_addr is not None else bbl_addr
193
193
 
@@ -195,13 +195,13 @@ class MemoryRegionMetaMixin(MemoryMixin):
195
195
  self.alocs[aloc_id] = AbstractLocation(
196
196
  bbl_addr, stmt_id, self.id, region_offset=addr, size=len(data) // self.state.arch.byte_width
197
197
  )
198
- return super().store(addr, data, endness=endness, **kwargs)
198
+ return super().store(addr, data, size=size, endness=endness, **kwargs)
199
199
  if self.alocs[aloc_id].update(addr, len(data) // self.state.arch.byte_width):
200
- return super().store(addr, data, endness=endness, **kwargs)
201
- return super().store(addr, data, endness=endness, **kwargs)
200
+ return super().store(addr, data, size=size, endness=endness, **kwargs)
201
+ return super().store(addr, data, size=size, endness=endness, **kwargs)
202
202
 
203
203
  def load(
204
- self, addr, size=None, bbl_addr=None, stmt_idx=None, ins_addr=None, **kwargs
204
+ self, addr, size=None, *, bbl_addr=None, stmt_idx=None, ins_addr=None, **kwargs
205
205
  ): # pylint:disable=unused-argument
206
206
  # if bbl_addr is not None and stmt_id is not None:
207
207
  return super().load(addr, size=size, **kwargs)
@@ -228,9 +228,11 @@ class MemoryRegionMetaMixin(MemoryMixin):
228
228
  return r
229
229
 
230
230
  def widen(self, others):
231
+ result = False
231
232
  for other_region in others:
232
233
  self._merge_alocs(other_region)
233
- super().widen([other_region.memory])
234
+ result |= super().widen([other_region.memory])
235
+ return result
234
236
 
235
237
  def dbg_print(self, indent=0):
236
238
  """
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
  import logging
3
3
  from itertools import count
4
4
  from typing import Optional, TYPE_CHECKING
5
- from collections.abc import Generator, Iterable
5
+ from collections.abc import Generator
6
6
 
7
7
  import claripy
8
8
  from claripy.annotation import RegionAnnotation
@@ -74,7 +74,7 @@ class RegionedMemoryMixin(MemoryMixin):
74
74
 
75
75
  @MemoryMixin.memo
76
76
  def copy(self, memo):
77
- o: RegionedMemoryMixin = super().copy(memo)
77
+ o = super().copy(memo)
78
78
  o._write_targets_limit = self._write_targets_limit
79
79
  o._read_targets_limit = self._read_targets_limit
80
80
  o._stack_size = self._stack_size
@@ -91,7 +91,7 @@ class RegionedMemoryMixin(MemoryMixin):
91
91
 
92
92
  return o
93
93
 
94
- def load(self, addr, size: BV | int | None = None, endness=None, condition: Bool | None = None, **kwargs):
94
+ def load(self, addr, size: BV | int | None = None, *, endness=None, condition: Bool | None = None, **kwargs):
95
95
  if isinstance(size, BV) and size.has_annotation_type(RegionAnnotation):
96
96
  _l.critical("load(): size %s is a ValueSet. Something is wrong.", size)
97
97
  if self.state.scratch.ins_addr is not None:
@@ -132,7 +132,7 @@ class RegionedMemoryMixin(MemoryMixin):
132
132
 
133
133
  return val
134
134
 
135
- def store(self, addr, data, size: int | None = None, endness=None, **kwargs): # pylint:disable=unused-argument
135
+ def store(self, addr, data, size: int | None = None, *, endness=None, **kwargs): # pylint:disable=unused-argument
136
136
  regioned_addrs_desc = self._normalize_address(addr)
137
137
  if (
138
138
  regioned_addrs_desc.cardinality >= self._write_targets_limit
@@ -144,7 +144,7 @@ class RegionedMemoryMixin(MemoryMixin):
144
144
  for aw in gen:
145
145
  self._region_store(aw.address, data, aw.region, endness, related_function_addr=aw.function_address)
146
146
 
147
- def merge(self, others: Iterable[RegionedMemoryMixin], merge_conditions, common_ancestor=None) -> bool:
147
+ def merge(self, others, merge_conditions, common_ancestor=None) -> bool:
148
148
  r = False
149
149
  for o in others:
150
150
  for region_id, region in o._regions.items():
@@ -300,7 +300,7 @@ class RegionedMemoryMixin(MemoryMixin):
300
300
  ),
301
301
  )
302
302
 
303
- return self._regions[key].load(addr, size, bbl_addr, stmt_id, ins_addr, **kwargs)
303
+ return self._regions[key].load(addr, size, bbl_addr=bbl_addr, stmt_idx=stmt_id, ins_addr=ins_addr, **kwargs)
304
304
 
305
305
  def _region_store(self, addr, data, key: str, endness, related_function_addr: int | None = None, **kwargs):
306
306
  if key not in self._regions:
@@ -320,9 +320,9 @@ class RegionedMemoryMixin(MemoryMixin):
320
320
  self._regions[key].store(
321
321
  addr,
322
322
  data,
323
- self.state.scratch.bbl_addr,
324
- self.state.scratch.stmt_idx,
325
- self.state.scratch.ins_addr,
323
+ bbl_addr=self.state.scratch.bbl_addr,
324
+ stmt_id=self.state.scratch.stmt_idx,
325
+ ins_addr=self.state.scratch.ins_addr,
326
326
  endness=endness,
327
327
  **kwargs,
328
328
  )
@@ -14,6 +14,7 @@ class StaticFindMixin(SmartFindMixin): # pylint:disable=abstract-method
14
14
  addr,
15
15
  data,
16
16
  max_search,
17
+ *,
17
18
  default=None,
18
19
  endness=None,
19
20
  chunk_size=None,
@@ -7,7 +7,7 @@ from angr.storage.memory_mixins.memory_mixin import MemoryMixin
7
7
 
8
8
 
9
9
  class SimpleInterfaceMixin(MemoryMixin):
10
- def load(self, addr, size=None, endness=None, condition=None, fallback=None, **kwargs):
10
+ def load(self, addr, size=None, *, endness=None, condition=None, fallback=None, **kwargs):
11
11
  tsize = self._translate_size(size, None)
12
12
  return super().load(
13
13
  self._translate_addr(addr),
@@ -18,7 +18,7 @@ class SimpleInterfaceMixin(MemoryMixin):
18
18
  **kwargs,
19
19
  )
20
20
 
21
- def store(self, addr, data, size=None, endness=None, condition=None, **kwargs):
21
+ def store(self, addr, data, size=None, *, endness=None, condition=None, **kwargs):
22
22
  tsize = self._translate_size(size, data)
23
23
  super().store(
24
24
  self._translate_addr(addr),
@@ -5,11 +5,11 @@ from angr.storage.memory_mixins.memory_mixin import MemoryMixin
5
5
 
6
6
 
7
7
  class SimplificationMixin(MemoryMixin):
8
- def store(self, addr, data, **kwargs):
8
+ def store(self, addr, data, size=None, **kwargs):
9
9
  if (self.category == "mem" and options.SIMPLIFY_MEMORY_WRITES in self.state.options) or (
10
10
  self.category == "reg" and options.SIMPLIFY_REGISTER_WRITES in self.state.options
11
11
  ):
12
12
  real_data = self.state.solver.simplify(data)
13
13
  else:
14
14
  real_data = data
15
- super().store(addr, real_data, **kwargs)
15
+ super().store(addr, real_data, size, **kwargs)
@@ -96,7 +96,7 @@ class SizeConcretizationMixin(MemoryMixin):
96
96
  out_size = self.state.solver.max(size)
97
97
  return super().load(addr, size=out_size, **kwargs)
98
98
 
99
- def store(self, addr, data, size=None, condition=None, **kwargs):
99
+ def store(self, addr, data, size=None, *, condition=None, **kwargs):
100
100
  if getattr(size, "op", "BVV") == "BVV":
101
101
  super().store(addr, data, size=size, condition=condition, **kwargs)
102
102
  return
@@ -81,7 +81,7 @@ class SlottedMemoryMixin(MemoryMixin):
81
81
  try:
82
82
  d = self.contents[addr]
83
83
  except KeyError:
84
- d = self._default_value(addr, self.width, (*self.variable_key_prefix, addr), **kwargs)
84
+ d = self._default_value(addr, self.width, key=(*self.variable_key_prefix, addr), **kwargs)
85
85
  self.contents[addr] = d
86
86
 
87
87
  if offset == 0 and size == self.width:
@@ -107,7 +107,7 @@ class SlottedMemoryMixin(MemoryMixin):
107
107
  end = cur.get_bytes(offset + size, self.width - offset - size)
108
108
  self.contents[addr] = start.concat(data, end)
109
109
 
110
- def load(self, addr, size=None, endness=None, **kwargs):
110
+ def load(self, addr, size=None, *, endness=None, **kwargs):
111
111
  accesses = self._resolve_access(addr, size)
112
112
 
113
113
  value = claripy.Concat(*(self._single_load(addr, offset, size) for addr, offset, size in accesses))
@@ -116,7 +116,7 @@ class SlottedMemoryMixin(MemoryMixin):
116
116
 
117
117
  return value
118
118
 
119
- def store(self, addr, data, size=None, endness=None, **kwargs):
119
+ def store(self, addr, data, size=None, *, endness=None, **kwargs):
120
120
  if endness != self.endness:
121
121
  data = data.reversed
122
122