angr 9.2.139__py3-none-manylinux2014_x86_64.whl → 9.2.141__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 (87) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/calling_convention/calling_convention.py +136 -53
  3. angr/analyses/calling_convention/fact_collector.py +44 -18
  4. angr/analyses/calling_convention/utils.py +3 -1
  5. angr/analyses/cfg/cfg_base.py +13 -0
  6. angr/analyses/cfg/cfg_fast.py +11 -0
  7. angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +9 -8
  8. angr/analyses/decompiler/ail_simplifier.py +115 -72
  9. angr/analyses/decompiler/callsite_maker.py +24 -11
  10. angr/analyses/decompiler/clinic.py +78 -43
  11. angr/analyses/decompiler/decompiler.py +18 -7
  12. angr/analyses/decompiler/expression_narrower.py +1 -1
  13. angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +8 -7
  14. angr/analyses/decompiler/optimization_passes/duplication_reverter/duplication_reverter.py +3 -1
  15. angr/analyses/decompiler/optimization_passes/flip_boolean_cmp.py +21 -2
  16. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +21 -13
  17. angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +84 -15
  18. angr/analyses/decompiler/optimization_passes/optimization_pass.py +92 -11
  19. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +53 -9
  20. angr/analyses/decompiler/peephole_optimizations/eager_eval.py +44 -7
  21. angr/analyses/decompiler/region_identifier.py +6 -4
  22. angr/analyses/decompiler/region_simplifiers/expr_folding.py +287 -122
  23. angr/analyses/decompiler/region_simplifiers/region_simplifier.py +31 -13
  24. angr/analyses/decompiler/ssailification/rewriting.py +23 -15
  25. angr/analyses/decompiler/ssailification/rewriting_engine.py +105 -24
  26. angr/analyses/decompiler/ssailification/ssailification.py +22 -14
  27. angr/analyses/decompiler/structured_codegen/c.py +73 -137
  28. angr/analyses/decompiler/structuring/dream.py +22 -18
  29. angr/analyses/decompiler/structuring/phoenix.py +158 -41
  30. angr/analyses/decompiler/structuring/recursive_structurer.py +1 -0
  31. angr/analyses/decompiler/structuring/structurer_base.py +37 -10
  32. angr/analyses/decompiler/structuring/structurer_nodes.py +4 -1
  33. angr/analyses/decompiler/utils.py +106 -21
  34. angr/analyses/deobfuscator/api_obf_finder.py +8 -5
  35. angr/analyses/deobfuscator/api_obf_type2_finder.py +18 -10
  36. angr/analyses/deobfuscator/string_obf_finder.py +105 -18
  37. angr/analyses/forward_analysis/forward_analysis.py +1 -1
  38. angr/analyses/propagator/top_checker_mixin.py +6 -6
  39. angr/analyses/reaching_definitions/__init__.py +2 -1
  40. angr/analyses/reaching_definitions/dep_graph.py +1 -12
  41. angr/analyses/reaching_definitions/engine_vex.py +36 -31
  42. angr/analyses/reaching_definitions/function_handler.py +15 -2
  43. angr/analyses/reaching_definitions/rd_state.py +1 -37
  44. angr/analyses/reaching_definitions/reaching_definitions.py +13 -24
  45. angr/analyses/s_propagator.py +6 -41
  46. angr/analyses/s_reaching_definitions/s_rda_model.py +7 -1
  47. angr/analyses/s_reaching_definitions/s_rda_view.py +43 -25
  48. angr/analyses/stack_pointer_tracker.py +36 -22
  49. angr/analyses/typehoon/simple_solver.py +45 -7
  50. angr/analyses/typehoon/typeconsts.py +18 -5
  51. angr/analyses/variable_recovery/engine_ail.py +1 -1
  52. angr/analyses/variable_recovery/engine_base.py +7 -5
  53. angr/analyses/variable_recovery/engine_vex.py +20 -4
  54. angr/block.py +69 -107
  55. angr/callable.py +14 -7
  56. angr/calling_conventions.py +30 -11
  57. angr/distributed/__init__.py +1 -1
  58. angr/engines/__init__.py +7 -8
  59. angr/engines/engine.py +1 -120
  60. angr/engines/failure.py +2 -2
  61. angr/engines/hook.py +2 -2
  62. angr/engines/light/engine.py +2 -2
  63. angr/engines/pcode/engine.py +2 -14
  64. angr/engines/procedure.py +2 -2
  65. angr/engines/soot/engine.py +2 -2
  66. angr/engines/soot/statements/switch.py +1 -1
  67. angr/engines/successors.py +124 -11
  68. angr/engines/syscall.py +2 -2
  69. angr/engines/unicorn.py +3 -3
  70. angr/engines/vex/heavy/heavy.py +3 -15
  71. angr/factory.py +12 -22
  72. angr/knowledge_plugins/key_definitions/atoms.py +8 -4
  73. angr/knowledge_plugins/key_definitions/live_definitions.py +41 -103
  74. angr/knowledge_plugins/variables/variable_manager.py +7 -5
  75. angr/sim_type.py +19 -17
  76. angr/simos/simos.py +3 -1
  77. angr/state_plugins/plugin.py +19 -4
  78. angr/storage/memory_mixins/memory_mixin.py +1 -1
  79. angr/storage/memory_mixins/paged_memory/pages/multi_values.py +10 -5
  80. angr/utils/ssa/__init__.py +119 -4
  81. angr/utils/types.py +48 -0
  82. {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/METADATA +6 -6
  83. {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/RECORD +87 -86
  84. {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/LICENSE +0 -0
  85. {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/WHEEL +0 -0
  86. {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/entry_points.txt +0 -0
  87. {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/top_level.txt +0 -0
@@ -27,6 +27,7 @@ from ailment.expression import (
27
27
  from angr.analyses.s_propagator import SPropagatorAnalysis
28
28
  from angr.analyses.s_reaching_definitions import SRDAModel
29
29
  from angr.utils.ail import is_phi_assignment, HasExprWalker
30
+ from angr.utils.ssa import has_call_in_between_stmts, has_store_stmt_in_between_stmts, has_load_expr_in_between_stmts
30
31
  from angr.code_location import CodeLocation, ExternalCodeLocation
31
32
  from angr.sim_variable import SimStackVariable, SimMemoryVariable, SimVariable
32
33
  from angr.knowledge_plugins.propagations.states import Equivalence
@@ -98,6 +99,7 @@ class AILSimplifier(Analysis):
98
99
  removed_vvar_ids: set[int] | None = None,
99
100
  arg_vvars: dict[int, tuple[VirtualVariable, SimVariable]] | None = None,
100
101
  avoid_vvar_ids: set[int] | None = None,
102
+ secondary_stackvars: set[int] | None = None,
101
103
  ):
102
104
  self.func = func
103
105
  self.func_graph = func_graph if func_graph is not None else func.graph
@@ -118,6 +120,7 @@ class AILSimplifier(Analysis):
118
120
  self._arg_vvars = arg_vvars
119
121
  self._avoid_vvar_ids = avoid_vvar_ids
120
122
  self._propagator_dead_vvar_ids: set[int] = set()
123
+ self._secondary_stackvars: set[int] = secondary_stackvars if secondary_stackvars is not None else set()
121
124
 
122
125
  self._calls_to_remove: set[CodeLocation] = set()
123
126
  self._assignments_to_remove: set[CodeLocation] = set()
@@ -292,7 +295,7 @@ class AILSimplifier(Analysis):
292
295
 
293
296
  narrowed = False
294
297
 
295
- addr_and_idx_to_block: dict[tuple[int, int], Block] = {}
298
+ addr_and_idx_to_block: dict[tuple[int, int | None], Block] = {}
296
299
  for block in self.func_graph.nodes():
297
300
  addr_and_idx_to_block[(block.addr, block.idx)] = block
298
301
 
@@ -424,6 +427,7 @@ class AILSimplifier(Analysis):
424
427
  return ExprNarrowingInfo(False)
425
428
 
426
429
  block = self.blocks.get(old_block, old_block)
430
+ assert loc.stmt_idx is not None
427
431
  if loc.stmt_idx >= len(block.statements):
428
432
  # missing a statement for whatever reason
429
433
  return ExprNarrowingInfo(False)
@@ -551,14 +555,23 @@ class AILSimplifier(Analysis):
551
555
  return None, None
552
556
  return expr.size, ("expr", (expr,))
553
557
 
554
- first_op = walker.operations[0]
558
+ ops = walker.operations
559
+ first_op = ops[0]
560
+ if isinstance(first_op, BinaryOp) and first_op.op in {"Add", "Sub"}:
561
+ # expr + x
562
+ ops = ops[1:]
563
+ if not ops:
564
+ if expr is None:
565
+ return None, None
566
+ return expr.size, ("expr", (expr,))
567
+ first_op = ops[0]
555
568
  if isinstance(first_op, Convert) and first_op.to_bits >= self.project.arch.byte_width:
556
569
  # we need at least one byte!
557
570
  return first_op.to_bits // self.project.arch.byte_width, ("convert", (first_op,))
558
571
  if isinstance(first_op, BinaryOp):
559
572
  second_op = None
560
- if len(walker.operations) >= 2:
561
- second_op = walker.operations[1]
573
+ if len(ops) >= 2:
574
+ second_op = ops[1]
562
575
  if (
563
576
  first_op.op == "And"
564
577
  and isinstance(first_op.operands[1], Const)
@@ -623,9 +636,9 @@ class AILSimplifier(Analysis):
623
636
  block = blocks_by_addr_and_idx[(block_addr, block_idx)]
624
637
 
625
638
  # only replace loads if there are stack arguments in this block
626
- replace_loads = insn_addrs_using_stack_args is not None and {
627
- stmt.ins_addr for stmt in block.statements
628
- }.intersection(insn_addrs_using_stack_args)
639
+ replace_loads: bool = insn_addrs_using_stack_args is not None and bool(
640
+ {stmt.ins_addr for stmt in block.statements}.intersection(insn_addrs_using_stack_args)
641
+ )
629
642
 
630
643
  # remove virtual variables in the avoid list
631
644
  if self._avoid_vvar_ids:
@@ -662,7 +675,7 @@ class AILSimplifier(Analysis):
662
675
  if not equivalence:
663
676
  return simplified
664
677
 
665
- addr_and_idx_to_block: dict[tuple[int, int], Block] = {}
678
+ addr_and_idx_to_block: dict[tuple[int, int | None], Block] = {}
666
679
  for block in self.func_graph.nodes():
667
680
  addr_and_idx_to_block[(block.addr, block.idx)] = block
668
681
 
@@ -943,6 +956,8 @@ class AILSimplifier(Analysis):
943
956
  for use_loc in all_use_locs:
944
957
  if use_loc == eq.codeloc:
945
958
  continue
959
+ assert use_loc.block_addr is not None
960
+ assert use_loc.stmt_idx is not None
946
961
  block = addr_and_idx_to_block[(use_loc.block_addr, use_loc.block_idx)]
947
962
  stmt = block.statements[use_loc.stmt_idx]
948
963
  if isinstance(stmt, Assignment) or (isinstance(replace_with, Load) and isinstance(stmt, Store)):
@@ -954,11 +969,15 @@ class AILSimplifier(Analysis):
954
969
 
955
970
  remove_initial_assignment = False # expression folding will take care of it
956
971
 
972
+ assert replace_with is not None
973
+
957
974
  if any(not isinstance(use_and_expr[1], VirtualVariable) for _, use_and_expr in all_uses_with_def):
958
975
  # if any of the uses are phi assignments, we skip
959
976
  used_in_phi_assignment = False
960
977
  for _, use_and_expr in all_uses_with_def:
961
978
  u = use_and_expr[0]
979
+ assert u.block_addr is not None
980
+ assert u.stmt_idx is not None
962
981
  block = addr_and_idx_to_block[(u.block_addr, u.block_idx)]
963
982
  stmt = block.statements[u.stmt_idx]
964
983
  if is_phi_assignment(stmt):
@@ -1120,8 +1139,6 @@ class AILSimplifier(Analysis):
1120
1139
  than once after simplification and graph structuring where conditions might be duplicated (e.g., in Dream).
1121
1140
  In such cases, the one-use expression folder in RegionSimplifier will perform this transformation.
1122
1141
  """
1123
- # Disabled until https://github.com/angr/angr/issues/5112 and related folding issues are fixed
1124
- return False
1125
1142
 
1126
1143
  # pylint:disable=unreachable
1127
1144
  simplified = False
@@ -1130,7 +1147,7 @@ class AILSimplifier(Analysis):
1130
1147
  if not equivalence:
1131
1148
  return simplified
1132
1149
 
1133
- addr_and_idx_to_block: dict[tuple[int, int], Block] = {}
1150
+ addr_and_idx_to_block: dict[tuple[int, int | None], Block] = {}
1134
1151
  for block in self.func_graph.nodes():
1135
1152
  addr_and_idx_to_block[(block.addr, block.idx)] = block
1136
1153
 
@@ -1168,6 +1185,8 @@ class AILSimplifier(Analysis):
1168
1185
  ),
1169
1186
  eq.codeloc,
1170
1187
  )
1188
+ assert the_def.codeloc.block_addr is not None
1189
+ assert the_def.codeloc.stmt_idx is not None
1171
1190
 
1172
1191
  all_uses: set[tuple[CodeLocation, Any]] = set(rd.get_vvar_uses_with_expr(the_def.atom))
1173
1192
 
@@ -1176,6 +1195,8 @@ class AILSimplifier(Analysis):
1176
1195
  u, used_expr = next(iter(all_uses))
1177
1196
  if used_expr is None:
1178
1197
  continue
1198
+ assert u.block_addr is not None
1199
+ assert u.stmt_idx is not None
1179
1200
 
1180
1201
  if u in def_locations_to_remove:
1181
1202
  # this use site has been altered by previous folding attempts. the corresponding statement will be
@@ -1196,41 +1217,28 @@ class AILSimplifier(Analysis):
1196
1217
  if u.block_addr not in {b.addr for b in super_node_blocks}:
1197
1218
  continue
1198
1219
 
1199
- # check if the register has been overwritten by statements in between the def site and the use site
1200
- # usesite_atom_defs = set(rd.get_defs(the_def.atom, u, OP_BEFORE))
1201
- # if len(usesite_atom_defs) != 1:
1202
- # continue
1203
- # usesite_atom_def = next(iter(usesite_atom_defs))
1204
- # if usesite_atom_def != the_def:
1205
- # continue
1206
-
1207
- # check if any atoms that the call relies on has been overwritten by statements in between the def site
1208
- # and the use site.
1209
- # TODO: Prove non-interference
1210
- # defsite_all_expr_uses = set(rd.all_uses.get_uses_by_location(the_def.codeloc))
1211
- # defsite_used_atoms = set()
1212
- # for dd in defsite_all_expr_uses:
1213
- # defsite_used_atoms.add(dd.atom)
1214
- # usesite_expr_def_outdated = False
1215
- # for defsite_expr_atom in defsite_used_atoms:
1216
- # usesite_expr_uses = set(rd.get_defs(defsite_expr_atom, u, OP_BEFORE))
1217
- # if not usesite_expr_uses:
1218
- # # the atom is not defined at the use site - it's fine
1219
- # continue
1220
- # defsite_expr_uses = set(rd.get_defs(defsite_expr_atom, the_def.codeloc, OP_BEFORE))
1221
- # if usesite_expr_uses != defsite_expr_uses:
1222
- # # special case: ok if this atom is assigned to at the def site and has not been overwritten
1223
- # if len(usesite_expr_uses) == 1:
1224
- # usesite_expr_use = next(iter(usesite_expr_uses))
1225
- # if usesite_expr_use.atom == defsite_expr_atom and (
1226
- # usesite_expr_use.codeloc == the_def.codeloc
1227
- # or usesite_expr_use.codeloc.block_addr == call_addr
1228
- # ):
1229
- # continue
1230
- # usesite_expr_def_outdated = True
1231
- # break
1232
- # if usesite_expr_def_outdated:
1233
- # continue
1220
+ # ensure there are no other calls between the def site and the use site.
1221
+ # this is because we do not want to alter the order of calls.
1222
+ u_inclusive = CodeLocation(u.block_addr, u.stmt_idx + 1, block_idx=u.block_idx)
1223
+ # note that the target statement being a store is fine
1224
+ if (
1225
+ has_call_in_between_stmts(
1226
+ self.func_graph,
1227
+ addr_and_idx_to_block,
1228
+ the_def.codeloc,
1229
+ u_inclusive,
1230
+ skip_if_contains_vvar=the_def.atom.varid,
1231
+ )
1232
+ or has_store_stmt_in_between_stmts(self.func_graph, addr_and_idx_to_block, the_def.codeloc, u)
1233
+ or has_load_expr_in_between_stmts(
1234
+ self.func_graph,
1235
+ addr_and_idx_to_block,
1236
+ the_def.codeloc,
1237
+ u_inclusive,
1238
+ skip_if_contains_vvar=the_def.atom.varid,
1239
+ )
1240
+ ):
1241
+ continue
1234
1242
 
1235
1243
  # check if there are any calls in between the def site and the use site
1236
1244
  if self._count_calls_in_supernodeblocks(super_node_blocks, the_def.codeloc, u) > 0:
@@ -1316,8 +1324,8 @@ class AILSimplifier(Analysis):
1316
1324
  # keeping tracking of statements to remove and statements (as well as dead vvars) to keep allows us to handle
1317
1325
  # cases where a statement defines more than one atoms, e.g., a call statement that defines both the return
1318
1326
  # value and the floating-point return value.
1319
- stmts_to_remove_per_block: dict[tuple[int, int], set[int]] = defaultdict(set)
1320
- stmts_to_keep_per_block: dict[tuple[int, int], set[int]] = defaultdict(set)
1327
+ stmts_to_remove_per_block: dict[tuple[int, int | None], set[int]] = defaultdict(set)
1328
+ stmts_to_keep_per_block: dict[tuple[int, int | None], set[int]] = defaultdict(set)
1321
1329
  dead_vvar_ids: set[int] = set()
1322
1330
 
1323
1331
  # Find all statements that should be removed
@@ -1342,8 +1350,12 @@ class AILSimplifier(Analysis):
1342
1350
  if rd.is_phi_vvar_id(def_.atom.varid):
1343
1351
  # we always remove unused phi variables
1344
1352
  pass
1353
+ elif def_.atom.varid in self._secondary_stackvars:
1354
+ # secondary stack variables are potentially removable
1355
+ pass
1345
1356
  elif stackarg_offsets is not None:
1346
1357
  # we always remove definitions for stack arguments
1358
+ assert def_.atom.stack_offset is not None
1347
1359
  if (def_.atom.stack_offset & mask) not in stackarg_offsets:
1348
1360
  continue
1349
1361
  else:
@@ -1364,11 +1376,18 @@ class AILSimplifier(Analysis):
1364
1376
  dead_vvar_ids.add(def_.atom.varid)
1365
1377
 
1366
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
1367
1381
  stmts_to_remove_per_block[(def_.codeloc.block_addr, def_.codeloc.block_idx)].add(
1368
1382
  def_.codeloc.stmt_idx
1369
1383
  )
1370
1384
  else:
1371
- stmts_to_keep_per_block[(def_.codeloc.block_addr, def_.codeloc.block_idx)].add(def_.codeloc.stmt_idx)
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
+ )
1372
1391
 
1373
1392
  # find all phi variables that rely on variables that no longer exist
1374
1393
  all_removed_var_ids = self._removed_vvar_ids.copy()
@@ -1380,6 +1399,7 @@ class AILSimplifier(Analysis):
1380
1399
  vvarid in removed_vvar_ids for vvarid in phi_use_varids
1381
1400
  ):
1382
1401
  loc = rd.all_vvar_definitions[rd.varid_to_vvar[phi_varid]]
1402
+ assert loc.block_addr is not None and loc.stmt_idx is not None
1383
1403
  stmts_to_remove_per_block[(loc.block_addr, loc.block_idx)].add(loc.stmt_idx)
1384
1404
  new_removed_vvar_ids.add(phi_varid)
1385
1405
  all_removed_var_ids.add(phi_varid)
@@ -1391,11 +1411,13 @@ class AILSimplifier(Analysis):
1391
1411
  redundant_phi_and_dirty_varids = self._find_cyclic_dependent_phis_and_dirty_vvars(rd)
1392
1412
  for varid in redundant_phi_and_dirty_varids:
1393
1413
  loc = rd.all_vvar_definitions[rd.varid_to_vvar[varid]]
1414
+ assert loc.block_addr is not None and loc.stmt_idx is not None
1394
1415
  stmts_to_remove_per_block[(loc.block_addr, loc.block_idx)].add(loc.stmt_idx)
1395
1416
  stmts_to_keep_per_block[(loc.block_addr, loc.block_idx)].discard(loc.stmt_idx)
1396
1417
 
1397
1418
  for codeloc in self._calls_to_remove | self._assignments_to_remove:
1398
1419
  # this call can be removed. make sure it exists in stmts_to_remove_per_block
1420
+ assert codeloc.block_addr is not None and codeloc.stmt_idx is not None
1399
1421
  stmts_to_remove_per_block[codeloc.block_addr, codeloc.block_idx].add(codeloc.stmt_idx)
1400
1422
 
1401
1423
  simplified = False
@@ -1488,8 +1510,36 @@ class AILSimplifier(Analysis):
1488
1510
 
1489
1511
  return simplified
1490
1512
 
1513
+ @staticmethod
1514
+ def _get_vvar_used_by(
1515
+ vvar_id: int, rd: SRDAModel, blocks_dict: dict[tuple[int, int | None], Block]
1516
+ ) -> set[int | None]:
1517
+ """
1518
+ Get all atoms that use a specified virtual variable. The atoms are in the form of virtual variable ID or None
1519
+ (indicating the virtual variable is used by another statement like Store).
1520
+
1521
+ :param vvar_id: ID of the virtual variable.
1522
+ :param rd: The SRDA model.
1523
+ :return: The set of vvar use atoms.
1524
+ """
1525
+
1526
+ vvar = rd.varid_to_vvar[vvar_id]
1527
+ used_by: set[int | None] = set()
1528
+ for used_vvar, loc in rd.all_vvar_uses[vvar]:
1529
+ if used_vvar is None:
1530
+ # no explicit reference
1531
+ used_by.add(None)
1532
+ elif loc.block_addr is not None:
1533
+ assert loc.stmt_idx is not None
1534
+ stmt = blocks_dict[(loc.block_addr, loc.block_idx)].statements[loc.stmt_idx]
1535
+ if isinstance(stmt, Assignment) and isinstance(stmt.dst, VirtualVariable):
1536
+ used_by.add(stmt.dst.varid)
1537
+ else:
1538
+ used_by.add(None)
1539
+ return used_by
1540
+
1491
1541
  def _find_cyclic_dependent_phis_and_dirty_vvars(self, rd: SRDAModel) -> set[int]:
1492
- blocks_dict = {(bb.addr, bb.idx): bb for bb in self.func_graph}
1542
+ blocks_dict: dict[tuple[int, int | None], Block] = {(bb.addr, bb.idx): bb for bb in self.func_graph}
1493
1543
 
1494
1544
  # find dirty vvars and vexccall vvars
1495
1545
  dirty_vvar_ids = set()
@@ -1505,25 +1555,14 @@ class AILSimplifier(Analysis):
1505
1555
 
1506
1556
  phi_and_dirty_vvar_ids = rd.phi_vvar_ids | dirty_vvar_ids
1507
1557
 
1508
- vvar_used_by: dict[int, set[int]] = defaultdict(set)
1558
+ vvar_used_by: dict[int, set[int | None]] = defaultdict(set)
1509
1559
  for var_id in phi_and_dirty_vvar_ids:
1510
1560
  if var_id in rd.phivarid_to_varids:
1511
1561
  for used_by_varid in rd.phivarid_to_varids[var_id]:
1512
- vvar_used_by[used_by_varid].add(var_id)
1513
-
1514
- vvar = rd.varid_to_vvar[var_id]
1515
- used_by = set()
1516
- for used_vvar, loc in rd.all_vvar_uses[vvar]:
1517
- if used_vvar is None:
1518
- # no explicit reference
1519
- used_by.add(None)
1520
- else:
1521
- stmt = blocks_dict[loc.block_addr, loc.block_idx].statements[loc.stmt_idx]
1522
- if isinstance(stmt, Assignment) and isinstance(stmt.dst, VirtualVariable):
1523
- used_by.add(stmt.dst.varid)
1524
- else:
1525
- used_by.add(None)
1526
- vvar_used_by[var_id] |= used_by
1562
+ if used_by_varid not in vvar_used_by:
1563
+ vvar_used_by[used_by_varid] |= self._get_vvar_used_by(used_by_varid, rd, blocks_dict)
1564
+ vvar_used_by[used_by_varid].add(var_id) # probably unnecessary
1565
+ vvar_used_by[var_id] |= self._get_vvar_used_by(var_id, rd, blocks_dict)
1527
1566
 
1528
1567
  g = networkx.DiGraph()
1529
1568
  dummy_vvar_id = -1
@@ -1542,8 +1581,12 @@ class AILSimplifier(Analysis):
1542
1581
 
1543
1582
  bail = False
1544
1583
  for varid in scc:
1545
- # if this vvar is a phi var, ensure this vvar is not used by anything else outside the scc
1546
- if varid in rd.phi_vvar_ids:
1584
+ # ensure this vvar is not used by anything else outside the scc (regardless of whether this vvar is a
1585
+ # phi variable or not)
1586
+ if varid in vvar_used_by and None in vvar_used_by[varid]:
1587
+ bail = True
1588
+ break
1589
+ if bail is False:
1547
1590
  succs = list(g.successors(varid))
1548
1591
  if any(succ_varid not in scc for succ_varid in succs):
1549
1592
  bail = True
@@ -1565,7 +1608,7 @@ class AILSimplifier(Analysis):
1565
1608
  if rewriter_cls is None:
1566
1609
  return False
1567
1610
 
1568
- walker = None
1611
+ walker = AILBlockWalker()
1569
1612
 
1570
1613
  class _any_update:
1571
1614
  """
@@ -1574,7 +1617,9 @@ class AILSimplifier(Analysis):
1574
1617
 
1575
1618
  v = False
1576
1619
 
1577
- def _handle_expr(expr_idx: int, expr: Expression, stmt_idx: int, stmt: Statement, block) -> Expression | None:
1620
+ def _handle_expr(
1621
+ expr_idx: int, expr: Expression, stmt_idx: int, stmt: Statement | None, block: Block | None
1622
+ ) -> Expression | None:
1578
1623
  if isinstance(expr, VEXCCallExpression):
1579
1624
  rewriter = rewriter_cls(expr, self.project.arch)
1580
1625
  if rewriter.result is not None:
@@ -1585,8 +1630,6 @@ class AILSimplifier(Analysis):
1585
1630
  return AILBlockWalker._handle_expr(walker, expr_idx, expr, stmt_idx, stmt, block)
1586
1631
 
1587
1632
  blocks_by_addr_and_idx = {(node.addr, node.idx): node for node in self.func_graph.nodes()}
1588
-
1589
- walker = AILBlockWalker()
1590
1633
  walker._handle_expr = _handle_expr
1591
1634
 
1592
1635
  updated = False
@@ -45,7 +45,7 @@ class CallSiteMaker(Analysis):
45
45
  self._ail_manager = ail_manager
46
46
 
47
47
  self.result_block = None
48
- self.stack_arg_offsets: set[tuple[int, int]] | None = None # ins_addr, stack_offset
48
+ self.stack_arg_offsets: set[tuple[int, int]] | None = None # call ins addr, stack_offset
49
49
  self.removed_vvar_ids: set[int] = set()
50
50
 
51
51
  self._analyze()
@@ -372,7 +372,9 @@ class CallSiteMaker(Analysis):
372
372
 
373
373
  return None
374
374
 
375
- def _resolve_stack_argument(self, call_stmt, arg_loc) -> tuple[Any, Any]: # pylint:disable=unused-argument
375
+ def _resolve_stack_argument(
376
+ self, call_stmt: Stmt.Call, arg_loc
377
+ ) -> tuple[Any, Any]: # pylint:disable=unused-argument
376
378
  assert self._stack_pointer_tracker is not None
377
379
 
378
380
  size = arg_loc.size
@@ -399,15 +401,26 @@ class CallSiteMaker(Analysis):
399
401
  # FIXME: vvar may be larger than that we ask; we may need to chop the correct value of vvar
400
402
  value = view.get_vvar_value(vvar)
401
403
  if value is not None and not isinstance(value, Expr.Phi):
402
- return None, value
403
- return None, Expr.VirtualVariable(
404
- self._atom_idx(),
405
- vvar.varid,
406
- vvar.bits,
407
- vvar.category,
408
- oident=vvar.oident,
409
- ins_addr=call_stmt.ins_addr,
410
- )
404
+ v: Expr.Expression = value
405
+ else:
406
+ v: Expr.Expression = Expr.VirtualVariable(
407
+ self._atom_idx(),
408
+ vvar.varid,
409
+ vvar.bits,
410
+ vvar.category,
411
+ oident=vvar.oident,
412
+ ins_addr=call_stmt.ins_addr,
413
+ )
414
+ if v.size > size:
415
+ v = Expr.Convert(
416
+ self._atom_idx(),
417
+ v.bits,
418
+ size * self.project.arch.byte_width,
419
+ False,
420
+ v,
421
+ ins_addr=call_stmt.ins_addr,
422
+ )
423
+ return None, v
411
424
 
412
425
  return None, Expr.Load(
413
426
  self._atom_idx(),