angr 9.2.122__py3-none-win_amd64.whl → 9.2.123__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.

@@ -42,10 +42,8 @@ class ITERegionConverter(OptimizationPass):
42
42
  if not ite_assign_regions:
43
43
  break
44
44
 
45
- for region_head, region_tail, true_block, true_stmt, false_block, false_stmt in ite_assign_regions:
46
- round_update |= self._convert_region_to_ternary_expr(
47
- region_head, region_tail, true_block, true_stmt, false_block, false_stmt
48
- )
45
+ for region_head, region_tail, _, true_stmt, _, false_stmt in ite_assign_regions:
46
+ round_update |= self._convert_region_to_ternary_expr(region_head, region_tail, true_stmt, false_stmt)
49
47
 
50
48
  if not round_update:
51
49
  break
@@ -188,9 +186,7 @@ class ITERegionConverter(OptimizationPass):
188
186
  self,
189
187
  region_head,
190
188
  region_tail,
191
- true_block,
192
189
  true_stmt: Assignment | Call,
193
- false_block,
194
190
  false_stmt: Assignment | Call,
195
191
  ):
196
192
  if region_head not in self._graph or region_tail not in self._graph:
@@ -242,6 +238,14 @@ class ITERegionConverter(OptimizationPass):
242
238
  #
243
239
 
244
240
  region_nodes = subgraph_between_nodes(self._graph, region_head, [region_tail])
241
+
242
+ # we must obtain the predecessors of the region tail instead of using true_block and false_block because
243
+ # true_block and false_block may have other successors before reaching the region tail!
244
+ region_tail_preds = [pred for pred in self._graph.predecessors(region_tail) if pred in region_nodes]
245
+ if len(region_tail_preds) != 2:
246
+ return False
247
+ region_tail_pred_srcs = {(pred.addr, pred.idx) for pred in region_tail_preds}
248
+
245
249
  for node in region_nodes:
246
250
  if node is region_head or node is region_tail:
247
251
  continue
@@ -259,7 +263,7 @@ class ITERegionConverter(OptimizationPass):
259
263
  continue
260
264
  new_src_and_vvars = []
261
265
  for src, vvar in stmt.src.src_and_vvars:
262
- if src not in {(true_block.addr, true_block.idx), (false_block.addr, false_block.idx)}:
266
+ if src not in region_tail_pred_srcs:
263
267
  new_src_and_vvars.append((src, vvar))
264
268
  new_vvar = new_assignment.dst.copy()
265
269
  new_src_and_vvars.append(((region_head.addr, region_head.idx), new_vvar))
@@ -396,7 +396,16 @@ class LoweredSwitchSimplifier(StructuringOptimizationPass):
396
396
  default_case_candidates = {}
397
397
  last_comp = None
398
398
  stack = [(head, 0, 0xFFFF_FFFF_FFFF_FFFF)]
399
- while stack:
399
+
400
+ # cursed: there is an infinite loop in the following loop that
401
+ # occurs rarely. we need to keep track of the nodes we've seen
402
+ # to break out of the loop.
403
+ # See https://github.com/angr/angr/pull/4953
404
+ #
405
+ # FIXME: the root cause should be fixed and this workaround removed
406
+ seen = set()
407
+ while stack and tuple(stack) not in seen:
408
+ seen.add(tuple(stack))
400
409
  comp, min_, max_ = stack.pop(0)
401
410
  (
402
411
  comp_type,
@@ -1,7 +1,7 @@
1
1
  # pylint:disable=too-many-boolean-expressions
2
2
  from __future__ import annotations
3
3
 
4
- from ailment.expression import Convert, BinaryOp, Const
4
+ from ailment.expression import Convert, BinaryOp, Const, Expression
5
5
 
6
6
  from .base import PeepholeOptimizationExprBase
7
7
 
@@ -56,47 +56,10 @@ class ConstMullAShift(PeepholeOptimizationExprBase):
56
56
 
57
57
  elif isinstance(expr, BinaryOp) and expr.op in {"Add", "Sub"}:
58
58
  expr0, expr1 = expr.operands
59
- if (
60
- isinstance(expr0, BinaryOp)
61
- and expr0.op in {"Shr", "Sar"}
62
- and isinstance(expr0.operands[1], Const)
63
- and isinstance(expr1, BinaryOp)
64
- and expr1.op in {"Shr", "Sar"}
65
- and isinstance(expr1.operands[1], Const)
66
- ):
67
- if (
68
- isinstance(expr0.operands[0], BinaryOp)
69
- and expr0.operands[0].op in {"Mull", "Mul"}
70
- and isinstance(expr0.operands[0].operands[1], Const)
71
- ):
72
- a0 = expr0.operands[0].operands[0]
73
- a1 = expr1.operands[0]
74
- elif (
75
- isinstance(expr1.operands[0], BinaryOp)
76
- and expr1.operands[0].op in {"Mull", "Mul"}
77
- and isinstance(expr1.operands[0].operands[1], Const)
78
- ):
79
- a1 = expr0.operands[0].operands[0]
80
- a0 = expr1.operands[0]
81
- else:
82
- a0, a1 = None, None
83
- if a0 is not None and a1 is not None and a0.likes(a1):
84
- # (a * x >> M1) +/- (a >> M2) ==> a / N
85
- C = expr0.operands[0].operands[1].value
86
- X = a0
87
- V = expr0.operands[1].value
88
- ndigits = 5 if V == 32 else 6
89
- divisor = self._check_divisor(pow(2, V), C, ndigits)
90
- if divisor is not None:
91
- new_const = Const(None, None, divisor, X.bits)
92
- # we cannot drop the convert in this case
93
- return BinaryOp(
94
- expr0.operands[0].idx,
95
- "Div",
96
- [X, new_const],
97
- expr0.operands[0].signed,
98
- **expr0.operands[0].tags,
99
- )
59
+ if isinstance(expr1, Convert) and expr1.from_bits == 32 and expr1.to_bits == 64:
60
+ r = self._match_case_a(expr0, expr1)
61
+ if r is not None:
62
+ return r
100
63
 
101
64
  # with Convert in consideration
102
65
  if (
@@ -149,6 +112,74 @@ class ConstMullAShift(PeepholeOptimizationExprBase):
149
112
 
150
113
  return None
151
114
 
115
+ def _match_case_a(self, expr0: Expression, expr1: Convert) -> BinaryOp | None:
116
+ # (
117
+ # (((Conv(32->64, vvar_44{reg 32}) * 0x4325c53f<64>) >>a 0x24<8>) & 0xffffffff<64>) -
118
+ # Conv(32->s64, (vvar_44{reg 32} >>a 0x1f<8>))
119
+ # )
120
+
121
+ expr1 = expr1.operand
122
+
123
+ if (
124
+ isinstance(expr0, BinaryOp)
125
+ and expr0.op == "And"
126
+ and isinstance(expr0.operands[1], Const)
127
+ and expr0.operands[1].value == 0xFFFFFFFF
128
+ ):
129
+ expr0 = expr0.operands[0]
130
+ else:
131
+ return None
132
+
133
+ if (
134
+ isinstance(expr0, BinaryOp)
135
+ and expr0.op in {"Shr", "Sar"}
136
+ and isinstance(expr0.operands[1], Const)
137
+ and isinstance(expr1, BinaryOp)
138
+ and expr1.op in {"Shr", "Sar"}
139
+ and isinstance(expr1.operands[1], Const)
140
+ ):
141
+ if (
142
+ isinstance(expr0.operands[0], BinaryOp)
143
+ and expr0.operands[0].op in {"Mull", "Mul"}
144
+ and isinstance(expr0.operands[0].operands[1], Const)
145
+ ):
146
+ a0 = expr0.operands[0].operands[0]
147
+ a1 = expr1.operands[0]
148
+ elif (
149
+ isinstance(expr1.operands[0], BinaryOp)
150
+ and expr1.operands[0].op in {"Mull", "Mul"}
151
+ and isinstance(expr1.operands[0].operands[1], Const)
152
+ ):
153
+ a1 = expr0.operands[0].operands[0]
154
+ a0 = expr1.operands[0]
155
+ else:
156
+ a0, a1 = None, None
157
+
158
+ # a0: Conv(32->64, vvar_44{reg 32})
159
+ # a1: vvar_44{reg 32}
160
+ if isinstance(a0, Convert) and a0.from_bits == a1.bits:
161
+ a0 = a0.operand
162
+
163
+ if a0 is not None and a1 is not None and a0.likes(a1):
164
+ # (a * x >> M1) +/- (a >> M2) ==> a / N
165
+ C = expr0.operands[0].operands[1].value
166
+ X = a0
167
+ V = expr0.operands[1].value
168
+ ndigits = 5 if V == 32 else 6
169
+ divisor = self._check_divisor(pow(2, V), C, ndigits)
170
+ if divisor is not None:
171
+ new_const = Const(None, None, divisor, X.bits)
172
+ # we cannot drop the convert in this case
173
+ return BinaryOp(
174
+ expr0.operands[0].idx,
175
+ "Div",
176
+ [X, new_const],
177
+ expr0.operands[0].signed,
178
+ **expr0.operands[0].tags,
179
+ )
180
+
181
+ return None
182
+
152
183
  @staticmethod
153
184
  def _check_divisor(a, b, ndigits=6):
154
185
  divisor_1 = 1 + (a // b)
@@ -395,6 +395,10 @@ class ExpressionReplacer(AILBlockWalker):
395
395
 
396
396
  def _handle_Assignment(self, stmt_idx: int, stmt: Assignment, block: Block | None):
397
397
  # override the base handler and make sure we do not replace .dst with a Call expression or an ITE expression
398
+
399
+ if is_phi_assignment(stmt):
400
+ return None
401
+
398
402
  changed = False
399
403
 
400
404
  dst = self._handle_expr(0, stmt.dst, stmt_idx, stmt, block)
@@ -216,11 +216,27 @@ class SequenceWalker:
216
216
  )
217
217
 
218
218
  def _handle_CascadingCondition(self, node: CascadingConditionNode, **kwargs):
219
- for index, (_, child_node) in enumerate(node.condition_and_nodes):
220
- self._handle(child_node, parent=node, index=index)
219
+ cond_nodes_changed = False
220
+ new_condition_and_nodes = []
221
+ for index, (cond, child_node) in enumerate(node.condition_and_nodes):
222
+ new_child = self._handle(child_node, parent=node, index=index)
223
+ if new_child is not None:
224
+ cond_nodes_changed = True
225
+ new_condition_and_nodes.append((cond, new_child))
226
+ else:
227
+ new_condition_and_nodes.append((cond, child_node))
228
+
229
+ new_else = None
221
230
  if node.else_node is not None:
222
- self._handle(node.else_node, parent=node, index=-1)
223
- return
231
+ new_else = self._handle(node.else_node, parent=node, index=-1)
232
+
233
+ if cond_nodes_changed or new_else is not None:
234
+ return CascadingConditionNode(
235
+ node.addr,
236
+ new_condition_and_nodes if cond_nodes_changed else node.condition_and_nodes,
237
+ else_node=new_else if new_else is not None else node.else_node,
238
+ )
239
+ return None
224
240
 
225
241
  def _handle_ConditionalBreak(self, node: ConditionalBreakNode, **kwargs): # pylint:disable=no-self-use
226
242
  return None
@@ -5,7 +5,7 @@ from collections import defaultdict
5
5
 
6
6
  from ailment.block import Block
7
7
  from ailment.expression import Const, VirtualVariable, VirtualVariableCategory, StackBaseOffset
8
- from ailment.statement import Assignment, Store, Return
8
+ from ailment.statement import Assignment, Store, Return, Jump
9
9
 
10
10
  from angr.knowledge_plugins.functions import Function
11
11
  from angr.code_location import CodeLocation
@@ -98,11 +98,15 @@ class SPropagatorAnalysis(Analysis):
98
98
  # find all vvar uses
99
99
  vvar_uselocs = get_vvar_uselocs(blocks.values())
100
100
 
101
- # find all ret sites
101
+ # find all ret sites and indirect jump sites
102
102
  retsites: set[tuple[int, int | None, int]] = set()
103
+ jumpsites: set[tuple[int, int | None, int]] = set()
103
104
  for bb in blocks.values():
104
- if bb.statements and isinstance(bb.statements[-1], Return):
105
- retsites.add((bb.addr, bb.idx, len(bb.statements) - 1))
105
+ if bb.statements:
106
+ if isinstance(bb.statements[-1], Return):
107
+ retsites.add((bb.addr, bb.idx, len(bb.statements) - 1))
108
+ elif isinstance(bb.statements[-1], Jump):
109
+ jumpsites.add((bb.addr, bb.idx, len(bb.statements) - 1))
106
110
 
107
111
  replacements = defaultdict(dict)
108
112
 
@@ -169,13 +173,13 @@ class SPropagatorAnalysis(Analysis):
169
173
  {
170
174
  loc
171
175
  for _, loc in vvar_uselocs[vvar.varid]
172
- if (loc.block_addr, loc.block_idx, loc.stmt_idx) not in retsites
176
+ if (loc.block_addr, loc.block_idx, loc.stmt_idx) not in (retsites | jumpsites)
173
177
  }
174
178
  )
175
179
  == 1
176
180
  ):
177
181
  if is_const_and_vvar_assignment(stmt):
178
- # this vvar is used once if we exclude its uses at ret sites. we can propagate it
182
+ # this vvar is used once if we exclude its uses at ret sites or jump sites. we can propagate it
179
183
  for vvar_used, vvar_useloc in vvar_uselocs[vvar.varid]:
180
184
  replacements[vvar_useloc][vvar_used] = stmt.src
181
185
 
@@ -1212,7 +1212,7 @@ class SimCCCdecl(SimCC):
1212
1212
  if isinstance(arg_type, (SimTypeArray, SimTypeFixedSizeArray)): # hack
1213
1213
  arg_type = SimTypePointer(arg_type.elem_type).with_arch(self.arch)
1214
1214
  locs_size = 0
1215
- byte_size = arg_type.size // self.arch.byte_width
1215
+ byte_size = arg_type.size // self.arch.byte_width if arg_type.size is not None else self.arch.bytes
1216
1216
  locs = []
1217
1217
  while locs_size < byte_size:
1218
1218
  locs.append(next(session.both_iter))
@@ -1293,7 +1293,7 @@ class SimCCMicrosoftAMD64(SimCC):
1293
1293
  except StopIteration:
1294
1294
  int_loc = fp_loc = next(session.both_iter)
1295
1295
 
1296
- byte_size = arg_type.size // self.arch.byte_width
1296
+ byte_size = arg_type.size // self.arch.byte_width if arg_type.size is not None else self.arch.bytes
1297
1297
 
1298
1298
  if isinstance(arg_type, SimTypeFloat):
1299
1299
  return fp_loc.refine(size=byte_size, is_fp=True, arch=self.arch)
@@ -604,6 +604,9 @@ class SimEngineLightVEXMixin(SimEngineLightMixin):
604
604
  if self._is_top(expr_0) or self._is_top(expr_1):
605
605
  return self._top(expr.result_size(self.tyenv))
606
606
 
607
+ if expr_1.concrete and expr_1.concrete_value == 0:
608
+ return self._top(expr.result_size(self.tyenv))
609
+
607
610
  signed = "U" in expr.op # Iop_DivModU64to32 vs Iop_DivMod
608
611
  from_size = expr_0.size()
609
612
  to_size = expr_1.size()
@@ -632,10 +635,13 @@ class SimEngineLightVEXMixin(SimEngineLightMixin):
632
635
  if self._is_top(expr_0) or self._is_top(expr_1):
633
636
  return self._top(expr_0.size())
634
637
 
638
+ if expr_1.concrete and expr_1.concrete_value == 0:
639
+ return self._top(expr.result_size(self.tyenv))
640
+
635
641
  try:
636
642
  return expr_0 / expr_1
637
643
  except ZeroDivisionError:
638
- return self._top(expr_0.size())
644
+ return self._top(expr.result_size(self.tyenv))
639
645
 
640
646
  def _handle_Mod(self, expr):
641
647
  args, r = self._binop_get_args(expr)
@@ -646,6 +652,9 @@ class SimEngineLightVEXMixin(SimEngineLightMixin):
646
652
  if self._is_top(expr_0) or self._is_top(expr_1):
647
653
  return self._top(expr_0.size())
648
654
 
655
+ if expr_1.concrete and expr_1.concrete_value == 0:
656
+ return self._top(expr.result_size(self.tyenv))
657
+
649
658
  try:
650
659
  return expr_0 - (expr_1 // expr_1) * expr_1
651
660
  except ZeroDivisionError:
@@ -974,7 +983,8 @@ class SimEngineLightAILMixin(SimEngineLightMixin):
974
983
  return expr
975
984
 
976
985
  def _ail_handle_CallExpr(self, expr: ailment.Stmt.Call):
977
- self._expr(expr.target)
986
+ if not isinstance(expr.target, str):
987
+ self._expr(expr.target)
978
988
  return expr
979
989
 
980
990
  def _ail_handle_Reinterpret(self, expr: ailment.Expr.Reinterpret):
@@ -1,6 +1,9 @@
1
1
  from __future__ import annotations
2
+
2
3
  import logging
3
4
 
5
+ import claripy
6
+
4
7
  from .base import SimSootExpr
5
8
 
6
9
  l = logging.getLogger(name=__name__)
@@ -9,4 +12,4 @@ l = logging.getLogger(name=__name__)
9
12
  class SimSootExpr_InstanceOf(SimSootExpr):
10
13
  def _execute(self):
11
14
  obj = self._translate_value(self.expr.value)
12
- self.expr = self.state.solver.StringV(obj.type) == self.state.solver.StringV(self.expr.check_type)
15
+ self.expr = claripy.StringV(obj.type) == claripy.StringV(self.expr.check_type)
@@ -504,7 +504,7 @@ class SimSuccessors:
504
504
  fallback = True
505
505
  break
506
506
 
507
- cond_and_targets.append((cond, target if not outer_reverse else state.solver.Reverse(target)))
507
+ cond_and_targets.append((cond, target if not outer_reverse else claripy.Reverse(target)))
508
508
 
509
509
  if reached_sentinel is False:
510
510
  # huh?