angr 9.2.95__py3-none-manylinux2014_x86_64.whl → 9.2.97__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 (55) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/cfg/cfg_fast.py +9 -6
  3. angr/analyses/cfg/indirect_jump_resolvers/const_resolver.py +6 -1
  4. angr/analyses/complete_calling_conventions.py +27 -11
  5. angr/analyses/decompiler/ail_simplifier.py +30 -8
  6. angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +20 -7
  7. angr/analyses/decompiler/clinic.py +21 -5
  8. angr/analyses/decompiler/condition_processor.py +11 -0
  9. angr/analyses/decompiler/decompiler.py +58 -46
  10. angr/analyses/decompiler/optimization_passes/__init__.py +11 -5
  11. angr/analyses/decompiler/optimization_passes/flip_boolean_cmp.py +13 -7
  12. angr/analyses/decompiler/optimization_passes/optimization_pass.py +31 -11
  13. angr/analyses/decompiler/optimization_passes/{return_duplicator.py → return_duplicator_base.py} +54 -102
  14. angr/analyses/decompiler/optimization_passes/return_duplicator_high.py +57 -0
  15. angr/analyses/decompiler/optimization_passes/return_duplicator_low.py +121 -0
  16. angr/analyses/decompiler/region_identifier.py +13 -0
  17. angr/analyses/decompiler/seq_to_blocks.py +19 -0
  18. angr/analyses/decompiler/structured_codegen/c.py +21 -0
  19. angr/analyses/decompiler/structuring/phoenix.py +28 -4
  20. angr/analyses/decompiler/structuring/recursive_structurer.py +35 -1
  21. angr/analyses/decompiler/structuring/structurer_base.py +3 -0
  22. angr/analyses/decompiler/utils.py +41 -6
  23. angr/analyses/disassembly.py +4 -1
  24. angr/analyses/find_objects_static.py +15 -10
  25. angr/analyses/forward_analysis/forward_analysis.py +15 -1
  26. angr/analyses/propagator/engine_ail.py +40 -0
  27. angr/analyses/propagator/propagator.py +6 -3
  28. angr/analyses/reaching_definitions/engine_ail.py +16 -24
  29. angr/analyses/reaching_definitions/rd_state.py +14 -1
  30. angr/analyses/reaching_definitions/reaching_definitions.py +19 -2
  31. angr/analyses/variable_recovery/engine_ail.py +6 -6
  32. angr/analyses/variable_recovery/engine_base.py +22 -4
  33. angr/analyses/variable_recovery/variable_recovery_base.py +4 -1
  34. angr/engines/light/engine.py +8 -1
  35. angr/knowledge_plugins/key_definitions/atoms.py +4 -2
  36. angr/knowledge_plugins/key_definitions/environment.py +11 -0
  37. angr/knowledge_plugins/key_definitions/live_definitions.py +41 -8
  38. angr/knowledge_plugins/key_definitions/uses.py +18 -4
  39. angr/knowledge_plugins/propagations/states.py +22 -3
  40. angr/knowledge_plugins/types.py +6 -0
  41. angr/knowledge_plugins/variables/variable_manager.py +54 -5
  42. angr/simos/simos.py +2 -0
  43. angr/storage/memory_mixins/__init__.py +3 -0
  44. angr/storage/memory_mixins/multi_value_merger_mixin.py +22 -11
  45. angr/storage/memory_mixins/paged_memory/paged_memory_mixin.py +20 -2
  46. angr/storage/memory_mixins/paged_memory/pages/mv_list_page.py +81 -44
  47. angr/utils/cowdict.py +4 -2
  48. angr/utils/funcid.py +6 -0
  49. angr/utils/mp.py +1 -1
  50. {angr-9.2.95.dist-info → angr-9.2.97.dist-info}/METADATA +6 -6
  51. {angr-9.2.95.dist-info → angr-9.2.97.dist-info}/RECORD +55 -52
  52. {angr-9.2.95.dist-info → angr-9.2.97.dist-info}/LICENSE +0 -0
  53. {angr-9.2.95.dist-info → angr-9.2.97.dist-info}/WHEEL +0 -0
  54. {angr-9.2.95.dist-info → angr-9.2.97.dist-info}/entry_points.txt +0 -0
  55. {angr-9.2.95.dist-info → angr-9.2.97.dist-info}/top_level.txt +0 -0
@@ -55,7 +55,7 @@ class ReachingDefinitionsAnalysis(
55
55
  self,
56
56
  subject: Union[Subject, ailment.Block, Block, Function, str] = None,
57
57
  func_graph=None,
58
- max_iterations=3,
58
+ max_iterations=30,
59
59
  track_tmps=False,
60
60
  track_consts=True,
61
61
  observation_points: "Iterable[ObservationPoint]" = None,
@@ -74,6 +74,7 @@ class ReachingDefinitionsAnalysis(
74
74
  interfunction_level: int = 0,
75
75
  track_liveness: bool = True,
76
76
  func_addr: Optional[int] = None,
77
+ element_limit: int = 5,
77
78
  ):
78
79
  """
79
80
  :param subject: The subject of the analysis: a function, or a single basic block
@@ -131,6 +132,7 @@ class ReachingDefinitionsAnalysis(
131
132
  self._canonical_size = canonical_size
132
133
  self._use_callee_saved_regs_at_return = use_callee_saved_regs_at_return
133
134
  self._func_addr = func_addr
135
+ self._element_limit = element_limit
134
136
 
135
137
  if dep_graph is None or dep_graph is False:
136
138
  self._dep_graph = None
@@ -469,13 +471,28 @@ class ReachingDefinitionsAnalysis(
469
471
  analysis=self,
470
472
  canonical_size=self._canonical_size,
471
473
  initializer=self._state_initializer,
474
+ element_limit=self._element_limit,
472
475
  )
473
476
 
474
477
  # pylint: disable=no-self-use,arguments-differ
475
478
  def _merge_states(self, _node, *states: ReachingDefinitionsState):
479
+ assert len(states) >= 2
476
480
  merged_state, merge_occurred = states[0].merge(*states[1:])
477
481
  return merged_state, not merge_occurred
478
482
 
483
+ def _compare_states(self, node, old_state: ReachingDefinitionsState, new_state: ReachingDefinitionsState) -> bool:
484
+ """
485
+ Return True if new_state >= old_state in the lattice.
486
+
487
+ :param node:
488
+ :param old_state:
489
+ :param new_state:
490
+ :return:
491
+ """
492
+
493
+ reached_fixedpoint = new_state.compare(old_state)
494
+ return reached_fixedpoint
495
+
479
496
  def _run_on_node(self, node, state: ReachingDefinitionsState):
480
497
  """
481
498
 
@@ -550,7 +567,7 @@ class ReachingDefinitionsAnalysis(
550
567
  state.downsize()
551
568
 
552
569
  if self._node_iterations[block_key] < self._max_iterations:
553
- return True, state
570
+ return None, state
554
571
  else:
555
572
  return False, state
556
573
 
@@ -469,20 +469,20 @@ class SimEngineVRAIL(
469
469
 
470
470
  r0 = self._expr(arg0)
471
471
  r1 = self._expr(arg1)
472
- from_size = r1.bits
473
- to_size = r0.bits
472
+ from_size = expr.from_bits
473
+ to_size = expr.to_bits
474
474
 
475
475
  if expr.signed:
476
- quotient = r0.data.SDiv(claripy.SignExt(to_size - from_size, r1.data))
477
- remainder = r0.data.SMod(claripy.SignExt(to_size - from_size, r1.data))
476
+ quotient = r0.data.SDiv(claripy.SignExt(from_size - to_size, r1.data))
477
+ remainder = r0.data.SMod(claripy.SignExt(from_size - to_size, r1.data))
478
478
  quotient_size = to_size
479
479
  remainder_size = to_size
480
480
  r = claripy.Concat(
481
481
  claripy.Extract(remainder_size - 1, 0, remainder), claripy.Extract(quotient_size - 1, 0, quotient)
482
482
  )
483
483
  else:
484
- quotient = r0.data // claripy.ZeroExt(to_size - from_size, r1.data)
485
- remainder = r0.data % claripy.ZeroExt(to_size - from_size, r1.data)
484
+ quotient = r0.data // claripy.ZeroExt(from_size - to_size, r1.data)
485
+ remainder = r0.data % claripy.ZeroExt(from_size - to_size, r1.data)
486
486
  quotient_size = to_size
487
487
  remainder_size = to_size
488
488
  r = claripy.Concat(
@@ -270,10 +270,8 @@ class SimEngineVRBase(SimEngineLight):
270
270
  return
271
271
 
272
272
  if not existing_vars:
273
- l.warning(
274
- "_reference() is called on expressions without variables associated. Did you call "
275
- "_ensure_variable_existence() first?"
276
- )
273
+ # no associated variables. it's usually because _ensure_variable_existence() is not called, or the address
274
+ # is a TOP. we ignore this case.
277
275
  return
278
276
  else:
279
277
  variable, _ = existing_vars[0]
@@ -392,6 +390,15 @@ class SimEngineVRBase(SimEngineLight):
392
390
  stored = True
393
391
 
394
392
  if not stored:
393
+ # remove existing variables linked to this statement
394
+ existing_vars = self.variable_manager[self.func_addr].find_variables_by_stmt(
395
+ self.block.addr, self.stmt_idx, "memory"
396
+ )
397
+ codeloc = self._codeloc()
398
+ if existing_vars:
399
+ for existing_var, _ in list(existing_vars):
400
+ self.variable_manager[self.func_addr].remove_variable_by_atom(codeloc, existing_var, stmt)
401
+
395
402
  # storing to a location specified by a pointer whose value cannot be determined at this point
396
403
  self._store_to_variable(richr_addr, size, stmt=stmt)
397
404
 
@@ -602,6 +609,7 @@ class SimEngineVRBase(SimEngineLight):
602
609
  self.block.addr, self.stmt_idx, ins_addr=self.ins_addr, block_idx=getattr(self.block, "idx", None)
603
610
  )
604
611
  typevar = None
612
+ v = None
605
613
 
606
614
  if self.state.is_stack_address(addr):
607
615
  stack_offset = self.state.get_stack_offset(addr)
@@ -743,6 +751,16 @@ class SimEngineVRBase(SimEngineLight):
743
751
  v = self._load_from_global(base_addr.concrete_value, size, expr=expr, offset=offset, elem_size=elem_size)
744
752
  typevar = v.typevar
745
753
 
754
+ if v is None and expr is not None:
755
+ # failed to map the address to a known variable
756
+ # remove existing variables linked to this variable
757
+ existing_vars = self.variable_manager[self.func_addr].find_variables_by_atom(
758
+ self.block.addr, self.stmt_idx, expr
759
+ )
760
+ if existing_vars:
761
+ for existing_var, _ in list(existing_vars):
762
+ self.variable_manager[self.func_addr].remove_variable_by_atom(codeloc, existing_var, expr)
763
+
746
764
  # Loading data from a pointer
747
765
  if richr_addr.type_constraints:
748
766
  for tc in richr_addr.type_constraints:
@@ -175,6 +175,7 @@ class VariableRecoveryStateBase:
175
175
  self.stack_region: MultiValuedMemory = MultiValuedMemory(
176
176
  memory_id="mem",
177
177
  top_func=self.top,
178
+ is_top_func=self.is_top,
178
179
  phi_maker=self._make_phi_variable,
179
180
  skip_missing_values_during_merging=True,
180
181
  page_kwargs={"mo_cmp": self._mo_cmp},
@@ -188,6 +189,7 @@ class VariableRecoveryStateBase:
188
189
  self.register_region: MultiValuedMemory = MultiValuedMemory(
189
190
  memory_id="reg",
190
191
  top_func=self.top,
192
+ is_top_func=self.is_top,
191
193
  phi_maker=self._make_phi_variable,
192
194
  skip_missing_values_during_merging=True,
193
195
  page_kwargs={"mo_cmp": self._mo_cmp},
@@ -201,6 +203,7 @@ class VariableRecoveryStateBase:
201
203
  self.global_region: MultiValuedMemory = MultiValuedMemory(
202
204
  memory_id="mem",
203
205
  top_func=self.top,
206
+ is_top_func=self.is_top,
204
207
  phi_maker=self._make_phi_variable,
205
208
  skip_missing_values_during_merging=True,
206
209
  page_kwargs={"mo_cmp": self._mo_cmp},
@@ -215,7 +218,7 @@ class VariableRecoveryStateBase:
215
218
  self.type_constraints = defaultdict(set) if type_constraints is None else type_constraints
216
219
  self.func_typevar = func_typevar
217
220
  self.delayed_type_constraints = (
218
- DefaultChainMapCOW(set, collapse_threshold=25)
221
+ DefaultChainMapCOW(default_factory=set, collapse_threshold=25)
219
222
  if delayed_type_constraints is None
220
223
  else delayed_type_constraints
221
224
  )
@@ -1078,7 +1078,14 @@ class SimEngineLightAILMixin(SimEngineLightMixin):
1078
1078
  expr_1 = arg1
1079
1079
 
1080
1080
  return ailment.Expr.BinaryOp(
1081
- expr.idx, "DivMod", [expr_0, expr_1], expr.signed, bits=expr_0.bits * 2, **expr.tags
1081
+ expr.idx,
1082
+ "DivMod",
1083
+ [expr_0, expr_1],
1084
+ expr.signed,
1085
+ bits=expr.bits,
1086
+ from_bits=expr.from_bits,
1087
+ to_bits=expr.to_bits,
1088
+ **expr.tags,
1082
1089
  )
1083
1090
 
1084
1091
  def _ail_handle_Mod(self, expr):
@@ -71,7 +71,7 @@ class Atom:
71
71
  Instanciate an `Atom` from a given argument.
72
72
 
73
73
  :param argument: The argument to create a new atom from.
74
- :param registers: A mapping representing the registers of a given architecture.
74
+ :param arch: The argument representing archinfo architecture for argument.
75
75
  :param full_reg: Whether to return an atom indicating the entire register if the argument only specifies a
76
76
  slice of the register.
77
77
  :param sp: The current stack offset. Optional. Only used when argument is a SimStackArg.
@@ -84,7 +84,9 @@ class Atom:
84
84
  elif isinstance(argument, SimStackArg):
85
85
  if sp is None:
86
86
  raise ValueError("You must provide a stack pointer to translate a SimStackArg")
87
- return MemoryLocation(SpOffset(arch.bits, argument.stack_offset + sp), argument.size)
87
+ return MemoryLocation(
88
+ SpOffset(arch.bits, argument.stack_offset + sp), argument.size, endness=arch.memory_endness
89
+ )
88
90
  else:
89
91
  raise TypeError("Argument type %s is not yet supported." % type(argument))
90
92
 
@@ -14,6 +14,8 @@ class Environment:
14
14
  **Note**: The <Environment> object does not store the values associated with variables themselves.
15
15
  """
16
16
 
17
+ __slots__ = ("_environment",)
18
+
17
19
  def __init__(self, environment: Dict[Union[str, Undefined], Set[claripy.ast.Base]] = None):
18
20
  self._environment: Dict[Union[str, Undefined], Set[claripy.ast.Base]] = environment or {}
19
21
 
@@ -81,3 +83,12 @@ class Environment:
81
83
 
82
84
  merge_occurred = new_env != self._environment
83
85
  return Environment(environment=new_env), merge_occurred
86
+
87
+ def compare(self, other: "Environment") -> bool:
88
+ for k in set(self._environment.keys()).union(set(other._environment.keys())):
89
+ if k not in self._environment:
90
+ return False
91
+ if k in self._environment and k in other._environment:
92
+ if not self._environment[k].issuperset(other._environment[k]):
93
+ return False
94
+ return True
@@ -49,11 +49,12 @@ class DefinitionAnnotation(Annotation):
49
49
  An annotation that attaches a `Definition` to an AST.
50
50
  """
51
51
 
52
- __slots__ = ("definition",)
52
+ __slots__ = ("definition", "_hash")
53
53
 
54
54
  def __init__(self, definition):
55
55
  super().__init__()
56
56
  self.definition = definition
57
+ self._hash = hash((DefinitionAnnotation, self.definition))
57
58
 
58
59
  @property
59
60
  def relocatable(self):
@@ -64,15 +65,11 @@ class DefinitionAnnotation(Annotation):
64
65
  return False
65
66
 
66
67
  def __hash__(self):
67
- return hash((self.definition, self.relocatable, self.eliminatable))
68
+ return self._hash
68
69
 
69
70
  def __eq__(self, other: "object"):
70
- if isinstance(other, DefinitionAnnotation):
71
- return (
72
- self.definition == other.definition
73
- and self.relocatable == other.relocatable
74
- and self.eliminatable == other.eliminatable
75
- )
71
+ if type(other) is DefinitionAnnotation:
72
+ return self.definition == other.definition
76
73
  else:
77
74
  return False
78
75
 
@@ -129,6 +126,7 @@ class LiveDefinitions:
129
126
  memory_uses=None,
130
127
  tmp_uses=None,
131
128
  other_uses=None,
129
+ element_limit=5,
132
130
  ):
133
131
  self.project: Optional["Project"] = None
134
132
  self.arch = arch
@@ -139,9 +137,11 @@ class LiveDefinitions:
139
137
  MultiValuedMemory(
140
138
  memory_id="reg",
141
139
  top_func=self.top,
140
+ is_top_func=self.is_top,
142
141
  skip_missing_values_during_merging=False,
143
142
  page_kwargs={"mo_cmp": self._mo_cmp},
144
143
  endness=self.arch.register_endness,
144
+ element_limit=element_limit,
145
145
  )
146
146
  if registers is None
147
147
  else registers
@@ -150,8 +150,10 @@ class LiveDefinitions:
150
150
  MultiValuedMemory(
151
151
  memory_id="mem",
152
152
  top_func=self.top,
153
+ is_top_func=self.is_top,
153
154
  skip_missing_values_during_merging=False,
154
155
  page_kwargs={"mo_cmp": self._mo_cmp},
156
+ element_limit=element_limit,
155
157
  )
156
158
  if stack is None
157
159
  else stack
@@ -160,8 +162,10 @@ class LiveDefinitions:
160
162
  MultiValuedMemory(
161
163
  memory_id="mem",
162
164
  top_func=self.top,
165
+ is_top_func=self.is_top,
163
166
  skip_missing_values_during_merging=False,
164
167
  page_kwargs={"mo_cmp": self._mo_cmp},
168
+ element_limit=element_limit,
165
169
  )
166
170
  if memory is None
167
171
  else memory
@@ -170,8 +174,10 @@ class LiveDefinitions:
170
174
  MultiValuedMemory(
171
175
  memory_id="mem",
172
176
  top_func=self.top,
177
+ is_top_func=self.is_top,
173
178
  skip_missing_values_during_merging=False,
174
179
  page_kwargs={"mo_cmp": self._mo_cmp},
180
+ element_limit=element_limit,
175
181
  )
176
182
  if heap is None
177
183
  else heap
@@ -464,6 +470,33 @@ class LiveDefinitions:
464
470
 
465
471
  return state, merge_occurred
466
472
 
473
+ def compare(self, other: "LiveDefinitions") -> bool:
474
+ r0 = self.registers.compare(other.registers)
475
+ if r0 is False:
476
+ return False
477
+ r1 = self.heap.compare(other.heap)
478
+ if r1 is False:
479
+ return False
480
+ r2 = self.memory.compare(other.memory)
481
+ if r2 is False:
482
+ return False
483
+ r3 = self.stack.compare(other.stack)
484
+ if r3 is False:
485
+ return False
486
+
487
+ r4 = True
488
+ for k in other.others:
489
+ if k in self.others:
490
+ thing = self.others[k].merge(other.others[k])
491
+ if thing != self.others[k]:
492
+ r4 = False
493
+ break
494
+ else:
495
+ r4 = False
496
+ break
497
+
498
+ return r0 and r1 and r2 and r3 and r4
499
+
467
500
  def kill_definitions(self, atom: Atom) -> None:
468
501
  """
469
502
  Overwrite existing definitions w.r.t 'atom' with a dummy definition instance. A dummy definition will not be
@@ -21,10 +21,14 @@ class Uses:
21
21
  uses_by_location: Optional[DefaultChainMapCOW] = None,
22
22
  ):
23
23
  self._uses_by_definition: DefaultChainMapCOW["Definition", Set[Tuple[CodeLocation, Optional[Any]]]] = (
24
- DefaultChainMapCOW(set, collapse_threshold=25) if uses_by_definition is None else uses_by_definition
24
+ DefaultChainMapCOW(default_factory=set, collapse_threshold=25)
25
+ if uses_by_definition is None
26
+ else uses_by_definition
25
27
  )
26
28
  self._uses_by_location: DefaultChainMapCOW[CodeLocation, Set[Tuple["Definition", Optional[Any]]]] = (
27
- DefaultChainMapCOW(set, collapse_threshold=25) if uses_by_location is None else uses_by_location
29
+ DefaultChainMapCOW(default_factory=set, collapse_threshold=25)
30
+ if uses_by_location is None
31
+ else uses_by_location
28
32
  )
29
33
 
30
34
  def add_use(self, definition: "Definition", codeloc: CodeLocation, expr: Optional[Any] = None):
@@ -35,7 +39,9 @@ class Uses:
35
39
  :param codeloc: The code location where the use occurs.
36
40
  :param expr: The expression that uses the specified definition at this location.
37
41
  """
42
+ self._uses_by_definition = self._uses_by_definition.clean()
38
43
  self._uses_by_definition[definition].add((codeloc, expr))
44
+ self._uses_by_location = self._uses_by_location.clean()
39
45
  self._uses_by_location[codeloc].add((definition, expr))
40
46
 
41
47
  def get_uses(self, definition: "Definition") -> Set[CodeLocation]:
@@ -65,6 +71,7 @@ class Uses:
65
71
  """
66
72
  if definition in self._uses_by_definition:
67
73
  if codeloc in self._uses_by_definition[definition]:
74
+ self._uses_by_definition = self._uses_by_definition.clean()
68
75
  if expr is None:
69
76
  for codeloc_, expr_ in list(self._uses_by_definition[definition]):
70
77
  if codeloc_ == codeloc:
@@ -73,6 +80,7 @@ class Uses:
73
80
  self._uses_by_definition[definition].remove((codeloc, expr))
74
81
 
75
82
  if codeloc in self._uses_by_location:
83
+ self._uses_by_location = self._uses_by_location.clean()
76
84
  for item in list(self._uses_by_location[codeloc]):
77
85
  if item[0] == definition:
78
86
  self._uses_by_location[codeloc].remove(item)
@@ -85,9 +93,11 @@ class Uses:
85
93
  :return: None
86
94
  """
87
95
  if definition in self._uses_by_definition:
96
+ self._uses_by_definition = self._uses_by_definition.clean()
88
97
  codeloc_and_ids = self._uses_by_definition[definition]
89
98
  del self._uses_by_definition[definition]
90
99
 
100
+ self._uses_by_location = self._uses_by_location.clean()
91
101
  for codeloc, _ in codeloc_and_ids:
92
102
  for item in list(self._uses_by_location[codeloc]):
93
103
  if item[0] == definition:
@@ -149,18 +159,22 @@ class Uses:
149
159
 
150
160
  for k, v in other._uses_by_definition.items():
151
161
  if k not in self._uses_by_definition:
162
+ self._uses_by_definition = self._uses_by_definition.clean()
152
163
  self._uses_by_definition[k] = v
153
164
  merge_occurred = True
154
165
  elif not v.issubset(self._uses_by_definition[k]):
155
166
  merge_occurred = True
156
- self._uses_by_definition[k] |= v
167
+ self._uses_by_definition = self._uses_by_definition.clean()
168
+ self._uses_by_definition[k] = self._uses_by_definition[k] | v
157
169
 
158
170
  for k, v in other._uses_by_location.items():
159
171
  if k not in self._uses_by_location:
172
+ self._uses_by_location = self._uses_by_location.clean()
160
173
  self._uses_by_location[k] = v
161
174
  merge_occurred = True
162
175
  elif not v.issubset(self._uses_by_location[k]):
163
176
  merge_occurred = True
164
- self._uses_by_location[k] |= v
177
+ self._uses_by_location = self._uses_by_location.clean()
178
+ self._uses_by_location[k] = self._uses_by_location[k] | v
165
179
 
166
180
  return merge_occurred
@@ -221,9 +221,10 @@ class PropagatorState:
221
221
  merge_occurred = True
222
222
  else:
223
223
  if PropagatorState.is_top(repl) or PropagatorState.is_top(replacements_0[loc][var]):
224
- t = PropagatorState.top(_get_repl_size(repl))
225
- replacements_0[loc][var] = t
226
- merge_occurred = True
224
+ if not PropagatorState.is_top(replacements_0[loc][var]):
225
+ t = PropagatorState.top(_get_repl_size(repl))
226
+ replacements_0[loc][var] = t
227
+ merge_occurred = True
227
228
  elif (
228
229
  isinstance(replacements_0[loc][var], claripy.ast.Base) or isinstance(repl, claripy.ast.Base)
229
230
  ) and replacements_0[loc][var] is not repl:
@@ -316,6 +317,12 @@ class RegisterAnnotation(claripy.Annotation):
316
317
  def relocatable(self) -> bool:
317
318
  return True
318
319
 
320
+ def __hash__(self):
321
+ return hash((RegisterAnnotation, self.offset, self.size))
322
+
323
+ def __eq__(self, other):
324
+ return type(other) is RegisterAnnotation and self.offset == other.offset and self.size == other.size
325
+
319
326
 
320
327
  class RegisterComparisonAnnotation(claripy.Annotation):
321
328
  """
@@ -336,6 +343,18 @@ class RegisterComparisonAnnotation(claripy.Annotation):
336
343
  def relocatable(self) -> bool:
337
344
  return True
338
345
 
346
+ def __hash__(self):
347
+ return hash((RegisterComparisonAnnotation, self.offset, self.size, self.cmp_op, self.value))
348
+
349
+ def __eq__(self, other):
350
+ return (
351
+ type(other) is RegisterAnnotation
352
+ and self.offset == other.offset
353
+ and self.size == other.size
354
+ and self.cmp_op == other.cmp_op
355
+ and self.value == other.value
356
+ )
357
+
339
358
 
340
359
  class PropagatorVEXState(PropagatorState):
341
360
  """
@@ -56,6 +56,12 @@ class TypesStore(KnowledgeBasePlugin, UserDict):
56
56
  yield from super().__iter__()
57
57
  yield from iter(ALL_TYPES)
58
58
 
59
+ def __getstate__(self):
60
+ return self.data # do not pickle self.kb
61
+
62
+ def __setstate__(self, state):
63
+ self.data = state
64
+
59
65
  def iter_own(self):
60
66
  """
61
67
  Iterate over all the names which are stored in this object - i.e. ``values()`` without ``ALL_TYPES``
@@ -122,7 +122,32 @@ class VariableManagerInternal(Serializable):
122
122
  self.__dict__.update(state)
123
123
 
124
124
  def __getstate__(self):
125
- d = dict(self.__dict__)
125
+ attributes = [
126
+ "func_addr",
127
+ "_variables",
128
+ "_global_region",
129
+ "_stack_region",
130
+ "_register_region",
131
+ "_live_variables",
132
+ "_variable_accesses",
133
+ "_insn_to_variable",
134
+ "_stmt_to_variable",
135
+ "_variable_to_stmt",
136
+ "_atom_to_variable",
137
+ "_ident_to_variable",
138
+ "_variable_counters",
139
+ "_unified_variables",
140
+ "_variables_to_unified_variables",
141
+ "_phi_variables",
142
+ "_variables_to_phivars",
143
+ "_phi_variables_by_block",
144
+ "types",
145
+ "variable_to_types",
146
+ "variables_with_manual_types",
147
+ "_variables_without_writes",
148
+ "ret_val_size",
149
+ ]
150
+ d = {k: getattr(self, k) for k in attributes}
126
151
  d["manager"] = None
127
152
  d["types"].kb = None
128
153
  return d
@@ -441,6 +466,29 @@ class VariableManagerInternal(Serializable):
441
466
  if atom_hash is not None:
442
467
  self._atom_to_variable[key][atom_hash].add(var_and_offset)
443
468
 
469
+ def remove_variable_by_atom(self, location: "CodeLocation", variable: SimVariable, atom):
470
+ key = (
471
+ (location.block_addr, location.stmt_idx)
472
+ if location.block_idx is None
473
+ else (location.block_addr, location.block_idx, location.stmt_idx)
474
+ )
475
+ if key in self._stmt_to_variable:
476
+ for var_and_offset in list(self._stmt_to_variable[key]):
477
+ if var_and_offset[0] == variable:
478
+ self._stmt_to_variable[key].remove(var_and_offset)
479
+ if not self._stmt_to_variable[key]:
480
+ del self._stmt_to_variable[key]
481
+
482
+ atom_hash = (hash(atom) & 0xFFFF_FFFF) if atom is not None else None
483
+ if key in self._atom_to_variable and atom_hash is not None and atom_hash in self._atom_to_variable[key]:
484
+ for var_and_offset in list(self._atom_to_variable[key][atom_hash]):
485
+ if var_and_offset[0] == variable:
486
+ self._atom_to_variable[key][atom_hash].discard(var_and_offset)
487
+ if not self._atom_to_variable[key][atom_hash]:
488
+ del self._atom_to_variable[key][atom_hash]
489
+ if not self._atom_to_variable[key]:
490
+ del self._atom_to_variable[key]
491
+
444
492
  def make_phi_node(self, block_addr, *variables):
445
493
  """
446
494
  Create a phi variable for variables at block `block_addr`.
@@ -736,10 +784,11 @@ class VariableManagerInternal(Serializable):
736
784
  if variable in self._phi_variables:
737
785
  # a phi variable is definitely not an input variable
738
786
  continue
739
- accesses = self._variable_accesses[variable]
740
- if has_read_access(accesses):
741
- if not exclude_specials or not variable.category:
742
- input_variables.append(variable)
787
+ if variable in self._variable_accesses:
788
+ accesses = self._variable_accesses[variable]
789
+ if has_read_access(accesses):
790
+ if not exclude_specials or not variable.category:
791
+ input_variables.append(variable)
743
792
 
744
793
  return input_variables
745
794
 
angr/simos/simos.py CHANGED
@@ -277,6 +277,8 @@ class SimOS:
277
277
 
278
278
  if state.arch.name == "PPC64" and toc is not None:
279
279
  state.regs.r2 = toc
280
+ elif state.arch.name in ("MIPS32", "MIPS64"):
281
+ state.regs.t9 = addr
280
282
 
281
283
  return state
282
284
 
@@ -67,6 +67,9 @@ class MemoryMixin(SimStatePlugin):
67
67
  def merge(self, others, merge_conditions, common_ancestor=None) -> bool:
68
68
  pass
69
69
 
70
+ def compare(self, other) -> bool:
71
+ pass
72
+
70
73
  def widen(self, others):
71
74
  pass
72
75
 
@@ -1,13 +1,17 @@
1
+ # pylint:disable=missing-class-docstring
1
2
  from typing import Iterable, Tuple, Any, Callable, Optional
2
3
 
3
4
  from . import MemoryMixin
4
5
 
5
6
 
6
7
  class MultiValueMergerMixin(MemoryMixin):
7
- def __init__(self, *args, element_limit=5, annotation_limit=256, top_func=None, phi_maker=None, **kwargs):
8
+ def __init__(
9
+ self, *args, element_limit=5, annotation_limit=256, top_func=None, is_top_func=None, phi_maker=None, **kwargs
10
+ ):
8
11
  self._element_limit = element_limit
9
12
  self._annotation_limit = annotation_limit
10
13
  self._top_func: Callable = top_func
14
+ self._is_top_func: Callable = is_top_func
11
15
  self._phi_maker: Optional[Callable] = phi_maker
12
16
 
13
17
  super().__init__(*args, **kwargs)
@@ -20,18 +24,23 @@ class MultiValueMergerMixin(MemoryMixin):
20
24
  return {phi_var}
21
25
 
22
26
  # try to merge it in the traditional way
23
- if len(values_set) > self._element_limit:
24
- # strip annotations from each value and see how many raw values there are in total
25
- # We have to use cache_key to determine uniqueness here, because if __hash__ collides,
26
- # python implicitly calls __eq__ to determine if the two objects are actually the same
27
- # and that just results in a new AST for a BV. Python then tries to convert that AST to a bool
28
- # which fails with the safeguard in claripy.ast.bool.Bool.__bool__.
29
- stripped_values_set = {v._apply_to_annotations(lambda alist: None).cache_key for v in values_set}
30
- if len(stripped_values_set) > 1:
27
+ has_top = any(self._is_top_func(v) for v in values_set)
28
+ if has_top or len(values_set) > self._element_limit:
29
+ if has_top:
31
30
  ret_val = self._top_func(merged_size * self.state.arch.byte_width)
32
31
  else:
33
- # Get the AST back from the cache_key
34
- ret_val = next(iter(stripped_values_set)).ast
32
+ # strip annotations from each value and see how many raw values there are in total
33
+ # We have to use cache_key to determine uniqueness here, because if __hash__ collides,
34
+ # python implicitly calls __eq__ to determine if the two objects are actually the same
35
+ # and that just results in a new AST for a BV. Python then tries to convert that AST to a bool
36
+ # which fails with the safeguard in claripy.ast.bool.Bool.__bool__.
37
+ stripped_values_set = {v._apply_to_annotations(lambda alist: None).cache_key for v in values_set}
38
+ if len(stripped_values_set) > 1:
39
+ ret_val = self._top_func(merged_size * self.state.arch.byte_width)
40
+ else:
41
+ # Get the AST back from the cache_key
42
+ ret_val = next(iter(stripped_values_set)).ast
43
+
35
44
  # migrate annotations
36
45
  annotations = []
37
46
  annotations_set = set()
@@ -41,6 +50,7 @@ class MultiValueMergerMixin(MemoryMixin):
41
50
  annotations.append(anno)
42
51
  annotations_set.add(anno)
43
52
  if annotations:
53
+ annotations = sorted(annotations, key=str)
44
54
  ret_val = ret_val.annotate(*annotations[: self._annotation_limit])
45
55
  merged_val = {ret_val}
46
56
  else:
@@ -52,5 +62,6 @@ class MultiValueMergerMixin(MemoryMixin):
52
62
  copied._element_limit = self._element_limit
53
63
  copied._annotation_limit = self._annotation_limit
54
64
  copied._top_func = self._top_func
65
+ copied._is_top_func = self._is_top_func
55
66
  copied._phi_maker = self._phi_maker
56
67
  return copied