angr 9.2.123__py3-none-win_amd64.whl → 9.2.125__py3-none-win_amd64.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 (104) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/__init__.py +9 -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/codecave.py +77 -0
  6. angr/analyses/decompiler/ail_simplifier.py +16 -19
  7. angr/analyses/decompiler/callsite_maker.py +8 -7
  8. angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +24 -2
  9. angr/analyses/decompiler/clinic.py +58 -2
  10. angr/analyses/decompiler/condition_processor.py +10 -3
  11. angr/analyses/decompiler/decompilation_cache.py +2 -0
  12. angr/analyses/decompiler/decompiler.py +54 -8
  13. angr/analyses/decompiler/dephication/graph_vvar_mapping.py +10 -2
  14. angr/analyses/decompiler/dephication/rewriting_engine.py +64 -1
  15. angr/analyses/decompiler/expression_narrower.py +5 -1
  16. angr/analyses/decompiler/optimization_passes/__init__.py +3 -0
  17. angr/analyses/decompiler/optimization_passes/div_simplifier.py +4 -1
  18. angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +13 -0
  19. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +23 -4
  20. angr/analyses/decompiler/optimization_passes/optimization_pass.py +3 -1
  21. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +8 -5
  22. angr/analyses/decompiler/optimization_passes/return_duplicator_high.py +10 -5
  23. angr/analyses/decompiler/optimization_passes/return_duplicator_low.py +18 -7
  24. angr/analyses/decompiler/optimization_passes/switch_default_case_duplicator.py +6 -0
  25. angr/analyses/decompiler/optimization_passes/tag_slicer.py +41 -0
  26. angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +2 -0
  27. angr/analyses/decompiler/peephole_optimizations/const_mull_a_shift.py +2 -0
  28. angr/analyses/decompiler/peephole_optimizations/constant_derefs.py +2 -2
  29. angr/analyses/decompiler/peephole_optimizations/remove_cascading_conversions.py +8 -2
  30. angr/analyses/decompiler/region_identifier.py +36 -0
  31. angr/analyses/decompiler/region_simplifiers/loop.py +2 -8
  32. angr/analyses/decompiler/region_simplifiers/switch_cluster_simplifier.py +9 -3
  33. angr/analyses/decompiler/ssailification/rewriting.py +5 -2
  34. angr/analyses/decompiler/ssailification/rewriting_engine.py +151 -25
  35. angr/analyses/decompiler/ssailification/rewriting_state.py +1 -0
  36. angr/analyses/decompiler/ssailification/ssailification.py +17 -9
  37. angr/analyses/decompiler/ssailification/traversal.py +3 -1
  38. angr/analyses/decompiler/ssailification/traversal_engine.py +35 -8
  39. angr/analyses/decompiler/ssailification/traversal_state.py +1 -0
  40. angr/analyses/decompiler/structured_codegen/c.py +42 -4
  41. angr/analyses/decompiler/structuring/phoenix.py +3 -0
  42. angr/analyses/patchfinder.py +137 -0
  43. angr/analyses/pathfinder.py +282 -0
  44. angr/analyses/propagator/engine_ail.py +10 -3
  45. angr/analyses/reaching_definitions/engine_ail.py +10 -15
  46. angr/analyses/s_propagator.py +16 -9
  47. angr/analyses/s_reaching_definitions/s_rda_view.py +127 -63
  48. angr/analyses/smc.py +159 -0
  49. angr/analyses/variable_recovery/engine_ail.py +14 -0
  50. angr/analyses/variable_recovery/engine_base.py +11 -0
  51. angr/angrdb/models.py +1 -2
  52. angr/engines/light/engine.py +12 -0
  53. angr/engines/vex/heavy/heavy.py +2 -0
  54. angr/exploration_techniques/spiller_db.py +1 -2
  55. angr/knowledge_plugins/__init__.py +2 -0
  56. angr/knowledge_plugins/decompilation.py +45 -0
  57. angr/knowledge_plugins/functions/function.py +4 -0
  58. angr/knowledge_plugins/functions/function_manager.py +18 -9
  59. angr/knowledge_plugins/functions/function_parser.py +1 -1
  60. angr/knowledge_plugins/functions/soot_function.py +1 -0
  61. angr/knowledge_plugins/key_definitions/atoms.py +8 -0
  62. angr/lib/angr_native.dll +0 -0
  63. angr/misc/ux.py +2 -2
  64. angr/procedures/definitions/parse_win32json.py +2 -1
  65. angr/project.py +17 -1
  66. angr/state_plugins/history.py +6 -4
  67. angr/storage/memory_mixins/actions_mixin.py +7 -7
  68. angr/storage/memory_mixins/address_concretization_mixin.py +5 -5
  69. angr/storage/memory_mixins/bvv_conversion_mixin.py +1 -1
  70. angr/storage/memory_mixins/clouseau_mixin.py +3 -3
  71. angr/storage/memory_mixins/conditional_store_mixin.py +3 -3
  72. angr/storage/memory_mixins/default_filler_mixin.py +3 -3
  73. angr/storage/memory_mixins/memory_mixin.py +45 -34
  74. angr/storage/memory_mixins/paged_memory/page_backer_mixins.py +15 -14
  75. angr/storage/memory_mixins/paged_memory/paged_memory_mixin.py +27 -16
  76. angr/storage/memory_mixins/paged_memory/pages/cooperation.py +18 -9
  77. angr/storage/memory_mixins/paged_memory/pages/ispo_mixin.py +5 -5
  78. angr/storage/memory_mixins/paged_memory/pages/multi_values.py +89 -55
  79. angr/storage/memory_mixins/paged_memory/pages/mv_list_page.py +16 -25
  80. angr/storage/memory_mixins/paged_memory/pages/permissions_mixin.py +11 -9
  81. angr/storage/memory_mixins/paged_memory/pages/ultra_page.py +23 -7
  82. angr/storage/memory_mixins/paged_memory/privileged_mixin.py +1 -1
  83. angr/storage/memory_mixins/regioned_memory/region_meta_mixin.py +9 -7
  84. angr/storage/memory_mixins/regioned_memory/regioned_memory_mixin.py +9 -9
  85. angr/storage/memory_mixins/regioned_memory/static_find_mixin.py +1 -0
  86. angr/storage/memory_mixins/simple_interface_mixin.py +2 -2
  87. angr/storage/memory_mixins/simplification_mixin.py +2 -2
  88. angr/storage/memory_mixins/size_resolution_mixin.py +1 -1
  89. angr/storage/memory_mixins/slotted_memory.py +3 -3
  90. angr/storage/memory_mixins/smart_find_mixin.py +1 -0
  91. angr/storage/memory_mixins/underconstrained_mixin.py +5 -5
  92. angr/storage/memory_mixins/unwrapper_mixin.py +4 -4
  93. angr/storage/memory_object.py +4 -3
  94. angr/utils/bits.py +4 -0
  95. angr/utils/constants.py +1 -1
  96. angr/utils/graph.py +15 -0
  97. angr/utils/tagged_interval_map.py +112 -0
  98. angr/vaults.py +2 -2
  99. {angr-9.2.123.dist-info → angr-9.2.125.dist-info}/METADATA +6 -6
  100. {angr-9.2.123.dist-info → angr-9.2.125.dist-info}/RECORD +104 -97
  101. {angr-9.2.123.dist-info → angr-9.2.125.dist-info}/WHEEL +1 -1
  102. {angr-9.2.123.dist-info → angr-9.2.125.dist-info}/LICENSE +0 -0
  103. {angr-9.2.123.dist-info → angr-9.2.125.dist-info}/entry_points.txt +0 -0
  104. {angr-9.2.123.dist-info → angr-9.2.125.dist-info}/top_level.txt +0 -0
@@ -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
 
@@ -15,6 +15,7 @@ class SmartFindMixin(MemoryMixin):
15
15
  addr,
16
16
  data,
17
17
  max_search,
18
+ *,
18
19
  default=None,
19
20
  endness=None,
20
21
  chunk_size=None,
@@ -22,15 +22,15 @@ class UnderconstrainedMixin(MemoryMixin):
22
22
  out._unconstrained_range = self._unconstrained_range
23
23
  return out
24
24
 
25
- def load(self, addr, **kwargs):
25
+ def load(self, addr, size=None, **kwargs):
26
26
  self._constrain_underconstrained_index(addr)
27
- return super().load(addr, **kwargs)
27
+ return super().load(addr, size, **kwargs)
28
28
 
29
- def store(self, addr, data, **kwargs):
29
+ def store(self, addr, data, size=None, **kwargs):
30
30
  self._constrain_underconstrained_index(addr)
31
- super().store(addr, data, **kwargs)
31
+ super().store(addr, data, size, **kwargs)
32
32
 
33
- def _default_value(self, addr, size, name=None, key=None, inspect=True, events=True, **kwargs):
33
+ def _default_value(self, addr, size, *, name=None, key=None, inspect=True, events=True, **kwargs):
34
34
  if o.UNDER_CONSTRAINED_SYMEXEC in self.state.options and type(addr) is int:
35
35
  if self.category == "mem":
36
36
  alloc_depth = self.state.uc_manager.get_alloc_depth(addr)
@@ -8,18 +8,18 @@ class UnwrapperMixin(MemoryMixin):
8
8
  This mixin processes SimActionObjects by passing on their .ast field.
9
9
  """
10
10
 
11
- def store(self, addr, data, size=None, condition=None, **kwargs):
11
+ def store(self, addr, data, size=None, *, condition=None, **kwargs):
12
12
  return super().store(
13
13
  _raw_ast(addr), _raw_ast(data), size=_raw_ast(size), condition=_raw_ast(condition), **kwargs
14
14
  )
15
15
 
16
- def load(self, addr, size=None, condition=None, fallback=None, **kwargs):
16
+ def load(self, addr, size=None, *, condition=None, fallback=None, **kwargs):
17
17
  return super().load(
18
18
  _raw_ast(addr), size=_raw_ast(size), condition=_raw_ast(condition), fallback=_raw_ast(fallback), **kwargs
19
19
  )
20
20
 
21
- def find(self, addr, what, max_search, default=None, **kwargs):
22
- return super().find(_raw_ast(addr), _raw_ast(what), max_search, default=_raw_ast(default), **kwargs)
21
+ def find(self, addr, data, max_search, *, default=None, **kwargs):
22
+ return super().find(_raw_ast(addr), _raw_ast(data), max_search, default=_raw_ast(default), **kwargs)
23
23
 
24
24
  def copy_contents(self, dst, src, size, condition=None, **kwargs):
25
25
  return super().copy_contents(_raw_ast(dst), _raw_ast(src), _raw_ast(size), _raw_ast(condition), **kwargs)
@@ -42,7 +42,7 @@ class SimMemoryObject:
42
42
  raise SimMemoryError("bytes can only be stored big-endian")
43
43
  self._byte_width = byte_width
44
44
  self.base = base
45
- self.object = obj
45
+ self.object: claripy.ast.BV | claripy.ast.FP = obj
46
46
  self.length = obj_bit_size(obj) // self._byte_width if length is None else length
47
47
  self.endness = endness
48
48
  self._concrete_bytes: bytes | None = None
@@ -98,8 +98,9 @@ class SimMemoryObject:
98
98
  return o if allow_concrete else claripy.BVV(o)
99
99
 
100
100
  offset = addr - self.base
101
+ bv_obj = claripy.fpToIEEEBV(self.object) if isinstance(self.object, claripy.ast.FP) else self.object
101
102
  try:
102
- thing = bv_slice(self.object, offset, length, self.endness == "Iend_LE", self._byte_width)
103
+ thing = bv_slice(bv_obj, offset, length, self.endness == "Iend_LE", self._byte_width)
103
104
  except claripy.ClaripyOperationError:
104
105
  # hacks to handle address space wrapping
105
106
  if offset >= 0:
@@ -110,7 +111,7 @@ class SimMemoryObject:
110
111
  offset += 2**64
111
112
  else:
112
113
  raise
113
- thing = bv_slice(self.object, offset, length, self.endness == "Iend_LE", self._byte_width)
114
+ thing = bv_slice(bv_obj, offset, length, self.endness == "Iend_LE", self._byte_width)
114
115
 
115
116
  if self.endness != endness:
116
117
  thing = thing.reversed
angr/utils/bits.py CHANGED
@@ -10,3 +10,7 @@ def truncate_bits(value: int, nbits: int) -> int:
10
10
  if nbits < 0:
11
11
  raise ValueError("nbits must not be negative")
12
12
  return value & (2**nbits - 1)
13
+
14
+
15
+ def ffs(x: int) -> int:
16
+ return (x & -x).bit_length() - 1
angr/utils/constants.py CHANGED
@@ -6,4 +6,4 @@ MAX_POINTSTO_BITS = -1330 * 8
6
6
 
7
7
 
8
8
  def is_alignment_mask(n):
9
- return n in {0xFFFFFFFFFFFFFFE0, 0xFFFFFFFFFFFFFFF0, 0xFFFFFFF0, 0xFFFFFFFC}
9
+ return n in {0xFFFFFFFFFFFFFFE0, 0xFFFFFFFFFFFFFFF0, 0xFFFFFFF0, 0xFFFFFFFC, 0xFFFFFFF8}
angr/utils/graph.py CHANGED
@@ -414,6 +414,7 @@ class Dominators:
414
414
  container_nodes = {}
415
415
 
416
416
  traversed_nodes = set()
417
+ endnode_encountered = False
417
418
  while queue:
418
419
  node = queue.pop()
419
420
 
@@ -433,9 +434,11 @@ class Dominators:
433
434
  # may end with a loop.
434
435
  if self._reverse:
435
436
  # Add an edge between the start node and this node
437
+ endnode_encountered = True
436
438
  new_graph.add_edge(start_node, container_node)
437
439
  else:
438
440
  # Add an edge between our this node and end node
441
+ endnode_encountered = True
439
442
  new_graph.add_edge(container_node, end_node)
440
443
 
441
444
  for s in successors:
@@ -451,6 +454,18 @@ class Dominators:
451
454
  if container_s not in traversed_nodes:
452
455
  queue.append(s)
453
456
 
457
+ if not endnode_encountered:
458
+ # the graph is a circle with no end node. we run it with DFS to identify an end node
459
+ nn = next((nn for nn in networkx.dfs_postorder_nodes(graph) if nn in container_nodes), None)
460
+ if nn is not None:
461
+ if self._reverse:
462
+ new_graph.add_edge(start_node, container_nodes[nn])
463
+ else:
464
+ new_graph.add_edge(container_nodes[nn], end_node)
465
+ else:
466
+ # the graph must be empty - totally unexpected!
467
+ raise RuntimeError("Cannot find any end node candidates in the graph. Is the graph empty?")
468
+
454
469
  if self._reverse:
455
470
  # Add the end node
456
471
  new_graph.add_edge(container_nodes[n], end_node)
@@ -0,0 +1,112 @@
1
+ from __future__ import annotations
2
+ from collections.abc import Iterator
3
+
4
+ from sortedcontainers import SortedDict
5
+
6
+
7
+ class TaggedIntervalMap:
8
+ """
9
+ Catalogs features of intervals.
10
+ """
11
+
12
+ def __init__(self, nbits: int = 0):
13
+ """
14
+ :param nbits: Number of binning bits. Higher values reduce detail. 0 for no binning.
15
+ """
16
+ self._nbits: int = nbits
17
+ self._map: SortedDict = SortedDict() # SortedDict[int, int]
18
+
19
+ @property
20
+ def nbits(self) -> int:
21
+ return self._nbits
22
+
23
+ def add(self, addr: int, size: int, tags: int) -> None:
24
+ """
25
+ Add interval starting at `addr` of `size` bytes.
26
+
27
+ When binning, intervals endpoints are aligned to 2^nbits. Gaps between added intervals are populated with
28
+ implicit intervals having tag value of 0. Overlapping intervals will have tag bits OR'd together.
29
+
30
+ Adjacent intervals in the interval map have unique tags. When intervals are added to the map, any adjacent stops
31
+ with identical tags will be eliminated so the map retains this property.
32
+
33
+ For example: if an interval(addr=0, size=100, tags=1) is added, followed by (100, 100, 1), the resulting
34
+ interval in the map will be (0, 200, 1).
35
+ """
36
+ assert addr >= 0
37
+ assert size >= 0
38
+ assert tags != 0
39
+
40
+ if size == 0:
41
+ return
42
+
43
+ max_bin_offset = (1 << self._nbits) - 1
44
+ mask = ~max_bin_offset
45
+
46
+ start_addr = addr & mask # Round down to bin alignment
47
+ end_addr = (addr + size + max_bin_offset) & mask # Round up to bin alignment
48
+
49
+ if self._is_already_covered(start_addr, end_addr, tags):
50
+ return
51
+
52
+ self._insert_stop(start_addr)
53
+ self._insert_stop(end_addr)
54
+ for affected_addr in self._map.irange(start_addr, end_addr, inclusive=(True, False)):
55
+ self._map[affected_addr] |= tags
56
+ self._eliminate_extraneous_stops(start_addr, end_addr)
57
+
58
+ def _insert_stop(self, addr: int) -> None:
59
+ """
60
+ Insert a new interval stop point at `addr`, if one is not already present in the map. Tags are copied from
61
+ nearest stop before `addr`.
62
+ """
63
+ if addr not in self._map:
64
+ idx = self._map.bisect(addr) - 1
65
+ self._map[addr] = self._map.peekitem(idx)[1] if idx >= 0 else 0
66
+
67
+ def _is_already_covered(self, min_addr: int, end_addr: int, tags: int) -> bool:
68
+ """
69
+ Determine if interval [min_addr, end_addr) is covered by an existing range with identical tags.
70
+ """
71
+ idx = self._map.bisect(min_addr) - 1
72
+ if idx >= 0 and len(self._map) > idx + 1:
73
+ e_addr, e_tags = self._map.peekitem(idx)
74
+ e_addr_next, _ = self._map.peekitem(idx + 1)
75
+ return (e_addr <= min_addr) and (end_addr <= e_addr_next) and (e_tags == tags)
76
+ return False
77
+
78
+ def _eliminate_extraneous_stops(self, min_addr: int, max_addr: int) -> None:
79
+ """
80
+ Canonicalize the map by eliminating adjacent stops with identical tags both inside and directly outside of
81
+ [min_addr, max_addr].
82
+ """
83
+ keys_to_drop = []
84
+ prev_tags = None
85
+ for addr, _, tags in self.irange(min_addr, max_addr):
86
+ if tags == prev_tags:
87
+ keys_to_drop.append(addr)
88
+ else:
89
+ prev_tags = tags
90
+
91
+ for addr in keys_to_drop:
92
+ del self._map[addr]
93
+
94
+ def irange(self, min_addr: int | None = None, max_addr: int | None = None) -> Iterator[tuple[int, int, int]]:
95
+ """
96
+ Iterate over intervals intersecting [min_addr, max_addr], yielding interval (addr, size, tags) tuples. Implicit
97
+ gap intervals (with tags=0) are also returned.
98
+
99
+ :param min_addr: Minimum address (inclusive) to begin iterating from. If None, iterate from start of map.
100
+ :param max_addr: Maximum address (inclusive) to iterate to. If None, iterate to end of map.
101
+ """
102
+ if not self._map:
103
+ return
104
+
105
+ start_idx = 0 if min_addr is None else max(0, self._map.bisect_left(min_addr) - 1)
106
+ stop_idx = None if max_addr is None else (self._map.bisect(max_addr) + 1)
107
+
108
+ start_addr, tags = self._map.peekitem(start_idx)
109
+ for addr in self._map.islice(start_idx + 1, stop_idx):
110
+ yield (start_addr, addr - start_addr, tags)
111
+ tags = self._map[addr]
112
+ start_addr = addr
angr/vaults.py CHANGED
@@ -311,7 +311,7 @@ class VaultShelf(VaultDict):
311
311
 
312
312
  def __init__(self, path=None):
313
313
  self._path = tempfile.mktemp() if path is None else path
314
- s = shelve.open(self._path, protocol=-1)
314
+ s = shelve.open(self._path, protocol=-1) # noqa: SIM115
315
315
  super().__init__(s)
316
316
 
317
317
  def close(self):
@@ -334,7 +334,7 @@ class VaultDirShelf(VaultDict):
334
334
  @contextlib.contextmanager
335
335
  def _locked_shelve(self, shelve_path):
336
336
  with self._lock:
337
- self._dict = shelve.open(shelve_path, protocol=-1)
337
+ self._dict = shelve.open(shelve_path, protocol=-1) # noqa: SIM115
338
338
  yield
339
339
  self._dict.close()
340
340
  self._dict = None