angr 9.2.140__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 (40) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/calling_convention/calling_convention.py +88 -32
  3. angr/analyses/calling_convention/fact_collector.py +44 -18
  4. angr/analyses/calling_convention/utils.py +3 -1
  5. angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +9 -8
  6. angr/analyses/decompiler/ail_simplifier.py +48 -20
  7. angr/analyses/decompiler/callsite_maker.py +24 -11
  8. angr/analyses/decompiler/clinic.py +10 -0
  9. angr/analyses/decompiler/decompiler.py +1 -0
  10. angr/analyses/decompiler/optimization_passes/duplication_reverter/duplication_reverter.py +3 -1
  11. angr/analyses/decompiler/optimization_passes/flip_boolean_cmp.py +21 -2
  12. angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +84 -15
  13. angr/analyses/decompiler/optimization_passes/optimization_pass.py +76 -1
  14. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +51 -7
  15. angr/analyses/decompiler/peephole_optimizations/eager_eval.py +44 -7
  16. angr/analyses/decompiler/region_identifier.py +6 -4
  17. angr/analyses/decompiler/region_simplifiers/expr_folding.py +32 -18
  18. angr/analyses/decompiler/region_simplifiers/region_simplifier.py +4 -1
  19. angr/analyses/decompiler/ssailification/rewriting.py +23 -15
  20. angr/analyses/decompiler/ssailification/rewriting_engine.py +105 -24
  21. angr/analyses/decompiler/ssailification/ssailification.py +22 -14
  22. angr/analyses/decompiler/structured_codegen/c.py +73 -137
  23. angr/analyses/decompiler/structuring/dream.py +1 -1
  24. angr/analyses/decompiler/structuring/phoenix.py +6 -1
  25. angr/analyses/decompiler/structuring/structurer_base.py +2 -1
  26. angr/analyses/decompiler/utils.py +46 -20
  27. angr/analyses/s_reaching_definitions/s_rda_view.py +43 -25
  28. angr/analyses/variable_recovery/engine_ail.py +1 -1
  29. angr/analyses/variable_recovery/engine_vex.py +20 -4
  30. angr/calling_conventions.py +15 -10
  31. angr/factory.py +8 -3
  32. angr/knowledge_plugins/variables/variable_manager.py +7 -5
  33. angr/simos/simos.py +3 -1
  34. angr/utils/types.py +48 -0
  35. {angr-9.2.140.dist-info → angr-9.2.141.dist-info}/METADATA +6 -6
  36. {angr-9.2.140.dist-info → angr-9.2.141.dist-info}/RECORD +40 -39
  37. {angr-9.2.140.dist-info → angr-9.2.141.dist-info}/LICENSE +0 -0
  38. {angr-9.2.140.dist-info → angr-9.2.141.dist-info}/WHEEL +0 -0
  39. {angr-9.2.140.dist-info → angr-9.2.141.dist-info}/entry_points.txt +0 -0
  40. {angr-9.2.140.dist-info → angr-9.2.141.dist-info}/top_level.txt +0 -0
@@ -36,19 +36,21 @@ class StatementLocation(LocationBase):
36
36
  __slots__ = (
37
37
  "block_addr",
38
38
  "block_idx",
39
+ "phi_stmt",
39
40
  "stmt_idx",
40
41
  )
41
42
 
42
- def __init__(self, block_addr, block_idx, stmt_idx):
43
+ def __init__(self, block_addr, block_idx, stmt_idx, phi_stmt: bool = False):
43
44
  self.block_addr = block_addr
44
45
  self.block_idx = block_idx
45
46
  self.stmt_idx = stmt_idx
47
+ self.phi_stmt = phi_stmt
46
48
 
47
49
  def __repr__(self):
48
- return f"Loc: Statement@{self.block_addr:x}.{self.block_idx}-{self.stmt_idx}"
50
+ return f"Loc: Statement@{self.block_addr:x}.{self.block_idx}-{self.stmt_idx}{' phi' if self.phi_stmt else ''}"
49
51
 
50
52
  def __hash__(self):
51
- return hash((StatementLocation, self.block_addr, self.block_idx, self.stmt_idx))
53
+ return hash((StatementLocation, self.block_addr, self.block_idx, self.stmt_idx, self.phi_stmt))
52
54
 
53
55
  def __eq__(self, other):
54
56
  return (
@@ -56,10 +58,11 @@ class StatementLocation(LocationBase):
56
58
  and self.block_addr == other.block_addr
57
59
  and self.block_idx == other.block_idx
58
60
  and self.stmt_idx == other.stmt_idx
61
+ and self.phi_stmt == other.phi_stmt
59
62
  )
60
63
 
61
64
  def copy(self):
62
- return StatementLocation(self.block_addr, self.block_idx, self.stmt_idx)
65
+ return StatementLocation(self.block_addr, self.block_idx, self.stmt_idx, phi_stmt=self.phi_stmt)
63
66
 
64
67
 
65
68
  class ExpressionLocation(LocationBase):
@@ -71,23 +74,28 @@ class ExpressionLocation(LocationBase):
71
74
  "block_addr",
72
75
  "block_idx",
73
76
  "expr_idx",
77
+ "phi_stmt",
74
78
  "stmt_idx",
75
79
  )
76
80
 
77
- def __init__(self, block_addr, block_idx, stmt_idx, expr_idx):
81
+ def __init__(self, block_addr, block_idx, stmt_idx, expr_idx, phi_stmt: bool = False):
78
82
  self.block_addr = block_addr
79
83
  self.block_idx = block_idx
80
84
  self.stmt_idx = stmt_idx
81
85
  self.expr_idx = expr_idx
86
+ self.phi_stmt = phi_stmt
82
87
 
83
88
  def __repr__(self):
84
- return f"Loc: Expression@{self.block_addr:x}.{self.block_idx}-{self.stmt_idx}[{self.expr_idx}]"
89
+ return (
90
+ f"Loc: Expression@{self.block_addr:x}.{self.block_idx}-{self.stmt_idx}[{self.expr_idx}]"
91
+ f"{'phi' if self.phi_stmt else ''}"
92
+ )
85
93
 
86
94
  def statement_location(self) -> StatementLocation:
87
- return StatementLocation(self.block_addr, self.block_idx, self.stmt_idx)
95
+ return StatementLocation(self.block_addr, self.block_idx, self.stmt_idx, phi_stmt=self.phi_stmt)
88
96
 
89
97
  def __hash__(self):
90
- return hash((ExpressionLocation, self.block_addr, self.block_idx, self.stmt_idx, self.expr_idx))
98
+ return hash((ExpressionLocation, self.block_addr, self.block_idx, self.stmt_idx, self.expr_idx, self.phi_stmt))
91
99
 
92
100
  def __eq__(self, other):
93
101
  return (
@@ -96,6 +104,7 @@ class ExpressionLocation(LocationBase):
96
104
  and self.block_idx == other.block_idx
97
105
  and self.stmt_idx == other.stmt_idx
98
106
  and self.expr_idx == other.expr_idx
107
+ and self.phi_stmt == other.phi_stmt
99
108
  )
100
109
 
101
110
 
@@ -201,7 +210,18 @@ class ExpressionUseFinder(AILBlockWalker):
201
210
  if isinstance(expr, ailment.Expr.VirtualVariable) and expr.was_reg:
202
211
  if not (isinstance(stmt, ailment.Stmt.Assignment) and stmt.dst is expr):
203
212
  if block is not None:
204
- self.uses[expr.varid].add((expr, ExpressionLocation(block.addr, block.idx, stmt_idx, expr_idx)))
213
+ self.uses[expr.varid].add(
214
+ (
215
+ expr,
216
+ ExpressionLocation(
217
+ block.addr,
218
+ block.idx,
219
+ stmt_idx,
220
+ expr_idx,
221
+ phi_stmt=stmt is not None and is_phi_assignment(stmt),
222
+ ),
223
+ )
224
+ )
205
225
  else:
206
226
  self.uses[expr.varid].add((expr, None))
207
227
  return None
@@ -239,11 +259,7 @@ class ExpressionCounter(SequenceWalker):
239
259
  if isinstance(stmt, ailment.Stmt.Assignment):
240
260
  if is_phi_assignment(stmt):
241
261
  return
242
- if (
243
- isinstance(stmt.dst, ailment.Expr.VirtualVariable)
244
- and stmt.dst.was_reg
245
- and stmt.dst.variable is not None
246
- ):
262
+ if isinstance(stmt.dst, ailment.Expr.VirtualVariable) and stmt.dst.was_reg:
247
263
  # dependency
248
264
  dependency_finder = ExpressionUseFinder()
249
265
  dependency_finder.walk_expression(stmt.src)
@@ -260,7 +276,6 @@ class ExpressionCounter(SequenceWalker):
260
276
  isinstance(stmt, ailment.Stmt.Call)
261
277
  and isinstance(stmt.ret_expr, ailment.Expr.VirtualVariable)
262
278
  and stmt.ret_expr.was_reg
263
- and stmt.ret_expr.variable is not None
264
279
  ):
265
280
  dependency_finder = ExpressionUseFinder()
266
281
  dependency_finder.walk_expression(stmt)
@@ -279,8 +294,7 @@ class ExpressionCounter(SequenceWalker):
279
294
  use_finder = ExpressionUseFinder()
280
295
  for idx, stmt in enumerate(node.statements):
281
296
  self._handle_Statement(idx, stmt, node)
282
- if not is_phi_assignment(stmt):
283
- use_finder.walk_statement(stmt)
297
+ use_finder.walk_statement(stmt, block=node)
284
298
 
285
299
  for varid, content in use_finder.uses.items():
286
300
  if varid not in self.uses:
@@ -407,7 +421,7 @@ class InterferenceChecker(SequenceWalker):
407
421
  the_call = None
408
422
  if isinstance(stmt, Assignment) and isinstance(stmt.src, ailment.Stmt.Call):
409
423
  the_call = stmt.src
410
- elif isinstance(stmt, ailment.Stmt.Call):
424
+ elif isinstance(stmt, ailment.Stmt.Call) and not isinstance(stmt.target, str):
411
425
  the_call = stmt
412
426
  if the_call is not None:
413
427
  spotter.walk_expression(the_call.target)
@@ -116,10 +116,13 @@ class RegionSimplifier(Analysis):
116
116
  for var, uses in expr_counter.uses.items():
117
117
  if len(uses) == 1 and var in expr_counter.assignments and len(expr_counter.assignments[var]) == 1:
118
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
119
123
  if has_loads:
120
124
  # the definition has at least one load expression. we need to ensure there are no store statements
121
125
  # between the definition site and the use site
122
- _, use_expr_loc = next(iter(uses))
123
126
  if isinstance(use_expr_loc, ExpressionLocation):
124
127
  use_loc = use_expr_loc.statement_location()
125
128
  else:
@@ -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