angr 9.2.139__py3-none-manylinux2014_aarch64.whl → 9.2.141__py3-none-manylinux2014_aarch64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of angr might be problematic. Click here for more details.

Files changed (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
@@ -12,7 +12,13 @@ from .if_ import IfSimplifier
12
12
  from .cascading_ifs import CascadingIfsRemover
13
13
  from .ifelse import IfElseFlattener
14
14
  from .loop import LoopSimplifier
15
- from .expr_folding import ExpressionCounter, ExpressionFolder, StoreStatementFinder, ExpressionLocation
15
+ from .expr_folding import (
16
+ ExpressionCounter,
17
+ ExpressionFolder,
18
+ StoreStatementFinder,
19
+ ExpressionLocation,
20
+ InterferenceChecker,
21
+ )
16
22
  from .cascading_cond_transformer import CascadingConditionTransformer
17
23
  from .switch_expr_simplifier import SwitchExpressionSimplifier
18
24
  from .switch_cluster_simplifier import SwitchClusterFinder, simplify_switch_clusters, simplify_lowered_switches
@@ -23,10 +29,17 @@ class RegionSimplifier(Analysis):
23
29
  Simplifies a given region.
24
30
  """
25
31
 
26
- def __init__(self, func, region, variable_kb=None, simplify_switches: bool = True, simplify_ifelse: bool = True):
32
+ def __init__(
33
+ self,
34
+ func,
35
+ region,
36
+ arg_vvars: set[int] | None = None,
37
+ simplify_switches: bool = True,
38
+ simplify_ifelse: bool = True,
39
+ ):
27
40
  self.func = func
28
41
  self.region = region
29
- self.variable_kb = variable_kb
42
+ self.arg_vvars = arg_vvars
30
43
  self._simplify_switches = simplify_switches
31
44
  self._should_simplify_ifelses = simplify_ifelse
32
45
 
@@ -54,7 +67,7 @@ class RegionSimplifier(Analysis):
54
67
  # Remove empty nodes again
55
68
  r = self._remove_empty_nodes(r)
56
69
 
57
- if self.variable_kb is not None:
70
+ if self.arg_vvars is not None:
58
71
  # Fold expressions that are only used once into their use sites
59
72
  r = self._fold_oneuse_expressions(r)
60
73
  r = self._remove_empty_nodes(r)
@@ -88,12 +101,8 @@ class RegionSimplifier(Analysis):
88
101
  #
89
102
 
90
103
  def _fold_oneuse_expressions(self, region):
91
- # Disabled until https://github.com/angr/angr/issues/5110 and related folding issues fixed
92
- return region
93
-
94
104
  # pylint:disable=unreachable
95
- variable_manager = self.variable_kb.variables[self.func.addr]
96
- expr_counter = ExpressionCounter(region, variable_manager)
105
+ expr_counter = ExpressionCounter(region)
97
106
 
98
107
  variable_assignments = {}
99
108
  variable_uses = {}
@@ -107,10 +116,13 @@ class RegionSimplifier(Analysis):
107
116
  for var, uses in expr_counter.uses.items():
108
117
  if len(uses) == 1 and var in expr_counter.assignments and len(expr_counter.assignments[var]) == 1:
109
118
  definition, deps, loc, has_loads = next(iter(expr_counter.assignments[var]))
119
+ _, use_expr_loc = next(iter(uses))
120
+ if isinstance(use_expr_loc, ExpressionLocation) and use_expr_loc.phi_stmt:
121
+ # we cannot fold expressions that are used in phi statements
122
+ continue
110
123
  if has_loads:
111
124
  # the definition has at least one load expression. we need to ensure there are no store statements
112
125
  # between the definition site and the use site
113
- _, use_expr_loc = next(iter(uses))
114
126
  if isinstance(use_expr_loc, ExpressionLocation):
115
127
  use_loc = use_expr_loc.statement_location()
116
128
  else:
@@ -130,7 +142,7 @@ class RegionSimplifier(Analysis):
130
142
  # make sure all variables that var depends on has been assigned at most once
131
143
  fail = False
132
144
  for dep_var in deps:
133
- if dep_var.is_function_argument:
145
+ if self.arg_vvars is not None and dep_var in self.arg_vvars:
134
146
  continue
135
147
  if dep_var in expr_counter.assignments and len(expr_counter.assignments[dep_var]) > 1:
136
148
  fail = True
@@ -155,8 +167,14 @@ class RegionSimplifier(Analysis):
155
167
  del variable_assignments[var]
156
168
  del variable_uses[var]
157
169
 
158
- # replace them
159
- ExpressionFolder(variable_assignments, variable_uses, region, variable_manager)
170
+ # ensure there is no interference between the call site and the use site
171
+ checker = InterferenceChecker(variable_assignments, variable_uses, region)
172
+ for varid in checker.interfered_assignments:
173
+ if varid in variable_assignments:
174
+ del variable_assignments[varid]
175
+ del variable_uses[varid]
176
+ # fold these expressions if possible
177
+ ExpressionFolder(variable_assignments, variable_uses, region)
160
178
  return region
161
179
 
162
180
  @staticmethod
@@ -8,21 +8,20 @@ import networkx
8
8
 
9
9
  import ailment
10
10
  from ailment import Block
11
- from ailment.expression import Expression, Phi, VirtualVariable, VirtualVariableCategory
12
- from ailment.statement import Statement, Assignment, Label
11
+ from ailment.expression import Phi, VirtualVariable, VirtualVariableCategory
12
+ from ailment.statement import Assignment, Label
13
13
 
14
14
  from angr.code_location import CodeLocation
15
15
  from angr.analyses import ForwardAnalysis
16
- from angr.analyses.forward_analysis.visitors.graph import NodeType
17
16
  from angr.analyses.forward_analysis import FunctionGraphVisitor
18
- from .rewriting_engine import SimEngineSSARewriting
17
+ from .rewriting_engine import SimEngineSSARewriting, DefExprType, AT
19
18
  from .rewriting_state import RewritingState
20
19
 
21
20
 
22
21
  l = logging.getLogger(__name__)
23
22
 
24
23
 
25
- class RewritingAnalysis(ForwardAnalysis[RewritingState, NodeType, object, object]):
24
+ class RewritingAnalysis(ForwardAnalysis[RewritingState, ailment.Block, object, object]):
26
25
  """
27
26
  RewritingAnalysis traverses the AIL graph, inserts phi nodes, and rewrites all expression uses to virtual variables
28
27
  when necessary.
@@ -37,7 +36,7 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, NodeType, object, object
37
36
  bp_as_gpr: bool,
38
37
  udef_to_phiid: dict[tuple, set[int]],
39
38
  phiid_to_loc: dict[int, tuple[int, int | None]],
40
- stackvar_locs: dict[int, int],
39
+ stackvar_locs: dict[int, set[int]],
41
40
  rewrite_tmps: bool,
42
41
  ail_manager,
43
42
  func_args: set[VirtualVariable],
@@ -75,7 +74,11 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, NodeType, object, object
75
74
 
76
75
  self._analyze()
77
76
 
78
- self.def_to_vvid: dict[tuple[int, int | None, int, Expression | Statement], int] = self._engine_ail.def_to_vvid
77
+ self.def_to_vvid: dict[tuple[int, int | None, int, DefExprType, AT], int] = self._engine_ail.def_to_vvid
78
+ # during SSA conversion, we create secondary stack variables because they overlap and are larger than the
79
+ # actual stack variables. these secondary stack variables can be safely eliminated during dead assignment
80
+ # elimination if not used by anything else.
81
+ self.secondary_stackvars: set[int] = self._engine_ail.secondary_stackvars
79
82
  self.out_graph = self._make_new_graph(ail_graph)
80
83
 
81
84
  @property
@@ -177,10 +180,11 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, NodeType, object, object
177
180
  def _reg_predicate(self, node_, *, reg_offset: int, reg_size: int) -> tuple[bool, Any]:
178
181
  out_state: RewritingState = self.out_states[(node_.addr, node_.idx)]
179
182
  if reg_offset in out_state.registers and reg_size in out_state.registers[reg_offset]:
180
- if out_state.registers[reg_offset][reg_size] is None:
183
+ existing_var = out_state.registers[reg_offset][reg_size]
184
+ if existing_var is None:
181
185
  # the vvar is not set. it should never be referenced
182
186
  return True, None
183
- vvar = out_state.registers[reg_offset][reg_size].copy()
187
+ vvar = existing_var.copy()
184
188
  vvar.idx = self._ail_manager.next_atom()
185
189
  return True, vvar
186
190
  return False, None
@@ -188,10 +192,11 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, NodeType, object, object
188
192
  def _stack_predicate(self, node_, *, stack_offset: int, stackvar_size: int) -> tuple[bool, Any]:
189
193
  out_state: RewritingState = self.out_states[(node_.addr, node_.idx)]
190
194
  if stack_offset in out_state.stackvars and stackvar_size in out_state.stackvars[stack_offset]:
191
- if out_state.stackvars[stack_offset][stackvar_size] is None:
195
+ existing_var = out_state.stackvars[stack_offset][stackvar_size]
196
+ if existing_var is None:
192
197
  # the vvar is not set. it should never be referenced
193
198
  return True, None
194
- vvar = out_state.stackvars[stack_offset][stackvar_size].copy()
199
+ vvar = existing_var.copy()
195
200
  vvar.idx = self._ail_manager.next_atom()
196
201
  return True, vvar
197
202
  return False, None
@@ -215,11 +220,14 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, NodeType, object, object
215
220
  )
216
221
  # update state with function arguments
217
222
  for func_arg in self._func_args:
218
- if func_arg.oident[0] == VirtualVariableCategory.REGISTER:
219
- reg_offset, reg_size = func_arg.oident[1], func_arg.size
223
+ if func_arg.parameter_category == VirtualVariableCategory.REGISTER:
224
+ reg_offset, reg_size = func_arg.parameter_reg_offset, func_arg.size
225
+ assert reg_offset is not None and reg_size is not None
220
226
  state.registers[reg_offset][reg_size] = func_arg
221
- elif func_arg.oident[0] == VirtualVariableCategory.STACK:
222
- state.stackvars[func_arg.oident[1]][func_arg.size] = func_arg
227
+ elif func_arg.parameter_category == VirtualVariableCategory.STACK:
228
+ parameter_stack_offset: int = func_arg.oident[1] # type: ignore
229
+ assert parameter_stack_offset is not None and func_arg.size is not None
230
+ state.stackvars[parameter_stack_offset][func_arg.size] = func_arg
223
231
  return state
224
232
 
225
233
  def _run_on_node(self, node, state: RewritingState):
@@ -1,7 +1,9 @@
1
1
  # pylint:disable=no-self-use,unused-argument
2
2
  from __future__ import annotations
3
+ from typing import Literal
3
4
  import logging
4
5
 
6
+ from archinfo import Endness
5
7
  from ailment.manager import Manager
6
8
  from ailment.statement import Statement, Assignment, Store, Call, Return, ConditionalJump, DirtyStatement, Jump
7
9
  from ailment.expression import (
@@ -22,6 +24,7 @@ from ailment.expression import (
22
24
  Reinterpret,
23
25
  )
24
26
 
27
+ from angr.knowledge_plugins.key_definitions import atoms
25
28
  from angr.engines.light.engine import SimEngineNostmtAIL
26
29
  from angr.utils.ssa import get_reg_offset_base_and_size
27
30
  from .rewriting_state import RewritingState
@@ -30,6 +33,10 @@ from .rewriting_state import RewritingState
30
33
  _l = logging.getLogger(__name__)
31
34
 
32
35
 
36
+ DefExprType = atoms.Register | atoms.Tmp | atoms.MemoryLocation
37
+ AT = Literal["l", "s"] | None
38
+
39
+
33
40
  class SimEngineSSARewriting(
34
41
  SimEngineNostmtAIL[RewritingState, Expression | None, Statement | tuple[Statement, ...], None]
35
42
  ):
@@ -38,6 +45,8 @@ class SimEngineSSARewriting(
38
45
  copies at each use location.
39
46
  """
40
47
 
48
+ state: RewritingState
49
+
41
50
  def __init__(
42
51
  self,
43
52
  project,
@@ -45,7 +54,7 @@ class SimEngineSSARewriting(
45
54
  sp_tracker,
46
55
  udef_to_phiid: dict[tuple, set[int]],
47
56
  phiid_to_loc: dict[int, tuple[int, int | None]],
48
- stackvar_locs: dict[int, int],
57
+ stackvar_locs: dict[int, set[int]],
49
58
  ail_manager: Manager,
50
59
  vvar_id_start: int = 0,
51
60
  bp_as_gpr: bool = False,
@@ -55,13 +64,15 @@ class SimEngineSSARewriting(
55
64
 
56
65
  self.sp_tracker = sp_tracker
57
66
  self.bp_as_gpr = bp_as_gpr
58
- self.def_to_vvid: dict[tuple[int, int | None, int, Expression | Statement], int] = {}
67
+ self.def_to_vvid: dict[tuple[int, int | None, int, DefExprType, AT], int] = {}
59
68
  self.stackvar_locs = stackvar_locs
60
69
  self.udef_to_phiid = udef_to_phiid
61
70
  self.phiid_to_loc = phiid_to_loc
62
71
  self.rewrite_tmps = rewrite_tmps
63
72
  self.ail_manager = ail_manager
64
73
 
74
+ self.secondary_stackvars: set[int] = set()
75
+
65
76
  self._current_vvar_id = vvar_id_start
66
77
 
67
78
  @property
@@ -103,6 +114,7 @@ class SimEngineSSARewriting(
103
114
  elif stmt.dst.category == VirtualVariableCategory.STACK:
104
115
  self.state.stackvars[stmt.dst.stack_offset][stmt.dst.size] = stmt.dst
105
116
  elif stmt.dst.category == VirtualVariableCategory.TMP:
117
+ assert stmt.dst.tmp_idx is not None
106
118
  self.state.tmps[stmt.dst.tmp_idx] = stmt.dst
107
119
  new_dst = None
108
120
  else:
@@ -134,10 +146,11 @@ class SimEngineSSARewriting(
134
146
  base_reg_vvar = self._replace_def_expr(
135
147
  self.block.addr, self.block.idx, self.stmt_idx, base_reg_expr
136
148
  )
149
+ assert base_reg_vvar is not None
137
150
  stmt_base_reg = Assignment(
138
151
  self.ail_manager.next_atom(),
139
152
  base_reg_vvar,
140
- self._reg_update_expr(
153
+ self._partial_update_expr(
141
154
  existing_base_reg_vvar, base_offset, base_size, new_dst, stmt.dst.reg_offset, stmt.dst.size
142
155
  ),
143
156
  **stmt.tags,
@@ -160,12 +173,40 @@ class SimEngineSSARewriting(
160
173
  return new_stmt
161
174
  return None
162
175
 
163
- def _handle_stmt_Store(self, stmt: Store) -> Store | Assignment | None:
176
+ def _handle_stmt_Store(self, stmt: Store) -> Store | Assignment | tuple[Assignment, ...] | None:
164
177
  new_data = self._expr(stmt.data)
165
178
  if stmt.guard is None:
179
+ # the variable
166
180
  vvar = self._replace_def_store(self.block.addr, self.block.idx, self.stmt_idx, stmt)
167
181
  if vvar is not None:
168
- return Assignment(stmt.idx, vvar, stmt.data if new_data is None else new_data, **stmt.tags)
182
+ assert isinstance(stmt.addr, StackBaseOffset) and isinstance(stmt.addr.offset, int)
183
+
184
+ # remove everything else that overlaps with the full (base) stack variable
185
+ # the full stack variable is kept around because it's always updated immediately and will be used in
186
+ # case of partial stack variable update
187
+ self._clear_overlapping_stackvars(stmt.addr.offset, stmt.size, remove_base_stackvar=False)
188
+
189
+ data = stmt.data if new_data is None else new_data
190
+ vvar_assignment = Assignment(stmt.idx, vvar, data, **stmt.tags)
191
+
192
+ full_size = self._get_stack_var_full_size(stmt)
193
+ assert full_size is not None
194
+ if vvar.size >= full_size:
195
+ return vvar_assignment
196
+
197
+ # update the full variable
198
+ existing_full_vvar = self._replace_use_load(Load(None, stmt.addr, full_size, stmt.endness))
199
+ vvar_full = self._replace_def_store(
200
+ self.block.addr, self.block.idx, self.stmt_idx, stmt, force_size=full_size
201
+ )
202
+ if existing_full_vvar is not None and vvar_full is not None:
203
+ self.secondary_stackvars.add(vvar_full.varid)
204
+ full_data = self._partial_update_expr(
205
+ existing_full_vvar, stmt.addr.offset, full_size, vvar, stmt.addr.offset, stmt.size
206
+ )
207
+ full_assignment = Assignment(stmt.idx, vvar_full, full_data, **stmt.tags)
208
+ return vvar_assignment, full_assignment
209
+ return vvar_assignment
169
210
 
170
211
  # fall back to Store
171
212
  new_addr = self._expr(stmt.addr)
@@ -210,7 +251,7 @@ class SimEngineSSARewriting(
210
251
  def _handle_stmt_Call(self, stmt: Call) -> Call | None:
211
252
  changed = False
212
253
 
213
- new_target = self._replace_use_expr(stmt.target)
254
+ new_target = self._replace_use_expr(stmt.target) if not isinstance(stmt.target, str) else None
214
255
  new_ret_expr = (
215
256
  self._replace_def_expr(self.block.addr, self.block.idx, self.stmt_idx, stmt.ret_expr)
216
257
  if stmt.ret_expr is not None
@@ -275,7 +316,7 @@ class SimEngineSSARewriting(
275
316
  assert isinstance(dirty, DirtyExpression)
276
317
  return DirtyStatement(stmt.idx, dirty, **stmt.tags)
277
318
 
278
- def _handle_expr_Register(self, expr: Register) -> VirtualVariable | None:
319
+ def _handle_expr_Register(self, expr: Register) -> VirtualVariable | Expression | None:
279
320
  return self._replace_use_reg(expr)
280
321
 
281
322
  def _handle_expr_Tmp(self, expr: Tmp) -> VirtualVariable | None:
@@ -460,9 +501,9 @@ class SimEngineSSARewriting(
460
501
  # Expression replacement
461
502
  #
462
503
 
463
- def _reg_update_expr(
504
+ def _partial_update_expr(
464
505
  self,
465
- existing_vvar: VirtualVariable,
506
+ existing_vvar: Expression,
466
507
  base_offset: int,
467
508
  base_size: int,
468
509
  new_vvar: VirtualVariable,
@@ -546,7 +587,7 @@ class SimEngineSSARewriting(
546
587
  """
547
588
 
548
589
  # get the virtual variable ID
549
- vvid = self.get_vvid_by_def(block_addr, block_idx, stmt_idx, expr)
590
+ vvid = self.get_vvid_by_def(block_addr, block_idx, stmt_idx, atoms.Register(expr.reg_offset, expr.size), "s")
550
591
  return VirtualVariable(
551
592
  expr.idx,
552
593
  vvid,
@@ -578,32 +619,51 @@ class SimEngineSSARewriting(
578
619
  )
579
620
  self.state.registers[base_off][base_size] = vvar
580
621
  return vvar
581
- return self.state.registers[base_off][base_size]
622
+ existing_var = self.state.registers[base_off][base_size]
623
+ assert existing_var is not None
624
+ return existing_var
625
+
626
+ def _get_stack_var_full_size(self, stmt: Store) -> int | None:
627
+ if (
628
+ isinstance(stmt.addr, StackBaseOffset)
629
+ and isinstance(stmt.addr.offset, int)
630
+ and stmt.addr.offset in self.stackvar_locs
631
+ and stmt.size in self.stackvar_locs[stmt.addr.offset]
632
+ ):
633
+ return max(self.stackvar_locs[stmt.addr.offset])
634
+ return None
582
635
 
583
636
  def _replace_def_store(
584
- self, block_addr: int, block_idx: int | None, stmt_idx: int, stmt: Store
637
+ self, block_addr: int, block_idx: int | None, stmt_idx: int, stmt: Store, force_size: int | None = None
585
638
  ) -> VirtualVariable | None:
586
639
  if (
587
640
  isinstance(stmt.addr, StackBaseOffset)
588
641
  and isinstance(stmt.addr.offset, int)
589
642
  and stmt.addr.offset in self.stackvar_locs
590
- and stmt.size == self.stackvar_locs[stmt.addr.offset]
643
+ and stmt.size in self.stackvar_locs[stmt.addr.offset]
591
644
  ):
592
- vvar_id = self.get_vvid_by_def(block_addr, block_idx, stmt_idx, stmt)
645
+ size = stmt.size if force_size is None else force_size
646
+ vvar_id = self.get_vvid_by_def(
647
+ block_addr,
648
+ block_idx,
649
+ stmt_idx,
650
+ atoms.MemoryLocation(stmt.addr.offset, size, Endness(stmt.endness)),
651
+ "s",
652
+ )
593
653
  vvar = VirtualVariable(
594
654
  self.ail_manager.next_atom(),
595
655
  vvar_id,
596
- stmt.size * self.arch.byte_width,
656
+ size * self.arch.byte_width,
597
657
  category=VirtualVariableCategory.STACK,
598
658
  oident=stmt.addr.offset,
599
659
  **stmt.tags,
600
660
  )
601
- self.state.stackvars[stmt.addr.offset][stmt.size] = vvar
661
+ self.state.stackvars[stmt.addr.offset][size] = vvar
602
662
  return vvar
603
663
  return None
604
664
 
605
665
  def _replace_def_tmp(self, block_addr: int, block_idx: int | None, stmt_idx: int, expr: Tmp) -> VirtualVariable:
606
- vvid = self.get_vvid_by_def(block_addr, block_idx, stmt_idx, expr)
666
+ vvid = self.get_vvid_by_def(block_addr, block_idx, stmt_idx, atoms.Tmp(expr.tmp_idx, expr.size), "s")
607
667
  vvar = VirtualVariable(
608
668
  expr.idx,
609
669
  vvid,
@@ -615,7 +675,7 @@ class SimEngineSSARewriting(
615
675
  self.state.tmps[expr.tmp_idx] = vvar
616
676
  return vvar
617
677
 
618
- def _replace_use_expr(self, thing: Expression | Statement) -> VirtualVariable | None:
678
+ def _replace_use_expr(self, thing: Expression | Statement) -> VirtualVariable | Expression | None:
619
679
  """
620
680
  Return a new virtual variable for the given defined expression.
621
681
  """
@@ -670,7 +730,7 @@ class SimEngineSSARewriting(
670
730
  elif reg_expr.size > existing_size:
671
731
  # part of the variable exists... maybe it's a parameter?
672
732
  vvar = self.state.registers[reg_expr.reg_offset][existing_size]
673
- if vvar.category == VirtualVariableCategory.PARAMETER:
733
+ if vvar is not None and vvar.category == VirtualVariableCategory.PARAMETER:
674
734
  # just zero-extend it
675
735
  return Convert(
676
736
  self.ail_manager.next_atom(),
@@ -698,7 +758,7 @@ class SimEngineSSARewriting(
698
758
  shift_amount = Const(
699
759
  self.ail_manager.next_atom(),
700
760
  None,
701
- (reg_expr.reg_offset - vvar.oident) * self.arch.byte_width,
761
+ (reg_expr.reg_offset - vvar.reg_offset) * self.arch.byte_width,
702
762
  8,
703
763
  **reg_expr.tags,
704
764
  )
@@ -729,11 +789,17 @@ class SimEngineSSARewriting(
729
789
  isinstance(expr.addr, StackBaseOffset)
730
790
  and isinstance(expr.addr.offset, int)
731
791
  and expr.addr.offset in self.stackvar_locs
732
- and expr.size == self.stackvar_locs[expr.addr.offset]
792
+ and expr.size in self.stackvar_locs[expr.addr.offset]
733
793
  ):
734
794
  if expr.size not in self.state.stackvars[expr.addr.offset]:
735
795
  # create it on the fly
736
- vvar_id = self.get_vvid_by_def(self.block.addr, self.block.idx, self.stmt_idx, expr)
796
+ vvar_id = self.get_vvid_by_def(
797
+ self.block.addr,
798
+ self.block.idx,
799
+ self.stmt_idx,
800
+ atoms.MemoryLocation(expr.addr.offset, expr.size, Endness(expr.endness)),
801
+ "l",
802
+ )
737
803
  return VirtualVariable(
738
804
  self.ail_manager.next_atom(),
739
805
  vvar_id,
@@ -783,9 +849,9 @@ class SimEngineSSARewriting(
783
849
  #
784
850
 
785
851
  def get_vvid_by_def(
786
- self, block_addr: int, block_idx: int | None, stmt_idx: int, thing: Expression | Statement
852
+ self, block_addr: int, block_idx: int | None, stmt_idx: int, thing: DefExprType, access_type: AT
787
853
  ) -> int:
788
- key = block_addr, block_idx, stmt_idx, thing
854
+ key = block_addr, block_idx, stmt_idx, thing, access_type
789
855
  if key in self.def_to_vvid:
790
856
  return self.def_to_vvid[key]
791
857
  vvid = self.next_vvar_id()
@@ -802,6 +868,21 @@ class SimEngineSSARewriting(
802
868
  else:
803
869
  del self.state.registers[off]
804
870
 
871
+ def _clear_overlapping_stackvars(self, stack_offset: int, size: int, remove_base_stackvar: bool = True) -> None:
872
+ for off in range(stack_offset, stack_offset + size):
873
+ if off in self.state.stackvars:
874
+ if (
875
+ not remove_base_stackvar
876
+ and off in self.stackvar_locs
877
+ and off == stack_offset
878
+ and (base_size := max(self.stackvar_locs[off])) == size
879
+ and base_size in self.state.stackvars[off]
880
+ ):
881
+ if len(self.state.stackvars[off]) > 1:
882
+ self.state.stackvars[off] = {base_size: self.state.stackvars[off][base_size]}
883
+ else:
884
+ del self.state.stackvars[off]
885
+
805
886
  def _unreachable(self, *args, **kwargs):
806
887
  assert False
807
888
 
@@ -79,7 +79,7 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
79
79
  # calculate virtual variables and phi nodes
80
80
  self._udef_to_phiid: dict[tuple, set[int]] = None
81
81
  self._phiid_to_loc: dict[int, tuple[int, int | None]] = None
82
- self._stackvar_locs: dict[int, int] = None
82
+ self._stackvar_locs: dict[int, set[int]] = None
83
83
  self._calculate_virtual_variables(ail_graph, traversal.def_to_loc, traversal.loc_to_defs)
84
84
 
85
85
  # insert phi variables and rewrite uses
@@ -97,6 +97,7 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
97
97
  self._func_args,
98
98
  vvar_id_start=vvar_id_start,
99
99
  )
100
+ self.secondary_stackvars = rewriter.secondary_stackvars
100
101
  self.out_graph = rewriter.out_graph
101
102
  self.max_vvar_id = rewriter.max_vvar_id
102
103
 
@@ -130,7 +131,7 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
130
131
  if self._func_args:
131
132
  for func_arg in self._func_args:
132
133
  if func_arg.oident[0] == VirtualVariableCategory.STACK:
133
- stackvar_locs[func_arg.oident[1]] = func_arg.size
134
+ stackvar_locs[func_arg.oident[1]] = {func_arg.size}
134
135
  sorted_stackvar_offs = sorted(stackvar_locs)
135
136
  else:
136
137
  stackvar_locs = {}
@@ -157,8 +158,13 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
157
158
  off = sorted_stackvar_offs[i]
158
159
  if off >= def_.addr.offset + def_.size:
159
160
  break
160
- udef_to_defs[("stack", off, stackvar_locs[off])].add(def_)
161
- udef_to_blockkeys[("stack", off, stackvar_locs[off])].add((loc.block_addr, loc.block_idx))
161
+ full_sz = max(stackvar_locs[off])
162
+ udef_to_defs[("stack", off, full_sz)].add(def_)
163
+ udef_to_blockkeys[("stack", off, full_sz)].add((loc.block_addr, loc.block_idx))
164
+ # add a definition for the partial stack variable
165
+ if def_.size in stackvar_locs[off] and def_.size < full_sz:
166
+ udef_to_defs[("stack", off, def_.size)].add(def_)
167
+ udef_to_blockkeys[("stack", off, def_.size)].add((loc.block_addr, loc.block_idx))
162
168
  elif isinstance(def_, Tmp):
163
169
  # Tmps are local to each block and do not need phi nodes
164
170
  pass
@@ -197,7 +203,15 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
197
203
  return last_frontier
198
204
 
199
205
  @staticmethod
200
- def _synthesize_stackvar_locs(defs: list[Store]) -> dict[int, int]:
206
+ def _synthesize_stackvar_locs(defs: list[Store]) -> dict[int, set[int]]:
207
+ """
208
+ Derive potential locations (in terms of offsets and sizes) for stack variables based on all stack variable
209
+ definitions provided.
210
+
211
+ :param defs: Store definitions.
212
+ :return: A dictionary of stack variable offsets and their sizes.
213
+ """
214
+
201
215
  accesses: defaultdict[int, set[int]] = defaultdict(set)
202
216
  offs: set[int] = set()
203
217
 
@@ -208,7 +222,7 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
208
222
  offs.add(stack_off)
209
223
 
210
224
  sorted_offs = sorted(offs)
211
- locs: dict[int, int] = {}
225
+ locs: dict[int, set[int]] = {}
212
226
  for idx, off in enumerate(sorted_offs):
213
227
  sorted_sizes = sorted(accesses[off])
214
228
  if idx < len(sorted_offs) - 1:
@@ -217,14 +231,8 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
217
231
  else:
218
232
  allowed_sizes = sorted_sizes
219
233
 
220
- if len(allowed_sizes) == 1:
221
- locs[off] = allowed_sizes[0]
222
- # else:
223
- # last_off = off
224
- # for a in allowed_sizes:
225
- # locs[off + a] = off + a - last_off
226
- # last_off = off + a
227
- # TODO: Update locs for sizes beyond allowed_sizes
234
+ if allowed_sizes:
235
+ locs[off] = set(allowed_sizes)
228
236
 
229
237
  return locs
230
238