angr 9.2.149__py3-none-manylinux2014_x86_64.whl → 9.2.152__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 (32) hide show
  1. angr/__init__.py +1 -1
  2. angr/__main__.py +100 -37
  3. angr/analyses/calling_convention/calling_convention.py +17 -9
  4. angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +39 -0
  5. angr/analyses/decompiler/clinic.py +73 -1
  6. angr/analyses/decompiler/dephication/rewriting_engine.py +38 -1
  7. angr/analyses/decompiler/optimization_passes/condition_constprop.py +6 -0
  8. angr/analyses/decompiler/optimization_passes/engine_base.py +5 -0
  9. angr/analyses/decompiler/optimization_passes/flip_boolean_cmp.py +2 -1
  10. angr/analyses/decompiler/peephole_optimizations/__init__.py +2 -0
  11. angr/analyses/decompiler/peephole_optimizations/cas_intrinsics.py +115 -0
  12. angr/analyses/decompiler/ssailification/rewriting_engine.py +37 -1
  13. angr/analyses/decompiler/ssailification/traversal_engine.py +10 -1
  14. angr/analyses/decompiler/utils.py +17 -0
  15. angr/analyses/disassembly.py +2 -1
  16. angr/analyses/patchfinder.py +1 -1
  17. angr/analyses/reaching_definitions/engine_ail.py +20 -0
  18. angr/analyses/s_propagator.py +28 -0
  19. angr/analyses/stack_pointer_tracker.py +2 -1
  20. angr/analyses/typehoon/typehoon.py +4 -1
  21. angr/analyses/variable_recovery/engine_ail.py +9 -0
  22. angr/engines/light/engine.py +7 -0
  23. angr/engines/pcode/lifter.py +7 -0
  24. angr/storage/memory_mixins/clouseau_mixin.py +7 -1
  25. angr/utils/graph.py +61 -39
  26. angr/utils/ssa/__init__.py +6 -1
  27. {angr-9.2.149.dist-info → angr-9.2.152.dist-info}/METADATA +6 -6
  28. {angr-9.2.149.dist-info → angr-9.2.152.dist-info}/RECORD +32 -31
  29. {angr-9.2.149.dist-info → angr-9.2.152.dist-info}/WHEEL +1 -1
  30. {angr-9.2.149.dist-info → angr-9.2.152.dist-info}/entry_points.txt +0 -0
  31. {angr-9.2.149.dist-info → angr-9.2.152.dist-info}/licenses/LICENSE +0 -0
  32. {angr-9.2.149.dist-info → angr-9.2.152.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,4 @@
1
- # pylint:disable=no-self-use,unused-argument
1
+ # pylint:disable=no-self-use,unused-argument,too-many-boolean-expressions
2
2
  from __future__ import annotations
3
3
  from typing import Literal
4
4
  import logging
@@ -9,6 +9,7 @@ from ailment.manager import Manager
9
9
  from ailment.statement import (
10
10
  Statement,
11
11
  Assignment,
12
+ CAS,
12
13
  Store,
13
14
  Call,
14
15
  Return,
@@ -18,6 +19,7 @@ from ailment.statement import (
18
19
  WeakAssignment,
19
20
  )
20
21
  from ailment.expression import (
22
+ Atom,
21
23
  Expression,
22
24
  Register,
23
25
  VirtualVariable,
@@ -204,6 +206,40 @@ class SimEngineSSARewriting(
204
206
  )
205
207
  return None
206
208
 
209
+ def _handle_stmt_CAS(self, stmt: CAS) -> CAS | None:
210
+ new_addr = self._expr(stmt.addr)
211
+ new_data_lo = self._expr(stmt.data_lo)
212
+ new_data_hi = self._expr(stmt.data_hi) if stmt.data_hi is not None else None
213
+ new_expd_lo = self._expr(stmt.expd_lo)
214
+ new_expd_hi = self._expr(stmt.expd_hi) if stmt.expd_hi is not None else None
215
+ new_old_lo = self._expr(stmt.old_lo)
216
+ new_old_hi = self._expr(stmt.old_hi) if stmt.old_hi is not None else None
217
+ assert new_old_lo is None or isinstance(new_old_lo, Atom)
218
+ assert new_old_hi is None or isinstance(new_old_hi, Atom)
219
+
220
+ if (
221
+ new_addr is not None
222
+ or new_old_lo is not None
223
+ or new_old_hi is not None
224
+ or new_data_lo is not None
225
+ or new_data_hi is not None
226
+ or new_expd_lo is not None
227
+ or new_expd_hi is not None
228
+ ):
229
+ return CAS(
230
+ stmt.idx,
231
+ stmt.addr if new_addr is None else new_addr,
232
+ stmt.data_lo if new_data_lo is None else new_data_lo,
233
+ stmt.data_hi if new_data_hi is None else new_data_hi,
234
+ stmt.expd_lo if new_expd_lo is None else new_expd_lo,
235
+ stmt.expd_hi if new_expd_hi is None else new_expd_hi,
236
+ stmt.old_lo if new_old_lo is None else new_old_lo,
237
+ stmt.old_hi if new_old_hi is None else new_old_hi,
238
+ stmt.endness,
239
+ **stmt.tags,
240
+ )
241
+ return None
242
+
207
243
  def _handle_stmt_Store(self, stmt: Store) -> Store | Assignment | tuple[Assignment, ...] | None:
208
244
  new_data = self._expr(stmt.data)
209
245
  if stmt.guard is None:
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
  from collections import OrderedDict
3
3
 
4
- from ailment.statement import Call, Store, ConditionalJump
4
+ from ailment.statement import Call, Store, ConditionalJump, CAS
5
5
  from ailment.expression import Register, BinaryOp, StackBaseOffset, ITE, VEXCCallExpression, Tmp, DirtyExpression, Load
6
6
 
7
7
  from angr.engines.light import SimEngineLightAIL
@@ -64,6 +64,15 @@ class SimEngineSSATraversal(SimEngineLightAIL[TraversalState, None, None, None])
64
64
  self._expr(stmt.src)
65
65
  self._expr(stmt.dst)
66
66
 
67
+ def _handle_stmt_CAS(self, stmt: CAS):
68
+ self._expr(stmt.addr)
69
+ self._expr(stmt.data_lo)
70
+ if stmt.data_hi is not None:
71
+ self._expr(stmt.data_hi)
72
+ self._expr(stmt.expd_lo)
73
+ if stmt.expd_hi is not None:
74
+ self._expr(stmt.expd_hi)
75
+
67
76
  def _handle_stmt_Store(self, stmt: Store):
68
77
  self._expr(stmt.addr)
69
78
  self._expr(stmt.data)
@@ -723,6 +723,23 @@ def structured_node_is_simple_return(
723
723
  return False
724
724
 
725
725
 
726
+ def structured_node_is_simple_return_strict(node: BaseNode | SequenceNode | MultiNode | ailment.Block) -> bool:
727
+ """
728
+ Returns True iff the node exclusively contains a return statement.
729
+ """
730
+ if isinstance(node, (SequenceNode, MultiNode)) and node.nodes:
731
+ flat_blocks = _flatten_structured_node(node)
732
+ if len(flat_blocks) != 1:
733
+ return False
734
+ node = flat_blocks[-1]
735
+
736
+ return (
737
+ isinstance(node, ailment.Block)
738
+ and len(node.statements) == 1
739
+ and isinstance(node.statements[0], ailment.Stmt.Return)
740
+ )
741
+
742
+
726
743
  def is_statement_terminating(stmt: ailment.statement.Statement, functions) -> bool:
727
744
  if isinstance(stmt, ailment.Stmt.Return):
728
745
  return True
@@ -1159,6 +1159,7 @@ class Disassembly(Analysis):
1159
1159
  show_bytes: bool = False,
1160
1160
  ascii_only: bool | None = None,
1161
1161
  color: bool = True,
1162
+ min_edge_depth: int = 0,
1162
1163
  ) -> str:
1163
1164
  """
1164
1165
  Render the disassembly to a string, with optional edges and addresses.
@@ -1288,7 +1289,7 @@ class Disassembly(Analysis):
1288
1289
  for f, t in sorted(edges_by_line, key=lambda e: abs(e[0] - e[1])):
1289
1290
  add_edge_to_buffer(edge_buf, ref_buf, f, t, lambda s: ansi_color(s, edge_col), ascii_only=ascii_only)
1290
1291
  add_edge_to_buffer(ref_buf, ref_buf, f, t, ascii_only=ascii_only)
1291
- max_edge_depth = max(map(len, ref_buf))
1292
+ max_edge_depth = max(*map(len, ref_buf), min_edge_depth)
1292
1293
 
1293
1294
  # Justify edge and combine with disassembly
1294
1295
  for i, line in enumerate(buf):
@@ -97,7 +97,7 @@ class PatchFinderAnalysis(Analysis):
97
97
  # - Looking for instruction partials broken by a patch (nodecode)
98
98
  # - Unusual stack manipulation
99
99
 
100
- atypical_alignments: list[Function]
100
+ atypical_alignments: list[AtypicallyAlignedFunction]
101
101
  possibly_patched_out: list[PatchedOutFunctionality]
102
102
 
103
103
  def __init__(self):
@@ -143,6 +143,26 @@ class SimEngineRDAIL(
143
143
  else:
144
144
  l.warning("Unsupported type of Assignment dst %s.", type(dst).__name__)
145
145
 
146
+ def _handle_stmt_CAS(self, stmt: ailment.statement.CAS):
147
+ addr = self._expr(stmt.addr)
148
+ old_lo = stmt.old_lo
149
+ old_hi = stmt.old_hi
150
+
151
+ self._expr(stmt.data_lo)
152
+ if stmt.data_hi is not None:
153
+ self._expr(stmt.data_hi)
154
+ self._expr(stmt.expd_lo)
155
+ if stmt.expd_hi is not None:
156
+ self._expr(stmt.expd_hi)
157
+
158
+ if isinstance(old_lo, ailment.Tmp):
159
+ self.state.kill_and_add_definition(Tmp(old_lo.tmp_idx, old_lo.size), addr)
160
+ self.tmps[old_lo.tmp_idx] = self._top(old_lo.size)
161
+
162
+ if isinstance(old_hi, ailment.Tmp):
163
+ self.state.kill_and_add_definition(Tmp(old_hi.tmp_idx, old_hi.size), addr)
164
+ self.tmps[old_hi.tmp_idx] = self._top(old_hi.size)
165
+
146
166
  def _handle_stmt_Store(self, stmt: ailment.Stmt.Store) -> None:
147
167
  data = self._expr(stmt.data)
148
168
  addr = self._expr_bv(stmt.addr)
@@ -511,5 +511,33 @@ class SPropagatorAnalysis(Analysis):
511
511
  }
512
512
  return (block_1.addr, block_1.idx) in stmt_0_targets
513
513
 
514
+ @staticmethod
515
+ def vvar_dep_graph(blocks, vvar_def_locs, vvar_use_locs) -> networkx.DiGraph:
516
+ g = networkx.DiGraph()
517
+
518
+ for var_id in vvar_def_locs:
519
+ # where is it used?
520
+ for _, use_loc in vvar_use_locs[var_id]:
521
+ if isinstance(use_loc, ExternalCodeLocation):
522
+ g.add_edge(var_id, "ExternalCodeLocation")
523
+ continue
524
+ assert use_loc.block_addr is not None
525
+ assert use_loc.stmt_idx is not None
526
+ block = blocks[(use_loc.block_addr, use_loc.block_idx)]
527
+ stmt = block.statements[use_loc.stmt_idx]
528
+ if isinstance(stmt, Assignment):
529
+ if isinstance(stmt.dst, VirtualVariable):
530
+ g.add_edge(var_id, stmt.dst.varid)
531
+ else:
532
+ g.add_edge(var_id, f"Assignment@{stmt.ins_addr:#x}")
533
+ elif isinstance(stmt, Store):
534
+ # store to memory
535
+ g.add_edge(var_id, f"Store@{stmt.ins_addr:#x}")
536
+ else:
537
+ # other statements
538
+ g.add_edge(var_id, f"{stmt.__class__.__name__}@{stmt.ins_addr:#x}")
539
+
540
+ return g
541
+
514
542
 
515
543
  register_analysis(SPropagatorAnalysis, "SPropagator")
@@ -791,7 +791,8 @@ class StackPointerTracker(Analysis, ForwardAnalysis):
791
791
  sp_adjusted = True
792
792
  sp_v = state.regs[self.project.arch.sp_offset]
793
793
  sp_v -= Constant(stmt.data.con.value)
794
- state.put(self.project.arch.sp_offset, sp_v, force=True)
794
+ state.put(self.project.arch.sp_offset, sp_v, force=True) # sp -= OFFSET
795
+ state.put(stmt.offset, Constant(0), force=True) # rax = 0
795
796
  break
796
797
 
797
798
  callee_cleanups = [
@@ -263,7 +263,10 @@ class Typehoon(Analysis):
263
263
  max_offset = offsets[-1]
264
264
  field0_size = 1
265
265
  if not isinstance(field0, TopType):
266
- field0_size = field0.size
266
+ try:
267
+ field0_size = field0.size
268
+ except NotImplementedError:
269
+ field0_size = 1
267
270
  count = (max_offset + field0_size) // alignment
268
271
  return Array(field0, count=count)
269
272
 
@@ -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
 
@@ -427,6 +427,13 @@ class IRSB:
427
427
 
428
428
  return exits
429
429
 
430
+ @property
431
+ def is_noop_block(self) -> bool:
432
+ """
433
+ Returns True if this block is a no-op block (i.e. it has no instructions and no jumps).
434
+ """
435
+ return not any(op.opcode != pypcode.OpCode.IMARK for op in self._ops)
436
+
430
437
  #
431
438
  # private methods
432
439
  #
@@ -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
@@ -672,9 +672,40 @@ class GraphUtils:
672
672
  addrs_to_index = {n.addr: i for (i, n) in enumerate(post_order)}
673
673
  return sorted(nodes, key=lambda n: addrs_to_index[n.addr], reverse=True)
674
674
 
675
+ @staticmethod
676
+ def _sort_node(node):
677
+ """
678
+ A sorter to make a deterministic order of nodes.
679
+ """
680
+ if hasattr(node, "addr"):
681
+ return node.addr
682
+ return node
683
+
684
+ @staticmethod
685
+ def _sort_edge(edge):
686
+ """
687
+ A sorter to make a deterministic order of edges.
688
+ """
689
+ _src, _dst = edge
690
+ src_addr, dst_addr = 0, 0
691
+ if hasattr(_src, "addr"):
692
+ src_addr = _src.addr
693
+ elif isinstance(_src, int):
694
+ src_addr = _src
695
+
696
+ if hasattr(_dst, "addr"):
697
+ dst_addr = _dst.addr
698
+ elif isinstance(_dst, int):
699
+ dst_addr = _dst
700
+
701
+ return src_addr + dst_addr
702
+
675
703
  @staticmethod
676
704
  def quasi_topological_sort_nodes(
677
- graph: networkx.DiGraph, nodes: list | None = None, loop_heads: list | None = None
705
+ graph: networkx.DiGraph,
706
+ nodes: list | None = None,
707
+ loop_heads: list | None = None,
708
+ panic_mode_threshold: int = 3000,
678
709
  ) -> list:
679
710
  """
680
711
  Sort a given set of nodes from a graph based on the following rules:
@@ -688,6 +719,7 @@ class GraphUtils:
688
719
  :param graph: A local transition graph of the function.
689
720
  :param nodes: A list of nodes to sort. None if you want to sort all nodes inside the graph.
690
721
  :param loop_heads: A list of nodes that should be treated loop heads.
722
+ :param panic_mode_threshold: Threshold of nodes in an SCC to begin aggressively removing edges.
691
723
  :return: A list of ordered nodes.
692
724
  """
693
725
 
@@ -702,32 +734,18 @@ class GraphUtils:
702
734
 
703
735
  # find all strongly connected components in the graph
704
736
  sccs = [scc for scc in networkx.strongly_connected_components(graph) if len(scc) > 1]
705
-
706
- def _sort_edge(edge):
707
- """
708
- A sorter to make a deterministic order of edges.
709
- """
710
- _src, _dst = edge
711
- src_addr, dst_addr = 0, 0
712
- if hasattr(_src, "addr"):
713
- src_addr = _src.addr
714
- elif isinstance(_src, int):
715
- src_addr = _src
716
-
717
- if hasattr(_dst, "addr"):
718
- dst_addr = _dst.addr
719
- elif isinstance(_dst, int):
720
- dst_addr = _dst
721
-
722
- return src_addr + dst_addr
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
723
742
 
724
743
  # collapse all strongly connected components
725
- edges = sorted(graph.edges(), key=_sort_edge)
726
- for src, dst in edges:
727
- scc_index = GraphUtils._components_index_node(sccs, src)
744
+ for src, dst in sorted(graph.edges(), key=GraphUtils._sort_edge):
745
+ scc_index = comp_indices.get(src)
728
746
  if scc_index is not None:
729
747
  src = SCCPlaceholder(scc_index)
730
- scc_index = GraphUtils._components_index_node(sccs, dst)
748
+ scc_index = comp_indices.get(dst)
731
749
  if scc_index is not None:
732
750
  dst = SCCPlaceholder(scc_index)
733
751
 
@@ -754,7 +772,13 @@ class GraphUtils:
754
772
  ordered_nodes = []
755
773
  for n in tmp_nodes:
756
774
  if isinstance(n, SCCPlaceholder):
757
- GraphUtils._append_scc(graph, ordered_nodes, sccs[n.scc_id], loop_head_candidates=loop_heads)
775
+ GraphUtils._append_scc(
776
+ graph,
777
+ ordered_nodes,
778
+ sccs[n.scc_id],
779
+ loop_head_candidates=loop_heads,
780
+ panic_mode_threshold=panic_mode_threshold,
781
+ )
758
782
  else:
759
783
  ordered_nodes.append(n)
760
784
 
@@ -762,16 +786,13 @@ class GraphUtils:
762
786
  return ordered_nodes
763
787
  return [n for n in ordered_nodes if n in set(nodes)]
764
788
 
765
- @staticmethod
766
- def _components_index_node(components, node):
767
- for i, comp in enumerate(components):
768
- if node in comp:
769
- return i
770
- return None
771
-
772
789
  @staticmethod
773
790
  def _append_scc(
774
- graph: networkx.DiGraph, ordered_nodes: list, scc: set, loop_head_candidates: list | None = None
791
+ graph: networkx.DiGraph,
792
+ ordered_nodes: list,
793
+ scc: set,
794
+ loop_head_candidates: list | None = None,
795
+ panic_mode_threshold: int = 3000,
775
796
  ) -> None:
776
797
  """
777
798
  Append all nodes from a strongly connected component to a list of ordered nodes and ensure the topological
@@ -780,15 +801,16 @@ class GraphUtils:
780
801
  :param graph: The graph where all nodes belong to.
781
802
  :param ordered_nodes: Ordered nodes.
782
803
  :param scc: A set of nodes that forms a strongly connected component in the graph.
804
+ :param panic_mode_threshold: Threshold of nodes in an SCC to begin aggressively removing edges.
783
805
  """
784
806
 
785
807
  loop_head = None
786
808
 
787
809
  if loop_head_candidates is not None:
788
810
  # find the first node that appears in loop_heads
789
- loop_head_candidates = set(loop_head_candidates)
811
+ loop_head_candidates_set = set(loop_head_candidates)
790
812
  for n in scc:
791
- if n in loop_head_candidates:
813
+ if n in loop_head_candidates_set:
792
814
  loop_head = n
793
815
  break
794
816
 
@@ -817,10 +839,10 @@ class GraphUtils:
817
839
  break
818
840
 
819
841
  if loop_head is None:
820
- # randomly pick one
821
- loop_head = next(iter(scc))
842
+ # pick the first one
843
+ loop_head = sorted(scc, key=GraphUtils._sort_node)[0]
822
844
 
823
- subgraph: networkx.DiGraph = graph.subgraph(scc).copy()
845
+ subgraph: networkx.DiGraph = graph.subgraph(scc).copy() # type: ignore
824
846
  for src, _ in list(subgraph.in_edges(loop_head)):
825
847
  subgraph.remove_edge(src, loop_head)
826
848
 
@@ -828,8 +850,8 @@ class GraphUtils:
828
850
  # will take too long to converge if we only remove one node out of the component each time. we introduce a
829
851
  # panic mode that will aggressively remove edges
830
852
 
831
- if len(subgraph) > 3000 and len(subgraph.edges) > len(subgraph) * 1.4:
832
- for n0, n1 in sorted(dfs_back_edges(subgraph, loop_head), key=lambda x: (x[0].addr, x[0].addr)):
853
+ if len(subgraph) > panic_mode_threshold and len(subgraph.edges) > len(subgraph) * 1.4:
854
+ for n0, n1 in sorted(dfs_back_edges(subgraph, loop_head), key=GraphUtils._sort_edge):
833
855
  subgraph.remove_edge(n0, n1)
834
856
  if len(subgraph.edges) <= len(subgraph) * 1.4:
835
857
  break
@@ -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.149
3
+ Version: 9.2.152
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.149
21
- Requires-Dist: archinfo==9.2.149
20
+ Requires-Dist: ailment==9.2.152
21
+ Requires-Dist: archinfo==9.2.152
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.149
26
- Requires-Dist: cle==9.2.149
25
+ Requires-Dist: claripy==9.2.152
26
+ Requires-Dist: cle==9.2.152
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.149
34
+ Requires-Dist: pyvex==9.2.152
35
35
  Requires-Dist: rich>=13.1.0
36
36
  Requires-Dist: sortedcontainers
37
37
  Requires-Dist: sympy