angr 9.2.142__py3-none-win_amd64.whl → 9.2.143__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.
- angr/__init__.py +1 -1
- angr/analyses/calling_convention/calling_convention.py +9 -9
- angr/analyses/calling_convention/fact_collector.py +31 -9
- angr/analyses/cfg/indirect_jump_resolvers/const_resolver.py +12 -1
- angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +4 -1
- angr/analyses/complete_calling_conventions.py +18 -5
- angr/analyses/decompiler/ail_simplifier.py +90 -65
- angr/analyses/decompiler/optimization_passes/condition_constprop.py +49 -14
- angr/analyses/decompiler/optimization_passes/ite_region_converter.py +8 -0
- angr/analyses/decompiler/peephole_optimizations/simplify_pc_relative_loads.py +15 -1
- angr/analyses/decompiler/sequence_walker.py +8 -0
- angr/analyses/decompiler/utils.py +13 -0
- angr/analyses/s_propagator.py +40 -29
- angr/analyses/s_reaching_definitions/s_rda_model.py +45 -36
- angr/analyses/s_reaching_definitions/s_rda_view.py +6 -3
- angr/analyses/s_reaching_definitions/s_reaching_definitions.py +21 -21
- angr/analyses/variable_recovery/engine_ail.py +6 -6
- angr/calling_conventions.py +18 -8
- angr/lib/angr_native.dll +0 -0
- angr/procedures/definitions/linux_kernel.py +5 -0
- angr/utils/doms.py +40 -33
- angr/utils/ssa/__init__.py +21 -14
- angr/utils/ssa/vvar_uses_collector.py +2 -2
- {angr-9.2.142.dist-info → angr-9.2.143.dist-info}/METADATA +6 -6
- {angr-9.2.142.dist-info → angr-9.2.143.dist-info}/RECORD +29 -29
- {angr-9.2.142.dist-info → angr-9.2.143.dist-info}/LICENSE +0 -0
- {angr-9.2.142.dist-info → angr-9.2.143.dist-info}/WHEEL +0 -0
- {angr-9.2.142.dist-info → angr-9.2.143.dist-info}/entry_points.txt +0 -0
- {angr-9.2.142.dist-info → angr-9.2.143.dist-info}/top_level.txt +0 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
from typing import TYPE_CHECKING
|
|
3
|
+
from collections import defaultdict
|
|
3
4
|
|
|
4
5
|
import networkx
|
|
5
6
|
|
|
6
7
|
from ailment import AILBlockWalker, Block
|
|
7
|
-
from ailment.statement import ConditionalJump, Statement
|
|
8
|
+
from ailment.statement import ConditionalJump, Statement, Assignment
|
|
8
9
|
from ailment.expression import Const, BinaryOp, VirtualVariable
|
|
9
10
|
|
|
10
11
|
from angr.analyses.decompiler.utils import first_nonlabel_nonphi_statement
|
|
@@ -41,6 +42,7 @@ class CCondPropBlockWalker(AILBlockWalker):
|
|
|
41
42
|
self._new_block: Block | None = None # output
|
|
42
43
|
self.vvar_id = vvar_id
|
|
43
44
|
self.const_value = const_value
|
|
45
|
+
self.abort = False
|
|
44
46
|
|
|
45
47
|
def walk(self, block: Block):
|
|
46
48
|
self._new_block = None
|
|
@@ -48,6 +50,17 @@ class CCondPropBlockWalker(AILBlockWalker):
|
|
|
48
50
|
return self._new_block
|
|
49
51
|
|
|
50
52
|
def _handle_stmt(self, stmt_idx: int, stmt: Statement, block: Block): # type: ignore
|
|
53
|
+
if self.abort:
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
if isinstance(stmt, Assignment) and isinstance(stmt.dst, VirtualVariable) and stmt.dst.varid == self.vvar_id:
|
|
57
|
+
# we see the assignment of this virtual variable; this is the original block that creates this variable
|
|
58
|
+
# and checks if this variable is equal to a constant value. as such, we stop processing this block.
|
|
59
|
+
# an example appears in binary 1de5cda760f9ed80bb6f4a35edcebc86ccec14c49cf4775ddf2ffc3e05ff35f4, function
|
|
60
|
+
# 0x4657C0, blocks 0x465bd6 and 0x465a5c
|
|
61
|
+
self.abort = True
|
|
62
|
+
return
|
|
63
|
+
|
|
51
64
|
r = super()._handle_stmt(stmt_idx, stmt, block)
|
|
52
65
|
if r is not None:
|
|
53
66
|
# replace the original statement
|
|
@@ -58,7 +71,9 @@ class CCondPropBlockWalker(AILBlockWalker):
|
|
|
58
71
|
def _handle_VirtualVariable( # type: ignore
|
|
59
72
|
self, expr_idx: int, expr: VirtualVariable, stmt_idx: int, stmt: Statement, block: Block | None
|
|
60
73
|
) -> Const | None:
|
|
61
|
-
if expr.varid == self.vvar_id
|
|
74
|
+
if expr.varid == self.vvar_id and not (
|
|
75
|
+
isinstance(stmt, Assignment) and isinstance(stmt.dst, VirtualVariable) and stmt.dst.varid == self.vvar_id
|
|
76
|
+
):
|
|
62
77
|
return Const(expr.idx, None, self.const_value.value, self.const_value.bits, **expr.tags)
|
|
63
78
|
return None
|
|
64
79
|
|
|
@@ -80,19 +95,9 @@ class ConditionConstantPropagation(OptimizationPass):
|
|
|
80
95
|
|
|
81
96
|
def _check(self):
|
|
82
97
|
cconds = self._find_const_conditions()
|
|
83
|
-
if not cconds:
|
|
84
|
-
return False, None
|
|
85
|
-
return True, {"cconds": cconds}
|
|
86
|
-
|
|
87
|
-
@timethis
|
|
88
|
-
def _analyze(self, cache=None):
|
|
89
|
-
if not cache or cache.get("cconds", None) is None: # noqa: SIM108
|
|
90
|
-
cconds = self._find_const_conditions()
|
|
91
|
-
else:
|
|
92
|
-
cconds = cache["cconds"]
|
|
93
98
|
|
|
94
99
|
if not cconds:
|
|
95
|
-
return
|
|
100
|
+
return False, None
|
|
96
101
|
|
|
97
102
|
# group cconds according to their sources
|
|
98
103
|
cconds_by_src: dict[tuple[int, int | None], list[ConstantCondition]] = {}
|
|
@@ -102,6 +107,36 @@ class ConditionConstantPropagation(OptimizationPass):
|
|
|
102
107
|
cconds_by_src[src] = []
|
|
103
108
|
cconds_by_src[src].append(ccond)
|
|
104
109
|
|
|
110
|
+
# eliminate conflicting conditions
|
|
111
|
+
for src in list(cconds_by_src):
|
|
112
|
+
cconds = cconds_by_src[src]
|
|
113
|
+
vvar_id_to_values = defaultdict(set)
|
|
114
|
+
ccond_dict = {} # keyed by vvar_id; used for deduplication
|
|
115
|
+
for ccond in cconds:
|
|
116
|
+
vvar_id_to_values[ccond.vvar_id].add(ccond.value)
|
|
117
|
+
ccond_dict[ccond.vvar_id] = ccond
|
|
118
|
+
new_cconds = []
|
|
119
|
+
for vid, vvalues in vvar_id_to_values.items():
|
|
120
|
+
if len(vvalues) == 1:
|
|
121
|
+
new_cconds.append(ccond_dict[vid])
|
|
122
|
+
if new_cconds:
|
|
123
|
+
cconds_by_src[src] = new_cconds
|
|
124
|
+
else:
|
|
125
|
+
del cconds_by_src[src]
|
|
126
|
+
|
|
127
|
+
if not cconds_by_src:
|
|
128
|
+
return False, None
|
|
129
|
+
return True, {"cconds_by_src": cconds_by_src}
|
|
130
|
+
|
|
131
|
+
@timethis
|
|
132
|
+
def _analyze(self, cache=None):
|
|
133
|
+
if not cache or cache.get("cconds_by_src", None) is None:
|
|
134
|
+
return
|
|
135
|
+
cconds_by_src = cache["cconds_by_src"]
|
|
136
|
+
|
|
137
|
+
if not cconds_by_src:
|
|
138
|
+
return
|
|
139
|
+
|
|
105
140
|
# calculate a dominance frontier for each block
|
|
106
141
|
entry_node_addr, entry_node_idx = self.entry_node_addr
|
|
107
142
|
entry_node = self._get_block(entry_node_addr, idx=entry_node_idx)
|
|
@@ -114,7 +149,7 @@ class ConditionConstantPropagation(OptimizationPass):
|
|
|
114
149
|
continue
|
|
115
150
|
|
|
116
151
|
for ccond in cconds:
|
|
117
|
-
for _, loc in rda.all_vvar_uses[
|
|
152
|
+
for _, loc in rda.all_vvar_uses[ccond.vvar_id]:
|
|
118
153
|
loc_block = self._get_block(loc.block_addr, idx=loc.block_idx)
|
|
119
154
|
if loc_block is None:
|
|
120
155
|
continue
|
|
@@ -192,6 +192,14 @@ class ITERegionConverter(OptimizationPass):
|
|
|
192
192
|
if region_head not in self._graph or region_tail not in self._graph:
|
|
193
193
|
return False
|
|
194
194
|
|
|
195
|
+
# ensure all phi statements in region_tail have valid source vvars
|
|
196
|
+
for stmt in region_tail.statements:
|
|
197
|
+
if not is_phi_assignment(stmt):
|
|
198
|
+
continue
|
|
199
|
+
for _, vvar in stmt.src.src_and_vvars:
|
|
200
|
+
if vvar is None:
|
|
201
|
+
return False
|
|
202
|
+
|
|
195
203
|
#
|
|
196
204
|
# create a new region_head
|
|
197
205
|
#
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# pylint:disable=too-many-boolean-expressions
|
|
1
2
|
from __future__ import annotations
|
|
2
3
|
from ailment.expression import BinaryOp, Const, Load
|
|
3
4
|
|
|
@@ -20,10 +21,23 @@ class SimplifyPcRelativeLoads(PeepholeOptimizationExprBase):
|
|
|
20
21
|
if expr.op == "Add" and len(expr.operands) == 2 and isinstance(expr.operands[0], Load):
|
|
21
22
|
op0, op1 = expr.operands
|
|
22
23
|
|
|
24
|
+
assert self.project is not None
|
|
25
|
+
if not hasattr(expr, "ins_addr"):
|
|
26
|
+
return expr
|
|
27
|
+
assert expr.ins_addr is not None
|
|
28
|
+
|
|
23
29
|
# check if op1 is PC
|
|
24
|
-
if
|
|
30
|
+
if (
|
|
31
|
+
isinstance(op1, Const)
|
|
32
|
+
and op1.is_int
|
|
33
|
+
and hasattr(expr, "ins_addr")
|
|
34
|
+
and is_pc(self.project, expr.ins_addr, op1.value) # type: ignore
|
|
35
|
+
and isinstance(op0.addr, Const)
|
|
36
|
+
and op0.addr.is_int
|
|
37
|
+
):
|
|
25
38
|
# check if op0.addr points to a read-only section
|
|
26
39
|
addr = op0.addr.value
|
|
40
|
+
assert isinstance(addr, int)
|
|
27
41
|
if is_in_readonly_section(self.project, addr) or is_in_readonly_segment(self.project, addr):
|
|
28
42
|
# found it!
|
|
29
43
|
# do the load first
|
|
@@ -186,6 +186,14 @@ class SequenceWalker:
|
|
|
186
186
|
new_condition = (
|
|
187
187
|
self._handle(node.condition, parent=node, label="condition") if node.condition is not None else None
|
|
188
188
|
)
|
|
189
|
+
|
|
190
|
+
# note that initializer and iterator are both statements, so they can return empty tuples
|
|
191
|
+
# TODO: Handle the case where multiple statements are returned
|
|
192
|
+
if new_initializer == ():
|
|
193
|
+
new_initializer = None
|
|
194
|
+
if new_iterator == ():
|
|
195
|
+
new_iterator = None
|
|
196
|
+
|
|
189
197
|
seq_node = self._handle(node.sequence_node, parent=node, label="body", index=0)
|
|
190
198
|
if seq_node is not None or new_initializer is not None or new_iterator is not None or new_condition is not None:
|
|
191
199
|
return LoopNode(
|
|
@@ -214,6 +214,19 @@ def switch_extract_switch_expr_from_jump_target(target: ailment.Expr.Expression)
|
|
|
214
214
|
target = target.operands[0]
|
|
215
215
|
else:
|
|
216
216
|
return None
|
|
217
|
+
elif target.op == "And":
|
|
218
|
+
# it must be and-ing the target expr with a constant
|
|
219
|
+
if (
|
|
220
|
+
isinstance(target.operands[1], ailment.Expr.VirtualVariable)
|
|
221
|
+
and isinstance(target.operands[0], ailment.Expr.Const)
|
|
222
|
+
) or (
|
|
223
|
+
isinstance(target.operands[0], ailment.Expr.VirtualVariable)
|
|
224
|
+
and isinstance(target.operands[1], ailment.Expr.Const)
|
|
225
|
+
):
|
|
226
|
+
break
|
|
227
|
+
return None
|
|
228
|
+
else:
|
|
229
|
+
return None
|
|
217
230
|
elif isinstance(target, ailment.Expr.Load):
|
|
218
231
|
# we want the address!
|
|
219
232
|
found_load = True
|
angr/analyses/s_propagator.py
CHANGED
|
@@ -26,6 +26,7 @@ from angr.utils.ssa import (
|
|
|
26
26
|
get_vvar_deflocs,
|
|
27
27
|
has_ite_expr,
|
|
28
28
|
has_ite_stmt,
|
|
29
|
+
has_tmp_expr,
|
|
29
30
|
is_phi_assignment,
|
|
30
31
|
is_const_assignment,
|
|
31
32
|
is_const_and_vvar_assignment,
|
|
@@ -126,7 +127,7 @@ class SPropagatorAnalysis(Analysis):
|
|
|
126
127
|
# update vvar_deflocs using function arguments
|
|
127
128
|
if self.func_args:
|
|
128
129
|
for func_arg in self.func_args:
|
|
129
|
-
vvar_deflocs[func_arg] = ExternalCodeLocation()
|
|
130
|
+
vvar_deflocs[func_arg.varid] = func_arg, ExternalCodeLocation()
|
|
130
131
|
|
|
131
132
|
# find all ret sites and indirect jump sites
|
|
132
133
|
retsites: set[tuple[int, int | None, int]] = set()
|
|
@@ -143,11 +144,11 @@ class SPropagatorAnalysis(Analysis):
|
|
|
143
144
|
# find constant and other propagatable assignments
|
|
144
145
|
vvarid_to_vvar = {}
|
|
145
146
|
const_vvars: dict[int, Const] = {}
|
|
146
|
-
for vvar, defloc in vvar_deflocs.items():
|
|
147
|
+
for vvar_id, (vvar, defloc) in vvar_deflocs.items():
|
|
147
148
|
if not vvar.was_reg and not vvar.was_parameter:
|
|
148
149
|
continue
|
|
149
150
|
|
|
150
|
-
vvarid_to_vvar[
|
|
151
|
+
vvarid_to_vvar[vvar_id] = vvar
|
|
151
152
|
if isinstance(defloc, ExternalCodeLocation):
|
|
152
153
|
continue
|
|
153
154
|
|
|
@@ -160,8 +161,8 @@ class SPropagatorAnalysis(Analysis):
|
|
|
160
161
|
if r:
|
|
161
162
|
# replace wherever it's used
|
|
162
163
|
assert v is not None
|
|
163
|
-
const_vvars[
|
|
164
|
-
for vvar_at_use, useloc in vvar_uselocs[
|
|
164
|
+
const_vvars[vvar_id] = v
|
|
165
|
+
for vvar_at_use, useloc in vvar_uselocs[vvar_id]:
|
|
165
166
|
replacements[useloc][vvar_at_use] = v
|
|
166
167
|
continue
|
|
167
168
|
|
|
@@ -189,10 +190,10 @@ class SPropagatorAnalysis(Analysis):
|
|
|
189
190
|
if self.mode == "function":
|
|
190
191
|
assert self.func_graph is not None
|
|
191
192
|
|
|
192
|
-
for vvar, defloc in vvar_deflocs.items():
|
|
193
|
-
if
|
|
193
|
+
for vvar_id, (vvar, defloc) in vvar_deflocs.items():
|
|
194
|
+
if vvar_id not in vvar_uselocs:
|
|
194
195
|
continue
|
|
195
|
-
if
|
|
196
|
+
if vvar_id in const_vvars:
|
|
196
197
|
continue
|
|
197
198
|
if isinstance(defloc, ExternalCodeLocation):
|
|
198
199
|
continue
|
|
@@ -200,11 +201,13 @@ class SPropagatorAnalysis(Analysis):
|
|
|
200
201
|
assert defloc.block_addr is not None
|
|
201
202
|
assert defloc.stmt_idx is not None
|
|
202
203
|
|
|
204
|
+
vvar_uselocs_set = set(vvar_uselocs[vvar_id]) # deduplicate
|
|
205
|
+
|
|
203
206
|
block = blocks[(defloc.block_addr, defloc.block_idx)]
|
|
204
207
|
stmt = block.statements[defloc.stmt_idx]
|
|
205
208
|
if (
|
|
206
209
|
(vvar.was_reg or vvar.was_parameter)
|
|
207
|
-
and len(
|
|
210
|
+
and len(vvar_uselocs_set) <= 2
|
|
208
211
|
and isinstance(stmt, Assignment)
|
|
209
212
|
and isinstance(stmt.src, Load)
|
|
210
213
|
):
|
|
@@ -215,43 +218,46 @@ class SPropagatorAnalysis(Analysis):
|
|
|
215
218
|
# v1 = v0 + 1;
|
|
216
219
|
# }
|
|
217
220
|
can_replace = True
|
|
218
|
-
for _, vvar_useloc in
|
|
221
|
+
for _, vvar_useloc in vvar_uselocs_set:
|
|
219
222
|
if has_store_stmt_in_between_stmts(self.func_graph, blocks, defloc, vvar_useloc):
|
|
220
223
|
can_replace = False
|
|
221
224
|
|
|
222
225
|
if can_replace:
|
|
223
226
|
# we can propagate this load because there is no store between its def and use
|
|
224
|
-
for vvar_used, vvar_useloc in
|
|
227
|
+
for vvar_used, vvar_useloc in vvar_uselocs_set:
|
|
225
228
|
replacements[vvar_useloc][vvar_used] = stmt.src
|
|
226
229
|
continue
|
|
227
230
|
|
|
228
231
|
if (
|
|
229
232
|
(vvar.was_reg or vvar.was_stack)
|
|
230
|
-
and len(
|
|
233
|
+
and len(vvar_uselocs_set) == 2
|
|
234
|
+
and isinstance(stmt, Assignment)
|
|
231
235
|
and not is_phi_assignment(stmt)
|
|
232
236
|
):
|
|
233
237
|
# a special case: in a typical switch-case construct, a variable may be used once for comparison
|
|
234
238
|
# for the default case and then used again for constructing the jump target. we can propagate this
|
|
235
239
|
# variable for such cases.
|
|
236
|
-
uselocs = {loc for _, loc in
|
|
237
|
-
if self.is_vvar_used_for_addr_loading_switch_case(uselocs, blocks):
|
|
238
|
-
for vvar_used, vvar_useloc in
|
|
240
|
+
uselocs = {loc for _, loc in vvar_uselocs_set}
|
|
241
|
+
if self.is_vvar_used_for_addr_loading_switch_case(uselocs, blocks) and not has_tmp_expr(stmt.src):
|
|
242
|
+
for vvar_used, vvar_useloc in vvar_uselocs_set:
|
|
239
243
|
replacements[vvar_useloc][vvar_used] = stmt.src
|
|
240
244
|
# mark the vvar as dead and should be removed
|
|
241
245
|
self.model.dead_vvar_ids.add(vvar.varid)
|
|
242
246
|
continue
|
|
243
247
|
|
|
244
248
|
if vvar.was_reg or vvar.was_parameter:
|
|
245
|
-
if len(
|
|
246
|
-
vvar_used, vvar_useloc = next(iter(
|
|
247
|
-
if
|
|
248
|
-
|
|
249
|
+
if len(vvar_uselocs_set) == 1:
|
|
250
|
+
vvar_used, vvar_useloc = next(iter(vvar_uselocs_set))
|
|
251
|
+
if (
|
|
252
|
+
is_const_vvar_load_assignment(stmt)
|
|
253
|
+
and not has_store_stmt_in_between_stmts(self.func_graph, blocks, defloc, vvar_useloc)
|
|
254
|
+
and not has_tmp_expr(stmt.src)
|
|
249
255
|
):
|
|
250
256
|
# we can propagate this load because there is no store between its def and use
|
|
251
257
|
replacements[vvar_useloc][vvar_used] = stmt.src
|
|
252
258
|
continue
|
|
253
259
|
|
|
254
|
-
if is_const_and_vvar_assignment(stmt):
|
|
260
|
+
if is_const_and_vvar_assignment(stmt) and not has_tmp_expr(stmt.src):
|
|
255
261
|
# if the useloc is a phi assignment statement, ensure that stmt.src is the same as the phi
|
|
256
262
|
# variable
|
|
257
263
|
assert vvar_useloc.block_addr is not None
|
|
@@ -273,18 +279,22 @@ class SPropagatorAnalysis(Analysis):
|
|
|
273
279
|
else:
|
|
274
280
|
non_exitsite_uselocs = [
|
|
275
281
|
loc
|
|
276
|
-
for _, loc in
|
|
282
|
+
for _, loc in vvar_uselocs_set
|
|
277
283
|
if (loc.block_addr, loc.block_idx, loc.stmt_idx) not in (retsites | jumpsites)
|
|
278
284
|
]
|
|
279
285
|
if is_const_and_vvar_assignment(stmt):
|
|
280
286
|
if len(non_exitsite_uselocs) == 1:
|
|
281
287
|
# this vvar is used once if we exclude its uses at ret sites or jump sites. we can
|
|
282
288
|
# propagate it
|
|
283
|
-
for vvar_used, vvar_useloc in
|
|
289
|
+
for vvar_used, vvar_useloc in vvar_uselocs_set:
|
|
284
290
|
replacements[vvar_useloc][vvar_used] = stmt.src
|
|
285
291
|
continue
|
|
286
292
|
|
|
287
|
-
if
|
|
293
|
+
if (
|
|
294
|
+
len(set(non_exitsite_uselocs)) == 1
|
|
295
|
+
and not has_ite_expr(stmt.src)
|
|
296
|
+
and not has_tmp_expr(stmt.src)
|
|
297
|
+
):
|
|
288
298
|
useloc = non_exitsite_uselocs[0]
|
|
289
299
|
assert useloc.block_addr is not None
|
|
290
300
|
assert useloc.stmt_idx is not None
|
|
@@ -292,13 +302,13 @@ class SPropagatorAnalysis(Analysis):
|
|
|
292
302
|
if stmt.src.depth <= 3 and not has_ite_stmt(useloc_stmt):
|
|
293
303
|
# remove duplicate use locs (e.g., if the variable is used multiple times by the
|
|
294
304
|
# same statement) - but ensure stmt is simple enough
|
|
295
|
-
for vvar_used, vvar_useloc in
|
|
305
|
+
for vvar_used, vvar_useloc in vvar_uselocs_set:
|
|
296
306
|
replacements[vvar_useloc][vvar_used] = stmt.src
|
|
297
307
|
continue
|
|
298
308
|
|
|
299
309
|
# special logic for global variables: if it's used once or multiple times, and the variable is never
|
|
300
310
|
# updated before it's used, we will propagate the load
|
|
301
|
-
if (vvar.was_reg or vvar.was_parameter) and isinstance(stmt, Assignment):
|
|
311
|
+
if (vvar.was_reg or vvar.was_parameter) and isinstance(stmt, Assignment) and not has_tmp_expr(stmt.src):
|
|
302
312
|
stmt_src = stmt.src
|
|
303
313
|
# unpack conversions
|
|
304
314
|
while isinstance(stmt_src, Convert):
|
|
@@ -309,7 +319,7 @@ class SPropagatorAnalysis(Analysis):
|
|
|
309
319
|
and isinstance(stmt_src.addr.value, int)
|
|
310
320
|
):
|
|
311
321
|
gv_updated = False
|
|
312
|
-
for _vvar_used, vvar_useloc in
|
|
322
|
+
for _vvar_used, vvar_useloc in vvar_uselocs_set:
|
|
313
323
|
gv_updated |= self.is_global_variable_updated(
|
|
314
324
|
self.func_graph,
|
|
315
325
|
blocks,
|
|
@@ -320,12 +330,13 @@ class SPropagatorAnalysis(Analysis):
|
|
|
320
330
|
vvar_useloc,
|
|
321
331
|
)
|
|
322
332
|
if not gv_updated:
|
|
323
|
-
for vvar_used, vvar_useloc in
|
|
333
|
+
for vvar_used, vvar_useloc in vvar_uselocs_set:
|
|
324
334
|
replacements[vvar_useloc][vvar_used] = stmt.src
|
|
325
335
|
continue
|
|
326
336
|
|
|
327
337
|
for vvar_id, uselocs in vvar_uselocs.items():
|
|
328
338
|
vvar = next(iter(uselocs))[0] if vvar_id not in vvarid_to_vvar else vvarid_to_vvar[vvar_id]
|
|
339
|
+
vvar_uselocs_set = set(uselocs) # deduplicate
|
|
329
340
|
|
|
330
341
|
if self._sp_tracker is not None and vvar.category == VirtualVariableCategory.REGISTER:
|
|
331
342
|
if vvar.oident == self.project.arch.sp_offset:
|
|
@@ -334,7 +345,7 @@ class SPropagatorAnalysis(Analysis):
|
|
|
334
345
|
if "sp" in self.project.arch.registers
|
|
335
346
|
else None
|
|
336
347
|
)
|
|
337
|
-
for vvar_at_use, useloc in
|
|
348
|
+
for vvar_at_use, useloc in vvar_uselocs_set:
|
|
338
349
|
sb_offset = self._sp_tracker.offset_before(useloc.ins_addr, self.project.arch.sp_offset)
|
|
339
350
|
if sb_offset is not None:
|
|
340
351
|
v = StackBaseOffset(None, self.project.arch.bits, sb_offset)
|
|
@@ -349,7 +360,7 @@ class SPropagatorAnalysis(Analysis):
|
|
|
349
360
|
if "bp" in self.project.arch.registers
|
|
350
361
|
else None
|
|
351
362
|
)
|
|
352
|
-
for vvar_at_use, useloc in
|
|
363
|
+
for vvar_at_use, useloc in vvar_uselocs_set:
|
|
353
364
|
sb_offset = self._sp_tracker.offset_before(useloc.ins_addr, self.project.arch.bp_offset)
|
|
354
365
|
if sb_offset is not None:
|
|
355
366
|
v = StackBaseOffset(None, self.project.arch.bits, sb_offset)
|
|
@@ -20,26 +20,35 @@ class SRDAModel:
|
|
|
20
20
|
self.func_args = func_args
|
|
21
21
|
self.arch = arch
|
|
22
22
|
self.varid_to_vvar: dict[int, VirtualVariable] = {}
|
|
23
|
-
self.all_vvar_definitions: dict[
|
|
24
|
-
self.all_vvar_uses: dict[
|
|
23
|
+
self.all_vvar_definitions: dict[int, CodeLocation] = {}
|
|
24
|
+
self.all_vvar_uses: dict[int, list[tuple[VirtualVariable | None, CodeLocation]]] = defaultdict(list)
|
|
25
25
|
self.all_tmp_definitions: dict[CodeLocation, dict[atoms.Tmp, int]] = defaultdict(dict)
|
|
26
26
|
self.all_tmp_uses: dict[CodeLocation, dict[atoms.Tmp, set[tuple[Tmp, int]]]] = defaultdict(dict)
|
|
27
27
|
self.phi_vvar_ids: set[int] = set()
|
|
28
28
|
self.phivarid_to_varids: dict[int, set[int]] = {}
|
|
29
|
+
self.vvar_uses_by_loc: dict[CodeLocation, list[int]] = {}
|
|
30
|
+
|
|
31
|
+
def add_vvar_use(self, vvar_id: int, expr: VirtualVariable | None, loc: CodeLocation) -> None:
|
|
32
|
+
self.all_vvar_uses[vvar_id].append((expr, loc))
|
|
33
|
+
if loc not in self.vvar_uses_by_loc:
|
|
34
|
+
self.vvar_uses_by_loc[loc] = []
|
|
35
|
+
self.vvar_uses_by_loc[loc].append(vvar_id)
|
|
29
36
|
|
|
30
37
|
@property
|
|
31
38
|
def all_definitions(self) -> Generator[Definition]:
|
|
32
|
-
for
|
|
33
|
-
|
|
39
|
+
for vvar_id, defloc in self.all_vvar_definitions.items():
|
|
40
|
+
vvar = self.varid_to_vvar[vvar_id]
|
|
41
|
+
yield Definition(atoms.VirtualVariable(vvar_id, vvar.size, vvar.category, vvar.oident), defloc)
|
|
34
42
|
|
|
35
43
|
def is_phi_vvar_id(self, idx: int) -> bool:
|
|
36
44
|
return idx in self.phi_vvar_ids
|
|
37
45
|
|
|
38
46
|
def get_all_definitions(self, block_loc: CodeLocation) -> set[Definition]:
|
|
39
47
|
s = set()
|
|
40
|
-
for
|
|
48
|
+
for vvar_id, codeloc in self.all_vvar_definitions.items():
|
|
49
|
+
vvar = self.varid_to_vvar[vvar_id]
|
|
41
50
|
if codeloc.block_addr == block_loc.block_addr and codeloc.block_idx == block_loc.block_idx:
|
|
42
|
-
s.add(Definition(atoms.VirtualVariable(
|
|
51
|
+
s.add(Definition(atoms.VirtualVariable(vvar_id, vvar.size, vvar.category, vvar.oident), codeloc))
|
|
43
52
|
return s | self.get_all_tmp_definitions(block_loc)
|
|
44
53
|
|
|
45
54
|
def get_all_tmp_definitions(self, block_loc: CodeLocation) -> set[Definition]:
|
|
@@ -64,45 +73,45 @@ class SRDAModel:
|
|
|
64
73
|
:return: A set of definitions that are used at the given location.
|
|
65
74
|
"""
|
|
66
75
|
if exprs:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
atoms.VirtualVariable(vvar.varid, vvar.size, vvar.category, vvar.oident),
|
|
75
|
-
self.all_vvar_definitions[vvar],
|
|
76
|
-
),
|
|
77
|
-
expr,
|
|
78
|
-
)
|
|
79
|
-
)
|
|
80
|
-
return defs
|
|
81
|
-
|
|
82
|
-
defs: set[Definition] = set()
|
|
83
|
-
for vvar, uses in self.all_vvar_uses.items():
|
|
84
|
-
for _, loc_ in uses:
|
|
85
|
-
if loc_ == loc:
|
|
86
|
-
defs.add(
|
|
76
|
+
def_with_exprs: set[tuple[Definition, Any]] = set()
|
|
77
|
+
if loc not in self.vvar_uses_by_loc:
|
|
78
|
+
return def_with_exprs
|
|
79
|
+
for vvar_id in self.vvar_uses_by_loc[loc]:
|
|
80
|
+
vvar = self.varid_to_vvar[vvar_id]
|
|
81
|
+
def_with_exprs.add(
|
|
82
|
+
(
|
|
87
83
|
Definition(
|
|
88
|
-
atoms.VirtualVariable(
|
|
89
|
-
self.all_vvar_definitions[
|
|
90
|
-
)
|
|
84
|
+
atoms.VirtualVariable(vvar_id, vvar.size, vvar.category, vvar.oident),
|
|
85
|
+
self.all_vvar_definitions[vvar_id],
|
|
86
|
+
),
|
|
87
|
+
vvar,
|
|
91
88
|
)
|
|
89
|
+
)
|
|
90
|
+
return def_with_exprs
|
|
91
|
+
|
|
92
|
+
defs: set[Definition] = set()
|
|
93
|
+
if loc not in self.vvar_uses_by_loc:
|
|
94
|
+
return defs
|
|
95
|
+
for vvar_id in self.vvar_uses_by_loc[loc]:
|
|
96
|
+
vvar = self.varid_to_vvar[vvar_id]
|
|
97
|
+
defs.add(
|
|
98
|
+
Definition(
|
|
99
|
+
atoms.VirtualVariable(vvar_id, vvar.size, vvar.category, vvar.oident),
|
|
100
|
+
self.all_vvar_definitions[vvar_id],
|
|
101
|
+
)
|
|
102
|
+
)
|
|
92
103
|
return defs
|
|
93
104
|
|
|
94
105
|
def get_vvar_uses(self, obj: VirtualVariable | atoms.VirtualVariable) -> set[CodeLocation]:
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
return {loc for _, loc in self.all_vvar_uses[the_vvar]}
|
|
106
|
+
if obj.varid in self.all_vvar_uses:
|
|
107
|
+
return {loc for _, loc in self.all_vvar_uses[obj.varid]}
|
|
98
108
|
return set()
|
|
99
109
|
|
|
100
110
|
def get_vvar_uses_with_expr(
|
|
101
111
|
self, obj: VirtualVariable | atoms.VirtualVariable
|
|
102
|
-
) -> set[tuple[
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
return {(loc, expr) for expr, loc in self.all_vvar_uses[the_vvar]}
|
|
112
|
+
) -> set[tuple[VirtualVariable | None, CodeLocation]]:
|
|
113
|
+
if obj.varid in self.all_vvar_uses:
|
|
114
|
+
return set(self.all_vvar_uses[obj.varid])
|
|
106
115
|
return set()
|
|
107
116
|
|
|
108
117
|
def get_tmp_uses(self, obj: atoms.Tmp, block_loc: CodeLocation) -> set[CodeLocation]:
|
|
@@ -185,7 +185,10 @@ class SRDAView:
|
|
|
185
185
|
vvars.append(func_arg)
|
|
186
186
|
# there might be multiple vvars; we prioritize the one whose size fits the best
|
|
187
187
|
for v in vvars:
|
|
188
|
-
if
|
|
188
|
+
if (
|
|
189
|
+
(v.was_stack and v.stack_offset == stack_offset)
|
|
190
|
+
or (v.was_parameter and v.parameter_stack_offset == stack_offset)
|
|
191
|
+
) and v.size == size:
|
|
189
192
|
return v
|
|
190
193
|
return vvars[0] if vvars else None
|
|
191
194
|
|
|
@@ -239,9 +242,9 @@ class SRDAView:
|
|
|
239
242
|
return vvars[0] if vvars else None
|
|
240
243
|
|
|
241
244
|
def get_vvar_value(self, vvar: VirtualVariable) -> Expression | None:
|
|
242
|
-
if vvar not in self.model.all_vvar_definitions:
|
|
245
|
+
if vvar.varid not in self.model.all_vvar_definitions:
|
|
243
246
|
return None
|
|
244
|
-
codeloc = self.model.all_vvar_definitions[vvar]
|
|
247
|
+
codeloc = self.model.all_vvar_definitions[vvar.varid]
|
|
245
248
|
|
|
246
249
|
for block in self.model.func_graph:
|
|
247
250
|
if block.addr == codeloc.block_addr and block.idx == codeloc.block_idx:
|
|
@@ -63,7 +63,7 @@ class SReachingDefinitionsAnalysis(Analysis):
|
|
|
63
63
|
case _:
|
|
64
64
|
raise NotImplementedError
|
|
65
65
|
|
|
66
|
-
phi_vvars = {}
|
|
66
|
+
phi_vvars: dict[int, set[int]] = {}
|
|
67
67
|
# find all vvar definitions
|
|
68
68
|
vvar_deflocs = get_vvar_deflocs(blocks.values(), phi_vvars=phi_vvars)
|
|
69
69
|
# find all explicit vvar uses
|
|
@@ -72,34 +72,35 @@ class SReachingDefinitionsAnalysis(Analysis):
|
|
|
72
72
|
# update vvar definitions using function arguments
|
|
73
73
|
if self.func_args:
|
|
74
74
|
for vvar in self.func_args:
|
|
75
|
-
if vvar not in vvar_deflocs:
|
|
76
|
-
vvar_deflocs[vvar] = ExternalCodeLocation()
|
|
75
|
+
if vvar.varid not in vvar_deflocs:
|
|
76
|
+
vvar_deflocs[vvar.varid] = vvar, ExternalCodeLocation()
|
|
77
77
|
self.model.func_args = self.func_args
|
|
78
78
|
|
|
79
79
|
# update model
|
|
80
|
-
for vvar, defloc in vvar_deflocs.items():
|
|
81
|
-
self.model.varid_to_vvar[
|
|
82
|
-
self.model.all_vvar_definitions[
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
self.model.phi_vvar_ids =
|
|
80
|
+
for vvar_id, (vvar, defloc) in vvar_deflocs.items():
|
|
81
|
+
self.model.varid_to_vvar[vvar_id] = vvar
|
|
82
|
+
self.model.all_vvar_definitions[vvar_id] = defloc
|
|
83
|
+
if vvar_id in vvar_uselocs:
|
|
84
|
+
for useloc in vvar_uselocs[vvar_id]:
|
|
85
|
+
self.model.add_vvar_use(vvar_id, *useloc)
|
|
86
|
+
|
|
87
|
+
self.model.phi_vvar_ids = set(phi_vvars)
|
|
88
88
|
self.model.phivarid_to_varids = {}
|
|
89
|
-
for
|
|
90
|
-
self.model.phivarid_to_varids[
|
|
91
|
-
src_vvar.varid for src_vvar in src_vvars if src_vvar is not None
|
|
92
|
-
}
|
|
89
|
+
for vvar_id, src_vvars in phi_vvars.items():
|
|
90
|
+
self.model.phivarid_to_varids[vvar_id] = src_vvars
|
|
93
91
|
|
|
94
92
|
if self.mode == "function":
|
|
93
|
+
|
|
95
94
|
# fix register definitions for arguments
|
|
96
|
-
defined_vvarids =
|
|
95
|
+
defined_vvarids = set(vvar_deflocs)
|
|
97
96
|
undefined_vvarids = set(vvar_uselocs.keys()).difference(defined_vvarids)
|
|
98
97
|
for vvar_id in undefined_vvarids:
|
|
99
98
|
used_vvar = next(iter(vvar_uselocs[vvar_id]))[0]
|
|
100
|
-
self.model.varid_to_vvar[
|
|
101
|
-
self.model.all_vvar_definitions[
|
|
102
|
-
|
|
99
|
+
self.model.varid_to_vvar[vvar_id] = used_vvar
|
|
100
|
+
self.model.all_vvar_definitions[vvar_id] = ExternalCodeLocation()
|
|
101
|
+
if vvar_id in vvar_uselocs:
|
|
102
|
+
for vvar_useloc in vvar_uselocs[vvar_id]:
|
|
103
|
+
self.model.add_vvar_use(vvar_id, *vvar_useloc)
|
|
103
104
|
|
|
104
105
|
srda_view = SRDAView(self.model)
|
|
105
106
|
|
|
@@ -151,8 +152,7 @@ class SReachingDefinitionsAnalysis(Analysis):
|
|
|
151
152
|
reg_offset = self.project.arch.registers[arg_reg_name][0]
|
|
152
153
|
if reg_offset in reg_to_vvarids:
|
|
153
154
|
vvarid = reg_to_vvarids[reg_offset]
|
|
154
|
-
|
|
155
|
-
self.model.all_vvar_uses[vvar].add((None, codeloc))
|
|
155
|
+
self.model.add_vvar_use(vvarid, None, codeloc)
|
|
156
156
|
|
|
157
157
|
if self._track_tmps:
|
|
158
158
|
# track tmps
|
|
@@ -143,9 +143,9 @@ class SimEngineVRAIL(
|
|
|
143
143
|
# this is a dynamically calculated call target
|
|
144
144
|
target_expr = self._expr(target)
|
|
145
145
|
funcaddr_typevar = target_expr.typevar
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
146
|
+
if funcaddr_typevar is not None:
|
|
147
|
+
load_typevar = self._create_access_typevar(funcaddr_typevar, False, self.arch.bytes, 0)
|
|
148
|
+
self.state.add_type_constraint(typevars.Subtype(funcaddr_typevar, load_typevar))
|
|
149
149
|
|
|
150
150
|
# discover the prototype
|
|
151
151
|
prototype: SimTypeFunction | None = None
|
|
@@ -212,9 +212,9 @@ class SimEngineVRAIL(
|
|
|
212
212
|
# this is a dynamically calculated call target
|
|
213
213
|
target_expr = self._expr(target)
|
|
214
214
|
funcaddr_typevar = target_expr.typevar
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
215
|
+
if funcaddr_typevar is not None:
|
|
216
|
+
load_typevar = self._create_access_typevar(funcaddr_typevar, False, self.arch.bytes, 0)
|
|
217
|
+
self.state.add_type_constraint(typevars.Subtype(funcaddr_typevar, load_typevar))
|
|
218
218
|
|
|
219
219
|
# discover the prototype
|
|
220
220
|
prototype: SimTypeFunction | None = None
|