angr 9.2.150__py3-none-macosx_11_0_arm64.whl → 9.2.153__py3-none-macosx_11_0_arm64.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 (34) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/calling_convention/calling_convention.py +17 -9
  3. angr/analyses/cfg/cfg_base.py +1 -1
  4. angr/analyses/cfg/cfg_fast.py +39 -0
  5. angr/analyses/decompiler/ail_simplifier.py +0 -1
  6. angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +39 -0
  7. angr/analyses/decompiler/clinic.py +118 -2
  8. angr/analyses/decompiler/dephication/rewriting_engine.py +38 -1
  9. angr/analyses/decompiler/optimization_passes/condition_constprop.py +6 -0
  10. angr/analyses/decompiler/optimization_passes/engine_base.py +5 -0
  11. angr/analyses/decompiler/peephole_optimizations/__init__.py +2 -0
  12. angr/analyses/decompiler/peephole_optimizations/cas_intrinsics.py +115 -0
  13. angr/analyses/decompiler/region_identifier.py +171 -119
  14. angr/analyses/decompiler/ssailification/rewriting_engine.py +37 -1
  15. angr/analyses/decompiler/ssailification/traversal_engine.py +10 -1
  16. angr/analyses/reaching_definitions/engine_ail.py +20 -0
  17. angr/analyses/s_propagator.py +28 -0
  18. angr/analyses/smc.py +3 -1
  19. angr/analyses/stack_pointer_tracker.py +2 -1
  20. angr/analyses/typehoon/simple_solver.py +143 -81
  21. angr/analyses/typehoon/typehoon.py +2 -1
  22. angr/analyses/variable_recovery/engine_ail.py +9 -0
  23. angr/engines/light/engine.py +7 -0
  24. angr/knowledge_plugins/functions/function.py +10 -4
  25. angr/lib/angr_native.dylib +0 -0
  26. angr/storage/memory_mixins/clouseau_mixin.py +7 -1
  27. angr/utils/graph.py +10 -12
  28. angr/utils/ssa/__init__.py +6 -1
  29. {angr-9.2.150.dist-info → angr-9.2.153.dist-info}/METADATA +6 -6
  30. {angr-9.2.150.dist-info → angr-9.2.153.dist-info}/RECORD +34 -33
  31. {angr-9.2.150.dist-info → angr-9.2.153.dist-info}/WHEEL +1 -1
  32. {angr-9.2.150.dist-info → angr-9.2.153.dist-info}/entry_points.txt +0 -0
  33. {angr-9.2.150.dist-info → angr-9.2.153.dist-info}/licenses/LICENSE +0 -0
  34. {angr-9.2.150.dist-info → angr-9.2.153.dist-info}/top_level.txt +0 -0
@@ -2,6 +2,7 @@
2
2
  from __future__ import annotations
3
3
  import enum
4
4
  from collections import defaultdict
5
+ from contextlib import suppress
5
6
  import logging
6
7
 
7
8
  import networkx
@@ -51,23 +52,6 @@ from .dfa import DFAConstraintSolver, EmptyEpsilonNFAError
51
52
  _l = logging.getLogger(__name__)
52
53
 
53
54
 
54
- PRIMITIVE_TYPES = {
55
- TopType(),
56
- Int(),
57
- Int8(),
58
- Int16(),
59
- Int32(),
60
- Int64(),
61
- Pointer32(),
62
- Pointer64(),
63
- BottomType(),
64
- Struct(),
65
- Array(),
66
- Float(),
67
- Float32(),
68
- Float64(),
69
- }
70
-
71
55
  Top_ = TopType()
72
56
  Int_ = Int()
73
57
  Int64_ = Int64()
@@ -83,6 +67,25 @@ Float_ = Float()
83
67
  Float32_ = Float32()
84
68
  Float64_ = Float64()
85
69
 
70
+
71
+ PRIMITIVE_TYPES = {
72
+ Top_,
73
+ Int_,
74
+ Int8_,
75
+ Int16_,
76
+ Int32_,
77
+ Int64_,
78
+ Pointer32_,
79
+ Pointer64_,
80
+ Bottom_,
81
+ Struct_,
82
+ Array_,
83
+ Float_,
84
+ Float32_,
85
+ Float64_,
86
+ }
87
+
88
+
86
89
  # lattice for 64-bit binaries
87
90
  BASE_LATTICE_64 = networkx.DiGraph()
88
91
  BASE_LATTICE_64.add_edge(Top_, Int_)
@@ -149,6 +152,24 @@ class SketchNode(SketchNodeBase):
149
152
  def __hash__(self):
150
153
  return hash((SketchNode, self.typevar))
151
154
 
155
+ @property
156
+ def size(self) -> int | None:
157
+ """
158
+ Best-effort estimation of the size of the typevar (in bits). Returns None if we cannot determine.
159
+ """
160
+
161
+ if isinstance(self.typevar, DerivedTypeVariable):
162
+ last_label = self.typevar.labels[-1]
163
+ if isinstance(last_label, HasField) and last_label.bits != MAX_POINTSTO_BITS:
164
+ return last_label.bits
165
+ if isinstance(self.lower_bound, TypeConstant) and not isinstance(self.lower_bound, (TopType, BottomType)):
166
+ with suppress(NotImplementedError):
167
+ return self.lower_bound.size * 8
168
+ if isinstance(self.upper_bound, TypeConstant) and not isinstance(self.upper_bound, (TopType, BottomType)):
169
+ with suppress(NotImplementedError):
170
+ return self.upper_bound.size * 8
171
+ return None
172
+
152
173
 
153
174
  class RecursiveRefNode(SketchNodeBase):
154
175
  """
@@ -433,9 +454,8 @@ class SimpleSolver:
433
454
  if isinstance(tv, TypeVariable) and isinstance(sol, TypeConstant):
434
455
  self.solution[tv] = sol
435
456
 
436
- equ_classes, sketches, _ = self.solve()
437
457
  self._solution_cache = {}
438
- self.determine(equ_classes, sketches, self.solution)
458
+ self.solve()
439
459
  for typevar in list(self._constraints):
440
460
  self._convert_arrays(self._constraints[typevar])
441
461
 
@@ -448,6 +468,11 @@ class SimpleSolver:
448
468
  - Build the constraint graph
449
469
  - Collect all constraints
450
470
  - Apply constraints to derive the lower and upper bounds
471
+ - Determine a solution for type variables with constraints
472
+ - Rewrite the constraint graph by replacing determined type variables with their solutions
473
+ - Solve repeatedly until all interesting type variables have solutions
474
+
475
+ By repeatedly solving until exhausting interesting type variables, we ensure the S-Trans rule is applied.
451
476
  """
452
477
 
453
478
  prem_typevars = set(self._constraints) | self._typevars
@@ -476,11 +501,7 @@ class SimpleSolver:
476
501
  elif isinstance(t, TypeVariable) and t in typevars:
477
502
  constrained_typevars.add(t)
478
503
 
479
- equivalence_classes, sketches = self.infer_shapes(typevars, constraints)
480
- # TODO: Handle global variables
481
-
482
- type_schemes = constraints
483
-
504
+ _, sketches = self.infer_shapes(typevars, constraints)
484
505
  constraintset2tvs = defaultdict(set)
485
506
  for idx, tv in enumerate(constrained_typevars):
486
507
  _l.debug("Collecting constraints for type variable %r (%d/%d)", tv, idx + 1, len(constrained_typevars))
@@ -490,20 +511,43 @@ class SimpleSolver:
490
511
 
491
512
  for idx, (constraint_subset, tvs) in enumerate(constraintset2tvs.items()):
492
513
  _l.debug(
493
- "Solving %d constraints for type variables %r (%d/%d)",
514
+ "Solving %d constraints for %d type variables %r (%d/%d)",
494
515
  len(constraint_subset),
516
+ len(tvs),
495
517
  tvs,
496
518
  idx + 1,
497
519
  len(constraintset2tvs),
498
520
  )
499
- base_constraint_graph = self._generate_constraint_graph(constraint_subset, tvs | PRIMITIVE_TYPES)
500
- for idx_0, tv in enumerate(tvs):
501
- _l.debug("Solving for type variable %r (%d/%d)", tv, idx_0 + 1, len(tvs))
502
- primitive_constraints = self._generate_primitive_constraints({tv}, base_constraint_graph)
521
+
522
+ while True:
523
+ base_constraint_graph = self._generate_constraint_graph(constraint_subset, tvs | PRIMITIVE_TYPES)
524
+ primitive_constraints = self._generate_primitive_constraints(tvs, base_constraint_graph)
525
+ tvs_with_primitive_constraints = set()
503
526
  for primitive_constraint in primitive_constraints:
527
+ tv = self._typevar_from_primitive_constraint(primitive_constraint)
528
+ tvs_with_primitive_constraints.add(tv)
529
+ assert tv is not None, f"Cannot find type variable in primitive constraint {primitive_constraint}"
504
530
  sketches[tv].add_constraint(primitive_constraint)
505
-
506
- return equivalence_classes, sketches, type_schemes
531
+ solutions = {}
532
+ self.determine(sketches, tvs_with_primitive_constraints, solutions)
533
+ _l.debug("Determined solutions for %d type variable(s).", len(tvs_with_primitive_constraints))
534
+ if not solutions:
535
+ break
536
+
537
+ self.solution |= solutions
538
+
539
+ tvs = {tv for tv in tvs if tv not in tvs_with_primitive_constraints}
540
+ if not tvs:
541
+ break
542
+ # rewrite existing constraints
543
+ new_constraint_subset = set()
544
+ for constraint in constraint_subset:
545
+ rewritten = self._rewrite_constraint(constraint, solutions)
546
+ new_constraint_subset.add(rewritten)
547
+ constraint_subset = new_constraint_subset
548
+
549
+ # set the solution for missing type vars to TOP
550
+ self.determine(sketches, set(sketches).difference(set(self.solution)), self.solution)
507
551
 
508
552
  def infer_shapes(
509
553
  self, typevars: set[TypeVariable], constraints: set[TypeConstraint]
@@ -593,7 +637,6 @@ class SimpleSolver:
593
637
  non_primitive_endpoints: set[TypeVariable | DerivedTypeVariable],
594
638
  constraint_graph,
595
639
  ) -> set[TypeConstraint]:
596
- # FIXME: Extract interesting variables
597
640
  constraints_0 = self._solve_constraints_between(constraint_graph, non_primitive_endpoints, PRIMITIVE_TYPES)
598
641
  constraints_1 = self._solve_constraints_between(constraint_graph, PRIMITIVE_TYPES, non_primitive_endpoints)
599
642
  return constraints_0 | constraints_1
@@ -612,6 +655,25 @@ class SimpleSolver:
612
655
  # TODO: Other types of constraints?
613
656
  return typevars
614
657
 
658
+ @staticmethod
659
+ def _typevar_from_primitive_constraint(constraint: TypeConstraint) -> TypeVariable | None:
660
+ if isinstance(constraint, Subtype):
661
+ if (
662
+ isinstance(constraint.sub_type, DerivedTypeVariable)
663
+ and type(constraint.sub_type.type_var) is TypeVariable
664
+ ):
665
+ return constraint.sub_type.type_var
666
+ if type(constraint.sub_type) is TypeVariable:
667
+ return constraint.sub_type
668
+ if (
669
+ isinstance(constraint.super_type, DerivedTypeVariable)
670
+ and type(constraint.super_type.type_var) is TypeVariable
671
+ ):
672
+ return constraint.super_type.type_var
673
+ if type(constraint.super_type) is TypeVariable:
674
+ return constraint.super_type
675
+ return None
676
+
615
677
  @staticmethod
616
678
  def _get_all_paths(
617
679
  graph: networkx.DiGraph,
@@ -825,7 +887,6 @@ class SimpleSolver:
825
887
  """
826
888
 
827
889
  graph = networkx.DiGraph()
828
- constraints = self._get_transitive_subtype_constraints(constraints)
829
890
  for constraint in constraints:
830
891
  if isinstance(constraint, Subtype):
831
892
  self._constraint_graph_add_edges(
@@ -836,33 +897,6 @@ class SimpleSolver:
836
897
  self._constraint_graph_recall_forget_split(graph)
837
898
  return graph
838
899
 
839
- @staticmethod
840
- def _get_transitive_subtype_constraints(constraints: set[TypeConstraint]) -> set[TypeConstraint]:
841
- """
842
- Apply the S-Trans rule: a <: b, b <: c => a <: c
843
- """
844
- tv2supertypes = defaultdict(set)
845
- for constraint in constraints:
846
- if isinstance(constraint, Subtype):
847
- tv2supertypes[constraint.sub_type].add(constraint.super_type)
848
-
849
- new_constraints = set()
850
- while True:
851
- changed = False
852
- for subtype, supertypes in tv2supertypes.items():
853
- supertypes_copy = set(supertypes)
854
- for supertype in supertypes_copy:
855
- if supertype in tv2supertypes:
856
- for supertype_ in tv2supertypes[supertype]:
857
- if supertype_ not in supertypes_copy:
858
- changed = True
859
- supertypes.add(supertype_)
860
- new_constraints.add(Subtype(subtype, supertype_))
861
- if not changed:
862
- break
863
-
864
- return constraints | new_constraints
865
-
866
900
  @staticmethod
867
901
  def _constraint_graph_add_recall_edges(graph: networkx.DiGraph, node: ConstraintGraphNode) -> None:
868
902
  while True:
@@ -1004,12 +1038,7 @@ class SimpleSolver:
1004
1038
  if typevar in typevar_set:
1005
1039
  return True
1006
1040
  if isinstance(typevar, Struct) and Struct_ in typevar_set:
1007
- if not typevar.fields:
1008
- return True
1009
- return all(
1010
- SimpleSolver._typevar_inside_set(field_typevar, typevar_set)
1011
- for field_typevar in typevar.fields.values()
1012
- )
1041
+ return True
1013
1042
  if isinstance(typevar, Array) and Array_ in typevar_set:
1014
1043
  return SimpleSolver._typevar_inside_set(typevar.element, typevar_set)
1015
1044
  if isinstance(typevar, Pointer) and (Pointer32_ in typevar_set or Pointer64_ in typevar_set):
@@ -1110,31 +1139,47 @@ class SimpleSolver:
1110
1139
  return Pointer64()
1111
1140
  return t
1112
1141
 
1142
+ @staticmethod
1143
+ def _rewrite_constraint(constraint: TypeConstraint, solutions: dict) -> TypeConstraint:
1144
+ if isinstance(constraint, Subtype):
1145
+ replaced = False
1146
+ if isinstance(constraint.sub_type, TypeVariable) and constraint.sub_type in solutions:
1147
+ sub_type = solutions[constraint.sub_type]
1148
+ replaced = True
1149
+ else:
1150
+ sub_type = constraint.sub_type
1151
+ if isinstance(constraint.super_type, TypeVariable) and constraint.super_type in solutions:
1152
+ super_type = solutions[constraint.super_type]
1153
+ replaced = True
1154
+ else:
1155
+ super_type = constraint.super_type
1156
+ return Subtype(sub_type, super_type) if replaced else constraint
1157
+ return constraint
1158
+
1113
1159
  def determine(
1114
1160
  self,
1115
- equivalent_classes: dict[TypeVariable, TypeVariable],
1116
1161
  sketches,
1162
+ tvs,
1117
1163
  solution: dict,
1118
1164
  nodes: set[SketchNode] | None = None,
1119
1165
  ) -> None:
1120
1166
  """
1121
1167
  Determine C-like types from sketches.
1122
1168
 
1123
- :param equivalent_classes: A dictionary mapping each type variable from its representative in the equivalence
1124
- class over ~.
1125
1169
  :param sketches: A dictionary storing sketches for each type variable.
1126
1170
  :param solution: The dictionary storing C-like types for each type variable. Output.
1127
1171
  :param nodes: Optional. Nodes that should be considered in the sketch.
1128
1172
  :return: None
1129
1173
  """
1130
- for typevar, sketch in sketches.items():
1131
- self._determine(equivalent_classes, typevar, sketch, solution, nodes=nodes)
1132
1174
 
1133
- for v, e in self._equivalence.items():
1134
- if v not in solution and e in solution:
1135
- solution[v] = solution[e]
1175
+ for typevar in tvs:
1176
+ self._determine(typevar, sketches[typevar], solution, nodes=nodes)
1177
+
1178
+ for v, eq in self._equivalence.items():
1179
+ if v not in solution and eq in solution:
1180
+ solution[v] = solution[eq]
1136
1181
 
1137
- def _determine(self, equivalent_classes, the_typevar, sketch, solution: dict, nodes: set[SketchNode] | None = None):
1182
+ def _determine(self, the_typevar, sketch, solution: dict, nodes: set[SketchNode] | None = None):
1138
1183
  """
1139
1184
  Return the solution from sketches
1140
1185
  """
@@ -1194,7 +1239,7 @@ class SimpleSolver:
1194
1239
  for vals, out in [(func_inputs, input_args), (func_outputs, output_values)]:
1195
1240
  for idx in range(max(vals) + 1):
1196
1241
  if idx in vals:
1197
- sol = self._determine(equivalent_classes, the_typevar, sketch, solution, nodes=vals[idx])
1242
+ sol = self._determine(the_typevar, sketch, solution, nodes=vals[idx])
1198
1243
  out.append(sol)
1199
1244
  else:
1200
1245
  out.append(None)
@@ -1290,7 +1335,7 @@ class SimpleSolver:
1290
1335
  offset = sorted_offsets[i]
1291
1336
 
1292
1337
  child_nodes = node_by_offset[offset]
1293
- sol = self._determine(equivalent_classes, the_typevar, sketch, solution, nodes=child_nodes)
1338
+ sol = self._determine(the_typevar, sketch, solution, nodes=child_nodes)
1294
1339
  if isinstance(sol, TopType):
1295
1340
  # make it an array if possible
1296
1341
  elem_size = min(offset_to_sizes[offset])
@@ -1323,12 +1368,20 @@ class SimpleSolver:
1323
1368
  lower_bound = Bottom_
1324
1369
  upper_bound = Top_
1325
1370
 
1371
+ node_sizes = set()
1326
1372
  for node in nodes:
1327
- lower_bound = self.join(lower_bound, node.lower_bound)
1328
- upper_bound = self.meet(upper_bound, node.upper_bound)
1329
- # TODO: Support variables that are accessed via differently sized pointers
1373
+ node_size = node.size
1374
+ if node_size is not None:
1375
+ node_sizes.add(node_size)
1376
+ if len(node_sizes) > 1:
1377
+ # multi-sized reads - cannot converge to a reasonable type
1378
+ result = Bottom_
1379
+ else:
1380
+ for node in nodes:
1381
+ lower_bound = self.join(lower_bound, node.lower_bound)
1382
+ upper_bound = self.meet(upper_bound, node.upper_bound)
1383
+ result = lower_bound if not isinstance(lower_bound, BottomType) else upper_bound
1330
1384
 
1331
- result = lower_bound if not isinstance(lower_bound, BottomType) else upper_bound
1332
1385
  for node in nodes:
1333
1386
  solution[node.typevar] = result
1334
1387
  self._solution_cache[node.typevar] = result
@@ -1386,3 +1439,12 @@ class SimpleSolver:
1386
1439
  if self.bits == 64:
1387
1440
  return Pointer64
1388
1441
  raise NotImplementedError(f"Unsupported bits {self.bits}")
1442
+
1443
+ @staticmethod
1444
+ def dump_constraint_graph(graph: networkx.DiGraph, filename: str) -> None:
1445
+ """
1446
+ Dump the constraint graph to a file.
1447
+ """
1448
+ from networkx.drawing.nx_agraph import write_dot # pylint:disable=import-outside-toplevel
1449
+
1450
+ write_dot(graph, filename)
@@ -100,7 +100,8 @@ class Typehoon(Analysis):
100
100
  and not isinstance(type_.pts_to, SimTypeArray)
101
101
  ):
102
102
  type_ = type_.pts_to
103
- type_candidates.append(type_)
103
+ if type_ is not None:
104
+ type_candidates.append(type_)
104
105
 
105
106
  # determine the best type - this logic can be made better!
106
107
  if not type_candidates:
@@ -115,6 +115,15 @@ class SimEngineVRAIL(
115
115
  tc = typevars.Subtype(src.typevar, dst.typevar)
116
116
  self.state.add_type_constraint(tc)
117
117
 
118
+ def _handle_stmt_CAS(self, stmt) -> None:
119
+ self._expr(stmt.addr)
120
+ self._expr(stmt.data_lo)
121
+ if stmt.data_hi is not None:
122
+ self._expr(stmt.data_hi)
123
+ self._expr(stmt.expd_lo)
124
+ if stmt.expd_hi is not None:
125
+ self._expr(stmt.expd_hi)
126
+
118
127
  def _handle_stmt_Store(self, stmt: ailment.Stmt.Store):
119
128
  addr_r = self._expr_bv(stmt.addr)
120
129
  data = self._expr(stmt.data)
@@ -533,6 +533,7 @@ class SimEngineLightAIL(
533
533
  def __init__(self, *args, **kwargs):
534
534
  self._stmt_handlers: dict[str, Callable[[Any], StmtDataType]] = {
535
535
  "Assignment": self._handle_stmt_Assignment,
536
+ "CAS": self._handle_stmt_CAS,
536
537
  "WeakAssignment": self._handle_stmt_WeakAssignment,
537
538
  "Store": self._handle_stmt_Store,
538
539
  "Jump": self._handle_stmt_Jump,
@@ -698,6 +699,9 @@ class SimEngineLightAIL(
698
699
  @abstractmethod
699
700
  def _handle_stmt_Assignment(self, stmt: ailment.statement.Assignment) -> StmtDataType: ...
700
701
 
702
+ @abstractmethod
703
+ def _handle_stmt_CAS(self, stmt: ailment.statement.CAS) -> StmtDataType: ...
704
+
701
705
  @abstractmethod
702
706
  def _handle_stmt_WeakAssignment(self, stmt: ailment.statement.WeakAssignment) -> StmtDataType: ...
703
707
 
@@ -1013,6 +1017,9 @@ class SimEngineNostmtAIL(
1013
1017
  def _handle_stmt_WeakAssignment(self, stmt) -> StmtDataType | None:
1014
1018
  pass
1015
1019
 
1020
+ def _handle_stmt_CAS(self, stmt) -> StmtDataType | None:
1021
+ pass
1022
+
1016
1023
  def _handle_stmt_Store(self, stmt) -> StmtDataType | None:
1017
1024
  pass
1018
1025
 
@@ -127,7 +127,7 @@ class Function(Serializable):
127
127
  self._retout_sites: set[BlockNode] = set()
128
128
  # block nodes (basic block nodes) at whose ends the function terminates
129
129
  # in theory, if everything works fine, endpoints == ret_sites | jumpout_sites | callout_sites
130
- self._endpoints = defaultdict(set)
130
+ self._endpoints: defaultdict[str, set[BlockNode]] = defaultdict(set)
131
131
 
132
132
  self._call_sites = {}
133
133
  self.addr = addr
@@ -1358,7 +1358,7 @@ class Function(Serializable):
1358
1358
  return
1359
1359
 
1360
1360
  graph = self.transition_graph
1361
- end_addresses = defaultdict(list)
1361
+ end_addresses: defaultdict[int, list[BlockNode]] = defaultdict(list)
1362
1362
 
1363
1363
  for block in self.nodes:
1364
1364
  if isinstance(block, BlockNode):
@@ -1456,7 +1456,13 @@ class Function(Serializable):
1456
1456
  )
1457
1457
  else:
1458
1458
  # We gotta create a new one
1459
- l.error("normalize(): Please report it to Fish/maybe john.")
1459
+ l.error("normalize(): Please report it to Fish.")
1460
+
1461
+ # update endpoints
1462
+ for sortset in self._endpoints.values():
1463
+ if n in sortset:
1464
+ sortset.remove(n)
1465
+ sortset.add(smallest_node)
1460
1466
 
1461
1467
  end_addresses[end_addr] = [smallest_node]
1462
1468
 
@@ -1680,7 +1686,7 @@ class Function(Serializable):
1680
1686
  n = separator
1681
1687
  if must_disambiguate_by_addr:
1682
1688
  n += hex(self.addr) + separator
1683
- elif self.binary is not self.project.loader.main_object:
1689
+ elif self.binary is not self.project.loader.main_object and self.binary_name is not None:
1684
1690
  n += self.binary_name + separator
1685
1691
  return n + (display_name or self.name)
1686
1692
 
Binary file
@@ -68,6 +68,7 @@ class InspectMixinHigh(MemoryMixin):
68
68
  if not inspect or not self.state.supports_inspect:
69
69
  return super().load(addr, size=size, condition=condition, endness=endness, inspect=inspect, **kwargs)
70
70
 
71
+ r = None
71
72
  if self.category == "reg":
72
73
  self.state._inspect(
73
74
  "reg_read",
@@ -76,7 +77,9 @@ class InspectMixinHigh(MemoryMixin):
76
77
  reg_read_length=size,
77
78
  reg_read_condition=condition,
78
79
  reg_read_endness=endness,
80
+ reg_read_expr=None,
79
81
  )
82
+ r = self.state._inspect_getattr("reg_read_expr", None)
80
83
  addr = self.state._inspect_getattr("reg_read_offset", addr)
81
84
  size = self.state._inspect_getattr("reg_read_length", size)
82
85
  condition = self.state._inspect_getattr("reg_read_condition", condition)
@@ -89,13 +92,16 @@ class InspectMixinHigh(MemoryMixin):
89
92
  mem_read_length=size,
90
93
  mem_read_condition=condition,
91
94
  mem_read_endness=endness,
95
+ mem_read_expr=None,
92
96
  )
97
+ r = self.state._inspect_getattr("mem_read_expr", None)
93
98
  addr = self.state._inspect_getattr("mem_read_address", addr)
94
99
  size = self.state._inspect_getattr("mem_read_length", size)
95
100
  condition = self.state._inspect_getattr("mem_read_condition", condition)
96
101
  endness = self.state._inspect_getattr("mem_read_endness", endness)
97
102
 
98
- r = super().load(addr, size=size, condition=condition, endness=endness, inspect=inspect, **kwargs)
103
+ if r is None:
104
+ r = super().load(addr, size=size, condition=condition, endness=endness, inspect=inspect, **kwargs)
99
105
 
100
106
  if self.category == "mem":
101
107
  self.state._inspect(
angr/utils/graph.py CHANGED
@@ -734,13 +734,18 @@ class GraphUtils:
734
734
 
735
735
  # find all strongly connected components in the graph
736
736
  sccs = [scc for scc in networkx.strongly_connected_components(graph) if len(scc) > 1]
737
+ comp_indices = {}
738
+ for i, scc in enumerate(sccs):
739
+ for node in scc:
740
+ if node not in comp_indices:
741
+ comp_indices[node] = i
737
742
 
738
743
  # collapse all strongly connected components
739
744
  for src, dst in sorted(graph.edges(), key=GraphUtils._sort_edge):
740
- scc_index = GraphUtils._components_index_node(sccs, src)
745
+ scc_index = comp_indices.get(src)
741
746
  if scc_index is not None:
742
747
  src = SCCPlaceholder(scc_index)
743
- scc_index = GraphUtils._components_index_node(sccs, dst)
748
+ scc_index = comp_indices.get(dst)
744
749
  if scc_index is not None:
745
750
  dst = SCCPlaceholder(scc_index)
746
751
 
@@ -781,13 +786,6 @@ class GraphUtils:
781
786
  return ordered_nodes
782
787
  return [n for n in ordered_nodes if n in set(nodes)]
783
788
 
784
- @staticmethod
785
- def _components_index_node(components, node):
786
- for i, comp in enumerate(components):
787
- if node in comp:
788
- return i
789
- return None
790
-
791
789
  @staticmethod
792
790
  def _append_scc(
793
791
  graph: networkx.DiGraph,
@@ -810,9 +808,9 @@ class GraphUtils:
810
808
 
811
809
  if loop_head_candidates is not None:
812
810
  # find the first node that appears in loop_heads
813
- loop_head_candidates = set(loop_head_candidates)
811
+ loop_head_candidates_set = set(loop_head_candidates)
814
812
  for n in scc:
815
- if n in loop_head_candidates:
813
+ if n in loop_head_candidates_set:
816
814
  loop_head = n
817
815
  break
818
816
 
@@ -844,7 +842,7 @@ class GraphUtils:
844
842
  # pick the first one
845
843
  loop_head = sorted(scc, key=GraphUtils._sort_node)[0]
846
844
 
847
- subgraph: networkx.DiGraph = graph.subgraph(scc).copy()
845
+ subgraph: networkx.DiGraph = graph.subgraph(scc).copy() # type: ignore
848
846
  for src, _ in list(subgraph.in_edges(loop_head)):
849
847
  subgraph.remove_edge(src, loop_head)
850
848
 
@@ -8,7 +8,7 @@ import networkx
8
8
  import archinfo
9
9
  from ailment import Expression, Block
10
10
  from ailment.expression import VirtualVariable, Const, Phi, Tmp, Load, Register, StackBaseOffset, DirtyExpression, ITE
11
- from ailment.statement import Statement, Assignment, Call, Store
11
+ from ailment.statement import Statement, Assignment, Call, Store, CAS
12
12
  from ailment.block_walker import AILBlockWalkerBase
13
13
 
14
14
  from angr.knowledge_plugins.key_definitions import atoms
@@ -126,6 +126,11 @@ def get_tmp_deflocs(blocks) -> dict[CodeLocation, dict[atoms.Tmp, int]]:
126
126
  for stmt_idx, stmt in enumerate(block.statements):
127
127
  if isinstance(stmt, Assignment) and isinstance(stmt.dst, Tmp):
128
128
  tmp_to_loc[codeloc][atoms.Tmp(stmt.dst.tmp_idx, stmt.dst.bits)] = stmt_idx
129
+ if isinstance(stmt, CAS):
130
+ if isinstance(stmt.old_lo, Tmp):
131
+ tmp_to_loc[codeloc][atoms.Tmp(stmt.old_lo.tmp_idx, stmt.old_lo.bits)] = stmt_idx
132
+ if stmt.old_hi is not None and isinstance(stmt.old_hi, Tmp):
133
+ tmp_to_loc[codeloc][atoms.Tmp(stmt.old_hi.tmp_idx, stmt.old_hi.bits)] = stmt_idx
129
134
 
130
135
  return tmp_to_loc
131
136
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: angr
3
- Version: 9.2.150
3
+ Version: 9.2.153
4
4
  Summary: A multi-architecture binary analysis toolkit, with the ability to perform dynamic symbolic execution and various static analyses on binaries
5
5
  License: BSD-2-Clause
6
6
  Project-URL: Homepage, https://angr.io/
@@ -17,13 +17,13 @@ Description-Content-Type: text/markdown
17
17
  License-File: LICENSE
18
18
  Requires-Dist: cxxheaderparser
19
19
  Requires-Dist: GitPython
20
- Requires-Dist: ailment==9.2.150
21
- Requires-Dist: archinfo==9.2.150
20
+ Requires-Dist: ailment==9.2.153
21
+ Requires-Dist: archinfo==9.2.153
22
22
  Requires-Dist: cachetools
23
23
  Requires-Dist: capstone==5.0.3
24
24
  Requires-Dist: cffi>=1.14.0
25
- Requires-Dist: claripy==9.2.150
26
- Requires-Dist: cle==9.2.150
25
+ Requires-Dist: claripy==9.2.153
26
+ Requires-Dist: cle==9.2.153
27
27
  Requires-Dist: mulpyplexer
28
28
  Requires-Dist: networkx!=2.8.1,>=2.0
29
29
  Requires-Dist: protobuf>=5.28.2
@@ -31,7 +31,7 @@ Requires-Dist: psutil
31
31
  Requires-Dist: pycparser>=2.18
32
32
  Requires-Dist: pydemumble
33
33
  Requires-Dist: pyformlang
34
- Requires-Dist: pyvex==9.2.150
34
+ Requires-Dist: pyvex==9.2.153
35
35
  Requires-Dist: rich>=13.1.0
36
36
  Requires-Dist: sortedcontainers
37
37
  Requires-Dist: sympy