angr 9.2.141__py3-none-win_amd64.whl → 9.2.142__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 (59) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/calling_convention/calling_convention.py +17 -3
  3. angr/analyses/cfg/cfg_base.py +38 -4
  4. angr/analyses/cfg/cfg_fast.py +23 -7
  5. angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +4 -0
  6. angr/analyses/class_identifier.py +8 -7
  7. angr/analyses/complete_calling_conventions.py +1 -1
  8. angr/analyses/decompiler/ail_simplifier.py +61 -46
  9. angr/analyses/decompiler/clinic.py +73 -5
  10. angr/analyses/decompiler/condition_processor.py +7 -7
  11. angr/analyses/decompiler/decompilation_cache.py +2 -1
  12. angr/analyses/decompiler/decompiler.py +10 -2
  13. angr/analyses/decompiler/dephication/graph_vvar_mapping.py +4 -6
  14. angr/analyses/decompiler/optimization_passes/base_ptr_save_simplifier.py +8 -2
  15. angr/analyses/decompiler/optimization_passes/condition_constprop.py +63 -34
  16. angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +1 -1
  17. angr/analyses/decompiler/optimization_passes/optimization_pass.py +2 -0
  18. angr/analyses/decompiler/optimization_passes/register_save_area_simplifier.py +29 -7
  19. angr/analyses/decompiler/optimization_passes/stack_canary_simplifier.py +6 -0
  20. angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +9 -1
  21. angr/analyses/decompiler/region_identifier.py +70 -47
  22. angr/analyses/decompiler/ssailification/rewriting.py +47 -17
  23. angr/analyses/decompiler/ssailification/rewriting_engine.py +13 -0
  24. angr/analyses/decompiler/stack_item.py +36 -0
  25. angr/analyses/decompiler/structured_codegen/c.py +14 -9
  26. angr/analyses/decompiler/structuring/phoenix.py +3 -3
  27. angr/analyses/find_objects_static.py +2 -1
  28. angr/analyses/reaching_definitions/engine_vex.py +13 -0
  29. angr/analyses/reaching_definitions/function_handler.py +24 -10
  30. angr/analyses/reaching_definitions/function_handler_library/stdio.py +1 -0
  31. angr/analyses/reaching_definitions/function_handler_library/stdlib.py +45 -12
  32. angr/analyses/reaching_definitions/function_handler_library/string.py +77 -21
  33. angr/analyses/reaching_definitions/function_handler_library/unistd.py +21 -1
  34. angr/analyses/reaching_definitions/rd_state.py +11 -7
  35. angr/analyses/s_liveness.py +44 -6
  36. angr/analyses/s_reaching_definitions/s_rda_model.py +4 -2
  37. angr/analyses/typehoon/simple_solver.py +35 -8
  38. angr/analyses/typehoon/typehoon.py +3 -1
  39. angr/calling_conventions.py +2 -2
  40. angr/knowledge_plugins/functions/function.py +5 -10
  41. angr/knowledge_plugins/variables/variable_manager.py +27 -0
  42. angr/lib/angr_native.dll +0 -0
  43. angr/procedures/definitions/__init__.py +3 -10
  44. angr/procedures/definitions/wdk_ntoskrnl.py +2 -0
  45. angr/procedures/win32_kernel/__fastfail.py +15 -0
  46. angr/sim_procedure.py +2 -2
  47. angr/simos/simos.py +14 -10
  48. angr/simos/windows.py +42 -1
  49. angr/utils/ail.py +41 -1
  50. angr/utils/cpp.py +17 -0
  51. angr/utils/doms.py +142 -0
  52. angr/utils/library.py +1 -1
  53. angr/utils/types.py +12 -1
  54. {angr-9.2.141.dist-info → angr-9.2.142.dist-info}/METADATA +7 -7
  55. {angr-9.2.141.dist-info → angr-9.2.142.dist-info}/RECORD +59 -55
  56. {angr-9.2.141.dist-info → angr-9.2.142.dist-info}/LICENSE +0 -0
  57. {angr-9.2.141.dist-info → angr-9.2.142.dist-info}/WHEEL +0 -0
  58. {angr-9.2.141.dist-info → angr-9.2.142.dist-info}/entry_points.txt +0 -0
  59. {angr-9.2.141.dist-info → angr-9.2.142.dist-info}/top_level.txt +0 -0
angr/__init__.py CHANGED
@@ -2,7 +2,7 @@
2
2
  # pylint: disable=wrong-import-position
3
3
  from __future__ import annotations
4
4
 
5
- __version__ = "9.2.141"
5
+ __version__ = "9.2.142"
6
6
 
7
7
  if bytes is str:
8
8
  raise Exception(
@@ -165,6 +165,19 @@ class CallingConventionAnalysis(Analysis):
165
165
  ):
166
166
  return
167
167
 
168
+ if (
169
+ hooker is not None
170
+ and hooker.cc is not None
171
+ and hooker.is_function
172
+ and not hooker.guessed_prototype
173
+ and hooker.prototype is not None
174
+ ):
175
+ # copy the calling convention and prototype from the SimProcedure instance
176
+ self.cc = hooker.cc
177
+ self.prototype = hooker.prototype
178
+ self.prototype_libname = hooker.library_name
179
+ return
180
+
168
181
  if self._function.prototype is None:
169
182
  # try our luck
170
183
  # we set ignore_binary_name to True because the binary name SimProcedures is "cle##externs" and does not
@@ -309,9 +322,10 @@ class CallingConventionAnalysis(Analysis):
309
322
  if self.project.is_hooked(real_func.addr):
310
323
  # prioritize the hooker
311
324
  hooker = self.project.hooked_by(real_func.addr)
312
- if hooker is not None and (
313
- not hooker.is_stub or (hooker.is_function and not hooker.guessed_prototype)
314
- ):
325
+ if hooker is not None and hooker.is_function and not hooker.guessed_prototype:
326
+ # we only take the prototype from the SimProcedure if
327
+ # - the SimProcedure is a function
328
+ # - the prototype of the SimProcedure is not guessed
315
329
  return cc, hooker.prototype
316
330
  if real_func.prototype is not None:
317
331
  return cc, real_func.prototype
@@ -11,7 +11,7 @@ import pyvex
11
11
  from cle import ELF, PE, Blob, TLSObject, MachO, ExternObject, KernelObject, FunctionHintSource, Hex, Coff, SRec, XBE
12
12
  from cle.backends import NamedRegion
13
13
  import archinfo
14
- from archinfo.arch_soot import SootAddressDescriptor
14
+ from archinfo.arch_soot import SootAddressDescriptor, SootMethodDescriptor
15
15
  from archinfo.arch_arm import is_arm_arch, get_real_address_if_arm
16
16
 
17
17
  from angr.knowledge_plugins.functions.function_manager import FunctionManager
@@ -129,7 +129,7 @@ class CFGBase(Analysis):
129
129
 
130
130
  # Store all the functions analyzed before the set is cleared
131
131
  # Used for performance optimization
132
- self._updated_nonreturning_functions: set[int] | None = None
132
+ self._updated_nonreturning_functions: set[int | SootMethodDescriptor] | None = None
133
133
 
134
134
  self._normalize = normalize
135
135
 
@@ -246,7 +246,7 @@ class CFGBase(Analysis):
246
246
  )
247
247
 
248
248
  self._regions_size = sum((end - start) for start, end in regions)
249
- self._regions: dict[int, int] = SortedDict(regions)
249
+ self._regions: SortedDict = SortedDict(regions)
250
250
 
251
251
  l.debug("CFG recovery covers %d regions:", len(self._regions))
252
252
  for start, end in self._regions.items():
@@ -1556,6 +1556,7 @@ class CFGBase(Analysis):
1556
1556
  self.kb.functions[func_addr].alignment = True
1557
1557
  continue
1558
1558
  node = function.get_node(block.addr)
1559
+ assert node is not None
1559
1560
  successors = list(function.graph.successors(node))
1560
1561
  if len(successors) == 1 and successors[0].addr == node.addr:
1561
1562
  # self loop. mark this function as a function alignment
@@ -2151,6 +2152,11 @@ class CFGBase(Analysis):
2151
2152
  f = self.kb.functions.function(addr=addr)
2152
2153
  assert f is not None
2153
2154
 
2155
+ # copy over existing metadata
2156
+ if known_functions.contains_addr(addr):
2157
+ kf = known_functions.get_by_addr(addr)
2158
+ f.is_plt = kf.is_plt
2159
+
2154
2160
  blockaddr_to_function[addr] = f
2155
2161
 
2156
2162
  function_is_returning = False
@@ -2532,6 +2538,34 @@ class CFGBase(Analysis):
2532
2538
  # Other functions
2533
2539
  #
2534
2540
 
2541
+ @staticmethod
2542
+ def _is_noop_jump_block(block) -> bool:
2543
+ """
2544
+ Check if the block does nothing but jumping to a constant address.
2545
+
2546
+ :param block: The block instance. We assume the block is already optimized.
2547
+ :return: True if the entire block is a jump to a constant address, False otherwise.
2548
+ """
2549
+
2550
+ vex = block.vex
2551
+ if vex.jumpkind != "Ijk_Boring":
2552
+ return False
2553
+ if isinstance(vex.next, pyvex.expr.Const):
2554
+ return all(isinstance(stmt, pyvex.stmt.IMark) for stmt in vex.statements)
2555
+ if isinstance(vex.next, pyvex.expr.RdTmp):
2556
+ next_tmp = vex.next.tmp
2557
+ return all(
2558
+ isinstance(stmt, pyvex.stmt.IMark)
2559
+ or (
2560
+ isinstance(stmt, pyvex.stmt.WrTmp)
2561
+ and stmt.tmp == next_tmp
2562
+ and isinstance(stmt.data, pyvex.expr.Load)
2563
+ and isinstance(stmt.data.addr, pyvex.expr.Const)
2564
+ )
2565
+ for stmt in vex.statements
2566
+ )
2567
+ return False
2568
+
2535
2569
  @staticmethod
2536
2570
  def _is_noop_block(arch: archinfo.Arch, block) -> bool:
2537
2571
  """
@@ -2755,7 +2789,7 @@ class CFGBase(Analysis):
2755
2789
  cfg_node: CFGNode,
2756
2790
  irsb: pyvex.IRSB,
2757
2791
  func_addr: int,
2758
- stmt_idx: int | str = DEFAULT_STATEMENT,
2792
+ stmt_idx: int = DEFAULT_STATEMENT,
2759
2793
  ) -> tuple[bool, set[int], IndirectJump | None]:
2760
2794
  """
2761
2795
  Called when we encounter an indirect jump. We will try to resolve this indirect jump using timeless (fast)
@@ -1782,7 +1782,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
1782
1782
  self.project.loader.discard_ro_memview()
1783
1783
 
1784
1784
  # Clean up
1785
- self._traced_addresses = None
1785
+ self._traced_addresses = None # type: ignore
1786
1786
  self._lifter_deregister_readonly_regions()
1787
1787
  self._function_returns = None
1788
1788
 
@@ -1838,6 +1838,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
1838
1838
  xrefs = self.kb.xrefs.get_xrefs_by_dst(security_cookie_addr)
1839
1839
  tested_func_addrs = set()
1840
1840
  for xref in xrefs:
1841
+ assert xref.block_addr is not None
1841
1842
  cfg_node = self.model.get_any_node(xref.block_addr)
1842
1843
  if cfg_node is None:
1843
1844
  continue
@@ -2081,13 +2082,20 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
2081
2082
 
2082
2083
  if (
2083
2084
  cfg_job.src_node is not None
2084
- and self.functions.contains_addr(cfg_job.src_node.addr)
2085
- and self.functions[cfg_job.src_node.addr].is_default_name
2086
2085
  and cfg_job.src_node.addr not in self.kb.labels
2087
2086
  and cfg_job.jumpkind == "Ijk_Boring"
2087
+ and self._is_noop_jump_block(cfg_job.src_node.block)
2088
2088
  ):
2089
- # assign a name to the caller function that jumps to this procedure
2090
- self.functions[cfg_job.src_node.addr].name = procedure.display_name
2089
+ # the caller node is very likely to be a PLT stub
2090
+ if not self.functions.contains_addr(cfg_job.src_node.addr):
2091
+ src_func = self.functions.function(addr=cfg_job.src_node.addr, create=True)
2092
+ else:
2093
+ src_func = self.functions.get_by_addr(cfg_job.src_node.addr)
2094
+ if len(src_func.block_addrs_set) <= 1 and src_func.is_default_name:
2095
+ # assign a name to the caller function that jumps to this procedure
2096
+ src_func.name = procedure.display_name
2097
+ # mark it as PLT
2098
+ src_func.is_plt = True
2091
2099
 
2092
2100
  if procedure.ADDS_EXITS:
2093
2101
  # Get two blocks ahead
@@ -3714,7 +3722,12 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
3714
3722
  #
3715
3723
 
3716
3724
  def _graph_add_edge(
3717
- self, cfg_node: CFGNode, src_node: CFGNode | None, src_jumpkind: str, src_ins_addr: int, src_stmt_idx: int
3725
+ self,
3726
+ cfg_node: CFGNode,
3727
+ src_node: CFGNode | None,
3728
+ src_jumpkind: str,
3729
+ src_ins_addr: int | None,
3730
+ src_stmt_idx: int | None,
3718
3731
  ):
3719
3732
  """
3720
3733
  Add edge between nodes, or add node if entry point
@@ -4584,6 +4597,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
4584
4597
  elif (
4585
4598
  lifted_block is not None
4586
4599
  and is_x86_x64_arch
4600
+ and lifted_block.bytes is not None
4587
4601
  and len(lifted_block.bytes) - irsb_size > 2
4588
4602
  and lifted_block.bytes[irsb_size : irsb_size + 2]
4589
4603
  in {
@@ -4659,7 +4673,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
4659
4673
  self._seg_list.occupy(real_addr + irsb_size, nodecode_size, "nodecode")
4660
4674
 
4661
4675
  # Occupy the block in segment list
4662
- if irsb.size > 0:
4676
+ if irsb is not None and irsb.size > 0:
4663
4677
  self._seg_list.occupy(real_addr, irsb.size, "code")
4664
4678
 
4665
4679
  # Create a CFG node, and add it to the graph
@@ -4969,6 +4983,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
4969
4983
 
4970
4984
  for assumption_addr in to_remove:
4971
4985
  # remove this assumption from the graph (since we may have new relationships formed later)
4986
+ assert self._decoding_assumption_relations is not None
4972
4987
  if assumption_addr in self._decoding_assumption_relations:
4973
4988
  self._decoding_assumption_relations.remove_node(assumption_addr)
4974
4989
 
@@ -5159,6 +5174,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
5159
5174
  target_func = edges[0][1]
5160
5175
  if isinstance(target_func, (HookNode, Function)) and self.project.is_hooked(target_func.addr):
5161
5176
  hooker = self.project.hooked_by(target_func.addr)
5177
+ assert hooker is not None
5162
5178
  if hooker.DYNAMIC_RET:
5163
5179
  return self._is_call_returning(callsite_cfgnode, target_func.addr)
5164
5180
 
@@ -183,7 +183,11 @@ class ConstantValueManager:
183
183
  # determine blocks to run FCP on
184
184
 
185
185
  # - include at most three levels of superblock successors from the entrypoint
186
+ self.mapping = {}
186
187
  startpoint = self.func.startpoint
188
+ if startpoint is None:
189
+ return
190
+
187
191
  blocks = set()
188
192
  succ_and_levels = [(startpoint, 0)]
189
193
  while succ_and_levels:
@@ -1,6 +1,8 @@
1
1
  from __future__ import annotations
2
+
2
3
  from angr.sim_type import SimCppClass, SimTypeCppFunction
3
4
  from angr.analyses import AnalysesHub
5
+ from angr.utils.cpp import is_cpp_funcname_ctor
4
6
  from . import Analysis, CFGFast, VtableFinder
5
7
 
6
8
 
@@ -33,17 +35,13 @@ class ClassIdentifier(Analysis):
33
35
  class_name = class_name.removeprefix("non-virtual thunk for ")
34
36
  if col_ind != -1:
35
37
  if class_name not in self.classes:
36
- ctor = False
37
- if func.demangled_name.find("{ctor}"):
38
- ctor = True
38
+ ctor = is_cpp_funcname_ctor(func.demangled_name)
39
39
  function_members = {func.addr: SimTypeCppFunction([], None, label=func.demangled_name, ctor=ctor)}
40
40
  new_class = SimCppClass(name=class_name, function_members=function_members)
41
41
  self.classes[class_name] = new_class
42
42
 
43
43
  else:
44
- ctor = False
45
- if func.demangled_name.find("{ctor}"):
46
- ctor = True
44
+ ctor = is_cpp_funcname_ctor(func.demangled_name)
47
45
  cur_class = self.classes[class_name]
48
46
  cur_class.function_members[func.addr] = SimTypeCppFunction(
49
47
  [], None, label=func.demangled_name, ctor=ctor
@@ -55,7 +53,10 @@ class ClassIdentifier(Analysis):
55
53
  vtable_calling_func = self.project.kb.functions.floor_func(ref.ins_addr)
56
54
  tmp_col_ind = vtable_calling_func.demangled_name.rfind("::")
57
55
  possible_constructor_class_name = vtable_calling_func.demangled_name[:tmp_col_ind]
58
- if "ctor" in vtable_calling_func.demangled_name and possible_constructor_class_name in self.classes:
56
+ if (
57
+ is_cpp_funcname_ctor(vtable_calling_func.demangled_name)
58
+ and possible_constructor_class_name in self.classes
59
+ ):
59
60
  self.classes[possible_constructor_class_name].vtable_ptrs.append(vtable.vaddr)
60
61
 
61
62
 
@@ -383,7 +383,7 @@ class CompleteCallingConventionsAnalysis(Analysis):
383
383
  return (
384
384
  cc_analysis.cc,
385
385
  cc_analysis.prototype,
386
- func.prototype_libname,
386
+ cc_analysis.prototype_libname if cc_analysis.prototype_libname is not None else func.prototype_libname,
387
387
  self.kb.variables.get_function_manager(func_addr),
388
388
  )
389
389
  _l.info("Cannot determine calling convention for %r.", func)
@@ -118,7 +118,7 @@ class AILSimplifier(Analysis):
118
118
  self._should_rewrite_ccalls = rewrite_ccalls
119
119
  self._removed_vvar_ids = removed_vvar_ids if removed_vvar_ids is not None else set()
120
120
  self._arg_vvars = arg_vvars
121
- self._avoid_vvar_ids = avoid_vvar_ids
121
+ self._avoid_vvar_ids = avoid_vvar_ids if avoid_vvar_ids is not None else set()
122
122
  self._propagator_dead_vvar_ids: set[int] = set()
123
123
  self._secondary_stackvars: set[int] = secondary_stackvars if secondary_stackvars is not None else set()
124
124
 
@@ -132,12 +132,10 @@ class AILSimplifier(Analysis):
132
132
  def _simplify(self):
133
133
  if self._narrow_expressions:
134
134
  _l.debug("Removing dead assignments before narrowing expressions")
135
- r = self._remove_dead_assignments()
135
+ r = self._iteratively_remove_dead_assignments()
136
136
  if r:
137
137
  _l.debug("... dead assignments removed")
138
138
  self.simplified = True
139
- self._rebuild_func_graph()
140
- self._clear_cache()
141
139
 
142
140
  _l.debug("Narrowing expressions")
143
141
  narrowed_exprs = self._narrow_exprs()
@@ -170,12 +168,10 @@ class AILSimplifier(Analysis):
170
168
 
171
169
  if self._unify_vars:
172
170
  _l.debug("Removing dead assignments")
173
- r = self._remove_dead_assignments()
171
+ r = self._iteratively_remove_dead_assignments()
174
172
  if r:
175
173
  _l.debug("... dead assignments removed")
176
174
  self.simplified = True
177
- self._rebuild_func_graph()
178
- self._clear_cache()
179
175
 
180
176
  _l.debug("Unifying local variables")
181
177
  r = self._unify_local_variables()
@@ -194,11 +190,10 @@ class AILSimplifier(Analysis):
194
190
  self._clear_cache()
195
191
 
196
192
  _l.debug("Removing dead assignments")
197
- r = self._remove_dead_assignments()
193
+ r = self._iteratively_remove_dead_assignments()
198
194
  if r:
199
195
  _l.debug("... dead assignments removed")
200
196
  self.simplified = True
201
- self._rebuild_func_graph()
202
197
 
203
198
  def _rebuild_func_graph(self):
204
199
  def _handler(node):
@@ -1319,14 +1314,27 @@ class AILSimplifier(Analysis):
1319
1314
 
1320
1315
  return False, None
1321
1316
 
1317
+ def _iteratively_remove_dead_assignments(self) -> bool:
1318
+ anything_removed = False
1319
+ while True:
1320
+ r = self._remove_dead_assignments()
1321
+ if not r:
1322
+ return anything_removed
1323
+ self._rebuild_func_graph()
1324
+ self._clear_cache()
1325
+
1322
1326
  def _remove_dead_assignments(self) -> bool:
1323
1327
 
1324
1328
  # keeping tracking of statements to remove and statements (as well as dead vvars) to keep allows us to handle
1325
- # cases where a statement defines more than one atoms, e.g., a call statement that defines both the return
1329
+ # cases where a statement defines more than one atom, e.g., a call statement that defines both the return
1326
1330
  # value and the floating-point return value.
1327
1331
  stmts_to_remove_per_block: dict[tuple[int, int | None], set[int]] = defaultdict(set)
1328
1332
  stmts_to_keep_per_block: dict[tuple[int, int | None], set[int]] = defaultdict(set)
1329
1333
  dead_vvar_ids: set[int] = set()
1334
+ dead_vvar_codelocs: set[CodeLocation] = set()
1335
+ blocks: dict[tuple[int, int | None], Block] = {
1336
+ (node.addr, node.idx): self.blocks.get(node, node) for node in self.func_graph.nodes()
1337
+ }
1330
1338
 
1331
1339
  # Find all statements that should be removed
1332
1340
  mask = (1 << self.project.arch.bits) - 1
@@ -1335,59 +1343,64 @@ class AILSimplifier(Analysis):
1335
1343
  stackarg_offsets = (
1336
1344
  {(tpl[1] & mask) for tpl in self._stack_arg_offsets} if self._stack_arg_offsets is not None else None
1337
1345
  )
1338
- for def_ in rd.all_definitions:
1339
- if def_.dummy:
1340
- continue
1341
- # we do not remove references to global memory regions no matter what
1342
- if isinstance(def_.atom, atoms.MemoryLocation) and isinstance(def_.atom.addr, int):
1343
- continue
1344
- if isinstance(def_.atom, atoms.VirtualVariable):
1345
- if def_.atom.varid in self._propagator_dead_vvar_ids:
1346
+ while True:
1347
+ new_dead_vars_found = False
1348
+ for vvar, codeloc in rd.all_vvar_definitions.items():
1349
+ if vvar.varid in dead_vvar_ids:
1350
+ continue
1351
+ if vvar.varid in self._propagator_dead_vvar_ids:
1346
1352
  # we are definitely removing this variable if it has no uses
1347
- uses = rd.get_vvar_uses(def_.atom)
1348
- elif def_.atom.was_stack:
1353
+ uses = rd.all_vvar_uses[vvar]
1354
+ elif vvar.was_stack:
1349
1355
  if not self._remove_dead_memdefs:
1350
- if rd.is_phi_vvar_id(def_.atom.varid):
1356
+ if rd.is_phi_vvar_id(vvar.varid):
1351
1357
  # we always remove unused phi variables
1352
1358
  pass
1353
- elif def_.atom.varid in self._secondary_stackvars:
1359
+ elif vvar.varid in self._secondary_stackvars:
1354
1360
  # secondary stack variables are potentially removable
1355
1361
  pass
1356
1362
  elif stackarg_offsets is not None:
1357
1363
  # we always remove definitions for stack arguments
1358
- assert def_.atom.stack_offset is not None
1359
- if (def_.atom.stack_offset & mask) not in stackarg_offsets:
1364
+ assert vvar.stack_offset is not None
1365
+ if (vvar.stack_offset & mask) not in stackarg_offsets:
1360
1366
  continue
1361
1367
  else:
1362
1368
  continue
1363
- uses = rd.get_vvar_uses(def_.atom)
1369
+ uses = rd.all_vvar_uses[vvar]
1364
1370
 
1365
- elif def_.atom.was_tmp or def_.atom.was_reg or def_.atom.was_parameter:
1366
- uses = rd.get_vvar_uses(def_.atom)
1371
+ elif vvar.was_tmp or vvar.was_reg or vvar.was_parameter:
1372
+ uses = rd.all_vvar_uses[vvar]
1367
1373
 
1368
1374
  else:
1369
1375
  uses = set()
1370
1376
 
1371
- else:
1372
- continue
1373
-
1374
- if not uses:
1375
- if isinstance(def_.atom, atoms.VirtualVariable):
1376
- dead_vvar_ids.add(def_.atom.varid)
1377
+ # remove uses where vvars are going to be removed
1378
+ filtered_uses_count = 0
1379
+ for _, loc in uses:
1380
+ if loc in dead_vvar_codelocs and loc.block_addr is not None and loc.stmt_idx is not None:
1381
+ stmt = blocks[(loc.block_addr, loc.block_idx)].statements[loc.stmt_idx]
1382
+ if not self._statement_has_call_exprs(stmt) and not isinstance(stmt, (DirtyStatement, Call)):
1383
+ continue
1384
+ filtered_uses_count += 1
1385
+
1386
+ if filtered_uses_count == 0:
1387
+ new_dead_vars_found = True
1388
+ dead_vvar_ids.add(vvar.varid)
1389
+ dead_vvar_codelocs.add(codeloc)
1390
+ if not isinstance(codeloc, ExternalCodeLocation):
1391
+ assert codeloc.block_addr is not None
1392
+ assert codeloc.stmt_idx is not None
1393
+ stmts_to_remove_per_block[(codeloc.block_addr, codeloc.block_idx)].add(codeloc.stmt_idx)
1394
+ stmts_to_keep_per_block[(codeloc.block_addr, codeloc.block_idx)].discard(codeloc.stmt_idx)
1395
+ else:
1396
+ if not isinstance(codeloc, ExternalCodeLocation):
1397
+ assert codeloc.block_addr is not None
1398
+ assert codeloc.stmt_idx is not None
1399
+ stmts_to_keep_per_block[(codeloc.block_addr, codeloc.block_idx)].add(codeloc.stmt_idx)
1377
1400
 
1378
- if not isinstance(def_.codeloc, ExternalCodeLocation):
1379
- assert def_.codeloc.block_addr is not None
1380
- assert def_.codeloc.stmt_idx is not None
1381
- stmts_to_remove_per_block[(def_.codeloc.block_addr, def_.codeloc.block_idx)].add(
1382
- def_.codeloc.stmt_idx
1383
- )
1384
- else:
1385
- if not isinstance(def_.codeloc, ExternalCodeLocation):
1386
- assert def_.codeloc.block_addr is not None
1387
- assert def_.codeloc.stmt_idx is not None
1388
- stmts_to_keep_per_block[(def_.codeloc.block_addr, def_.codeloc.block_idx)].add(
1389
- def_.codeloc.stmt_idx
1390
- )
1401
+ if not new_dead_vars_found:
1402
+ # nothing more is found. let's end the loop
1403
+ break
1391
1404
 
1392
1405
  # find all phi variables that rely on variables that no longer exist
1393
1406
  all_removed_var_ids = self._removed_vvar_ids.copy()
@@ -1462,6 +1475,7 @@ class AILSimplifier(Analysis):
1462
1475
  if codeloc in self._assignments_to_remove:
1463
1476
  # it should be removed
1464
1477
  simplified = True
1478
+ self._assignments_to_remove.discard(codeloc)
1465
1479
  continue
1466
1480
 
1467
1481
  if self._statement_has_call_exprs(stmt):
@@ -1489,6 +1503,7 @@ class AILSimplifier(Analysis):
1489
1503
  codeloc = CodeLocation(block.addr, idx, ins_addr=stmt.ins_addr, block_idx=block.idx)
1490
1504
  if codeloc in self._calls_to_remove:
1491
1505
  # this call can be removed
1506
+ self._calls_to_remove.discard(codeloc)
1492
1507
  simplified = True
1493
1508
  continue
1494
1509