angr 9.2.138__py3-none-macosx_11_0_arm64.whl → 9.2.139__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 (59) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/calling_convention/fact_collector.py +59 -12
  3. angr/analyses/calling_convention/utils.py +2 -2
  4. angr/analyses/cfg/cfg_fast.py +12 -4
  5. angr/analyses/decompiler/ail_simplifier.py +14 -3
  6. angr/analyses/decompiler/block_simplifier.py +0 -2
  7. angr/analyses/decompiler/callsite_maker.py +80 -14
  8. angr/analyses/decompiler/clinic.py +31 -37
  9. angr/analyses/decompiler/condition_processor.py +2 -2
  10. angr/analyses/decompiler/decompiler.py +2 -0
  11. angr/analyses/decompiler/dephication/rewriting_engine.py +16 -7
  12. angr/analyses/decompiler/optimization_passes/__init__.py +3 -0
  13. angr/analyses/decompiler/optimization_passes/condition_constprop.py +149 -0
  14. angr/analyses/decompiler/optimization_passes/deadblock_remover.py +12 -3
  15. angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +1 -1
  16. angr/analyses/decompiler/optimization_passes/optimization_pass.py +5 -2
  17. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +15 -7
  18. angr/analyses/decompiler/optimization_passes/return_duplicator_high.py +7 -10
  19. angr/analyses/decompiler/peephole_optimizations/eager_eval.py +12 -1
  20. angr/analyses/decompiler/peephole_optimizations/remove_redundant_conversions.py +61 -25
  21. angr/analyses/decompiler/peephole_optimizations/remove_redundant_shifts.py +50 -1
  22. angr/analyses/decompiler/presets/fast.py +2 -0
  23. angr/analyses/decompiler/presets/full.py +2 -0
  24. angr/analyses/decompiler/region_simplifiers/region_simplifier.py +4 -0
  25. angr/analyses/decompiler/ssailification/rewriting_engine.py +20 -2
  26. angr/analyses/decompiler/ssailification/traversal_engine.py +4 -3
  27. angr/analyses/decompiler/structured_codegen/c.py +10 -3
  28. angr/analyses/decompiler/structuring/dream.py +7 -2
  29. angr/analyses/decompiler/structuring/phoenix.py +101 -49
  30. angr/analyses/decompiler/structuring/structurer_base.py +85 -36
  31. angr/analyses/decompiler/structuring/structurer_nodes.py +3 -1
  32. angr/analyses/deobfuscator/api_obf_finder.py +6 -1
  33. angr/analyses/deobfuscator/api_obf_type2_finder.py +158 -0
  34. angr/analyses/s_propagator.py +127 -50
  35. angr/analyses/s_reaching_definitions/s_rda_view.py +2 -2
  36. angr/analyses/s_reaching_definitions/s_reaching_definitions.py +3 -1
  37. angr/analyses/variable_recovery/engine_ail.py +1 -1
  38. angr/analyses/variable_recovery/engine_base.py +55 -62
  39. angr/analyses/variable_recovery/engine_vex.py +1 -1
  40. angr/analyses/variable_recovery/irsb_scanner.py +2 -2
  41. angr/calling_conventions.py +66 -9
  42. angr/engines/engine.py +2 -18
  43. angr/engines/light/engine.py +3 -8
  44. angr/engines/pcode/emulate.py +2 -2
  45. angr/engines/pcode/lifter.py +2 -2
  46. angr/engines/successors.py +1 -8
  47. angr/engines/vex/lifter.py +2 -2
  48. angr/engines/vex/light/light.py +2 -2
  49. angr/knowledge_plugins/cfg/cfg_model.py +3 -2
  50. angr/knowledge_plugins/labels.py +2 -2
  51. angr/knowledge_plugins/obfuscations.py +1 -0
  52. angr/knowledge_plugins/xrefs/xref_manager.py +4 -0
  53. angr/lib/angr_native.dylib +0 -0
  54. {angr-9.2.138.dist-info → angr-9.2.139.dist-info}/METADATA +6 -6
  55. {angr-9.2.138.dist-info → angr-9.2.139.dist-info}/RECORD +59 -57
  56. {angr-9.2.138.dist-info → angr-9.2.139.dist-info}/LICENSE +0 -0
  57. {angr-9.2.138.dist-info → angr-9.2.139.dist-info}/WHEEL +0 -0
  58. {angr-9.2.138.dist-info → angr-9.2.139.dist-info}/entry_points.txt +0 -0
  59. {angr-9.2.138.dist-info → angr-9.2.139.dist-info}/top_level.txt +0 -0
angr/__init__.py CHANGED
@@ -2,7 +2,7 @@
2
2
  # pylint: disable=wrong-import-position
3
3
  from __future__ import annotations
4
4
 
5
- __version__ = "9.2.138"
5
+ __version__ = "9.2.139"
6
6
 
7
7
  if bytes is str:
8
8
  raise Exception(
@@ -1,3 +1,4 @@
1
+ # pylint:disable=too-many-boolean-expressions
1
2
  from __future__ import annotations
2
3
  from typing import Any
3
4
 
@@ -332,6 +333,9 @@ class FactCollector(Analysis):
332
333
  cc_cls = default_cc(
333
334
  self.project.arch.name, platform=self.project.simos.name if self.project.simos is not None else None
334
335
  )
336
+ if cc_cls is None:
337
+ # don't know what the calling convention may be... give up
338
+ return
335
339
  cc = cc_cls(self.project.arch)
336
340
  if isinstance(cc.RETURN_VAL, SimRegArg):
337
341
  retreg_offset = cc.RETURN_VAL.check_offset(self.project.arch)
@@ -401,6 +405,16 @@ class FactCollector(Analysis):
401
405
  func_graph = self.function.transition_graph
402
406
  callee_restored_regs = set()
403
407
 
408
+ sp_masks = {
409
+ 0xFFFFFFFE,
410
+ 0xFFFFFFFC,
411
+ 0xFFFFFFF8,
412
+ 0xFFFFFFF0,
413
+ 0xFFFFFFFF_FFFFFFFE,
414
+ 0xFFFFFFFF_FFFFFFFC,
415
+ 0xFFFFFFFF_FFFFFFF8,
416
+ 0xFFFFFFFF_FFFFFFF0,
417
+ }
404
418
  for endpoint in self.function.endpoints:
405
419
  traversed = set()
406
420
  queue: list[tuple[int, BlockNode | HookNode]] = [(0, endpoint)]
@@ -434,17 +448,29 @@ class FactCollector(Analysis):
434
448
  tmps[stmt.tmp] = "stack_value"
435
449
  elif isinstance(stmt.data, pyvex.IRExpr.Const):
436
450
  tmps[stmt.tmp] = "const"
437
- elif isinstance(stmt.data, pyvex.IRExpr.Binop) and ( # noqa:SIM102
438
- stmt.data.op.startswith("Iop_Add") or stmt.data.op.startswith("Iop_Sub")
439
- ):
440
- if (
441
- isinstance(stmt.data.args[0], pyvex.IRExpr.RdTmp)
442
- and tmps.get(stmt.data.args[0].tmp) == "sp"
443
- ) or (
444
- isinstance(stmt.data.args[1], pyvex.IRExpr.RdTmp)
445
- and tmps.get(stmt.data.args[1].tmp) == "sp"
446
- ):
447
- tmps[stmt.tmp] = "sp"
451
+ elif isinstance(stmt.data, pyvex.IRExpr.Binop):
452
+ if stmt.data.op.startswith("Iop_Add") or stmt.data.op.startswith("Iop_Sub"):
453
+ if (
454
+ isinstance(stmt.data.args[0], pyvex.IRExpr.RdTmp)
455
+ and tmps.get(stmt.data.args[0].tmp) == "sp"
456
+ ) or (
457
+ isinstance(stmt.data.args[1], pyvex.IRExpr.RdTmp)
458
+ and tmps.get(stmt.data.args[1].tmp) == "sp"
459
+ ):
460
+ tmps[stmt.tmp] = "sp"
461
+ elif stmt.data.op.startswith("Iop_And"): # noqa: SIM102
462
+ if (
463
+ isinstance(stmt.data.args[0], pyvex.IRExpr.RdTmp)
464
+ and tmps.get(stmt.data.args[0].tmp) == "sp"
465
+ and isinstance(stmt.data.args[1], pyvex.IRExpr.Const)
466
+ and stmt.data.args[1].con.value in sp_masks
467
+ ) or (
468
+ isinstance(stmt.data.args[1], pyvex.IRExpr.RdTmp)
469
+ and tmps.get(stmt.data.args[1].tmp) == "sp"
470
+ and isinstance(stmt.data.args[0], pyvex.IRExpr.Const)
471
+ and stmt.data.args[0].con.value in sp_masks
472
+ ):
473
+ tmps[stmt.tmp] = "sp"
448
474
  if isinstance(stmt, pyvex.IRStmt.Put):
449
475
  size = stmt.data.result_size(block.vex.tyenv) // self.project.arch.byte_width
450
476
  # is the data loaded from the stack?
@@ -460,7 +486,28 @@ class FactCollector(Analysis):
460
486
  if pred not in traversed and depth + 1 <= self._max_depth and edge_type == "transition":
461
487
  queue.append((depth + 1, pred))
462
488
 
463
- return callee_restored_regs
489
+ # remove offsets of registers that may store return values from callee_restored_regs
490
+ ret_reg_offsets = set()
491
+ cc_cls = default_cc(
492
+ self.project.arch.name, platform=self.project.simos.name if self.project.simos is not None else None
493
+ )
494
+ if cc_cls is not None:
495
+ cc = cc_cls(self.project.arch)
496
+ if isinstance(cc.RETURN_VAL, SimRegArg):
497
+ retreg_offset = cc.RETURN_VAL.check_offset(self.project.arch)
498
+ ret_reg_offsets.add(retreg_offset)
499
+ if isinstance(cc.OVERFLOW_RETURN_VAL, SimRegArg):
500
+ retreg_offset = cc.OVERFLOW_RETURN_VAL.check_offset(self.project.arch)
501
+ ret_reg_offsets.add(retreg_offset)
502
+ if isinstance(cc.FP_RETURN_VAL, SimRegArg):
503
+ try:
504
+ retreg_offset = cc.FP_RETURN_VAL.check_offset(self.project.arch)
505
+ ret_reg_offsets.add(retreg_offset)
506
+ except KeyError:
507
+ # register name does not exist
508
+ pass
509
+
510
+ return callee_restored_regs.difference(ret_reg_offsets)
464
511
 
465
512
  def _determine_input_args(self, end_states: list[FactCollectorState], callee_restored_regs: set[int]) -> None:
466
513
  self.input_args = []
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
  import logging
3
3
 
4
4
  import archinfo
5
- from archinfo.arch_arm import is_arm_arch, ArchARMHF
5
+ from archinfo.arch_arm import is_arm_arch, ArchARMHF, ArchARMCortexM
6
6
 
7
7
  from angr.calling_conventions import SimCC
8
8
 
@@ -37,7 +37,7 @@ def is_sane_register_variable(arch: archinfo.Arch, reg_offset: int, reg_size: in
37
37
  # 224 <= reg_offset < 480) # xmm0-xmm7
38
38
 
39
39
  if is_arm_arch(arch):
40
- if isinstance(arch, ArchARMHF):
40
+ if isinstance(arch, (ArchARMHF, ArchARMCortexM)):
41
41
  return 8 <= reg_offset < 24 or 128 <= reg_offset < 160 # r0 - 32 # s0 - s7, or d0 - d4
42
42
  return 8 <= reg_offset < 24 # r0-r3
43
43
 
@@ -2819,11 +2819,13 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
2819
2819
  if sec is not None and sec.is_readable and not sec.is_writable:
2820
2820
  # points to a non-writable region. read it out and see if there is another pointer!
2821
2821
  v = self._fast_memory_load_pointer(ref.data_addr, ref.data_size)
2822
+ if v is None:
2823
+ return
2822
2824
 
2823
2825
  # this value can either be a pointer or an offset from the pc... we need to try them both
2824
2826
  # attempt 1: a direct pointer
2825
2827
  sec_2nd = self.project.loader.find_section_containing(v)
2826
- if sec_2nd is not None and sec_2nd.is_readable and not sec_2nd.is_writable:
2828
+ if sec_2nd is not None and sec_2nd.is_readable:
2827
2829
  # found it!
2828
2830
  self._add_data_reference(
2829
2831
  irsb_addr,
@@ -2872,7 +2874,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
2872
2874
  actual_ref_ins_addr = ref.ins_addr + 4
2873
2875
  v += 8 + actual_ref_ins_addr
2874
2876
  sec_3rd = self.project.loader.find_section_containing(v)
2875
- if sec_3rd is not None and sec_3rd.is_readable and not sec_3rd.is_writable:
2877
+ if sec_3rd is not None and sec_3rd.is_readable:
2876
2878
  # found it!
2877
2879
  self._add_data_reference(
2878
2880
  irsb_addr, ref.stmt_idx, actual_ref_ins_addr, v, data_size=None, data_type=MemoryDataSort.Unknown
@@ -4178,7 +4180,10 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
4178
4180
  for segment in self.project.loader.main_object.segments:
4179
4181
  if segment.is_readable and segment.memsize >= 8:
4180
4182
  # the gp area is sometimes writable, so we can't test for (not segment.is_writable)
4181
- content = self.project.loader.memory.load(segment.vaddr, segment.memsize)
4183
+ try:
4184
+ content = self.project.loader.memory.load(segment.vaddr, segment.memsize)
4185
+ except KeyError:
4186
+ continue
4182
4187
  content_buf = pyvex.ffi.from_buffer(content)
4183
4188
  self._ro_region_cdata_cache.append(content_buf)
4184
4189
  pyvex.pvc.register_readonly_region(segment.vaddr, segment.memsize, content_buf)
@@ -4187,7 +4192,10 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
4187
4192
  # also map .got
4188
4193
  for section in self.project.loader.main_object.sections:
4189
4194
  if section.name == ".got":
4190
- content = self.project.loader.memory.load(section.vaddr, section.memsize)
4195
+ try:
4196
+ content = self.project.loader.memory.load(section.vaddr, section.memsize)
4197
+ except KeyError:
4198
+ continue
4191
4199
  content_buf = pyvex.ffi.from_buffer(content)
4192
4200
  self._ro_region_cdata_cache.append(content_buf)
4193
4201
  pyvex.pvc.register_readonly_region(section.vaddr, section.memsize, content_buf)
@@ -102,7 +102,7 @@ class AILSimplifier(Analysis):
102
102
  self.func = func
103
103
  self.func_graph = func_graph if func_graph is not None else func.graph
104
104
  self._reaching_definitions: SRDAModel | None = None
105
- self._propagator = None
105
+ self._propagator: SPropagatorAnalysis | None = None
106
106
 
107
107
  self._remove_dead_memdefs = remove_dead_memdefs
108
108
  self._stack_arg_offsets = stack_arg_offsets
@@ -117,6 +117,7 @@ class AILSimplifier(Analysis):
117
117
  self._removed_vvar_ids = removed_vvar_ids if removed_vvar_ids is not None else set()
118
118
  self._arg_vvars = arg_vvars
119
119
  self._avoid_vvar_ids = avoid_vvar_ids
120
+ self._propagator_dead_vvar_ids: set[int] = set()
120
121
 
121
122
  self._calls_to_remove: set[CodeLocation] = set()
122
123
  self._assignments_to_remove: set[CodeLocation] = set()
@@ -231,6 +232,7 @@ class AILSimplifier(Analysis):
231
232
  only_consts=self._only_consts,
232
233
  )
233
234
  self._propagator = prop
235
+ self._propagator_dead_vvar_ids = prop.dead_vvar_ids
234
236
  return prop
235
237
 
236
238
  def _compute_equivalence(self) -> set[Equivalence]:
@@ -247,6 +249,9 @@ class AILSimplifier(Analysis):
247
249
  if isinstance(stmt.ret_expr, (VirtualVariable, Load)):
248
250
  codeloc = CodeLocation(block.addr, stmt_idx, block_idx=block.idx, ins_addr=stmt.ins_addr)
249
251
  equivalence.add(Equivalence(codeloc, stmt.ret_expr, stmt))
252
+ elif isinstance(stmt.fp_ret_expr, (VirtualVariable, Load)):
253
+ codeloc = CodeLocation(block.addr, stmt_idx, block_idx=block.idx, ins_addr=stmt.ins_addr)
254
+ equivalence.add(Equivalence(codeloc, stmt.fp_ret_expr, stmt))
250
255
  elif (
251
256
  isinstance(stmt, Store)
252
257
  and isinstance(stmt.size, int)
@@ -1115,7 +1120,10 @@ class AILSimplifier(Analysis):
1115
1120
  than once after simplification and graph structuring where conditions might be duplicated (e.g., in Dream).
1116
1121
  In such cases, the one-use expression folder in RegionSimplifier will perform this transformation.
1117
1122
  """
1123
+ # Disabled until https://github.com/angr/angr/issues/5112 and related folding issues are fixed
1124
+ return False
1118
1125
 
1126
+ # pylint:disable=unreachable
1119
1127
  simplified = False
1120
1128
 
1121
1129
  equivalence = self._compute_equivalence()
@@ -1326,7 +1334,10 @@ class AILSimplifier(Analysis):
1326
1334
  if isinstance(def_.atom, atoms.MemoryLocation) and isinstance(def_.atom.addr, int):
1327
1335
  continue
1328
1336
  if isinstance(def_.atom, atoms.VirtualVariable):
1329
- if def_.atom.was_stack:
1337
+ if def_.atom.varid in self._propagator_dead_vvar_ids:
1338
+ # we are definitely removing this variable if it has no uses
1339
+ uses = rd.get_vvar_uses(def_.atom)
1340
+ elif def_.atom.was_stack:
1330
1341
  if not self._remove_dead_memdefs:
1331
1342
  if rd.is_phi_vvar_id(def_.atom.varid):
1332
1343
  # we always remove unused phi variables
@@ -1540,7 +1551,7 @@ class AILSimplifier(Analysis):
1540
1551
  if bail:
1541
1552
  continue
1542
1553
 
1543
- if all(varid in phi_and_dirty_vvar_ids for varid in scc):
1554
+ if all(varid in phi_and_dirty_vvar_ids or rd.varid_to_vvar[varid].was_reg for varid in scc):
1544
1555
  cyclic_dependent_phi_varids |= set(scc)
1545
1556
 
1546
1557
  return cyclic_dependent_phi_varids
@@ -58,7 +58,6 @@ class BlockSimplifier(Analysis):
58
58
  self,
59
59
  block: Block | None,
60
60
  func_addr: int | None = None,
61
- remove_dead_memdefs=False,
62
61
  stack_pointer_tracker=None,
63
62
  peephole_optimizations: None | (
64
63
  Iterable[type[PeepholeOptimizationStmtBase] | type[PeepholeOptimizationExprBase]]
@@ -74,7 +73,6 @@ class BlockSimplifier(Analysis):
74
73
  self.block = block
75
74
  self.func_addr = func_addr
76
75
 
77
- self._remove_dead_memdefs = remove_dead_memdefs
78
76
  self._stack_pointer_tracker = stack_pointer_tracker
79
77
 
80
78
  if peephole_optimizations is None:
@@ -5,9 +5,19 @@ import logging
5
5
 
6
6
  import archinfo
7
7
  from ailment import Stmt, Expr, Const
8
+ from ailment.manager import Manager
8
9
 
9
10
  from angr.procedures.stubs.format_parser import FormatParser, FormatSpecifier
10
- from angr.sim_type import SimTypeBottom, SimTypePointer, SimTypeChar, SimTypeInt, dereference_simtype
11
+ from angr.sim_type import (
12
+ SimTypeBottom,
13
+ SimTypePointer,
14
+ SimTypeChar,
15
+ SimTypeInt,
16
+ SimTypeFloat,
17
+ dereference_simtype,
18
+ SimTypeFunction,
19
+ SimTypeLongLong,
20
+ )
11
21
  from angr.calling_conventions import SimRegArg, SimStackArg, SimCC, SimStructArg, SimComboArg
12
22
  from angr.knowledge_plugins.key_definitions.constants import OP_BEFORE
13
23
  from angr.analyses import Analysis, register_analysis
@@ -27,7 +37,7 @@ class CallSiteMaker(Analysis):
27
37
  Add calling convention, declaration, and args to a call site.
28
38
  """
29
39
 
30
- def __init__(self, block, reaching_definitions=None, stack_pointer_tracker=None, ail_manager=None):
40
+ def __init__(self, block, reaching_definitions=None, stack_pointer_tracker=None, ail_manager: Manager = None):
31
41
  self.block = block
32
42
 
33
43
  self._reaching_definitions = reaching_definitions
@@ -60,7 +70,7 @@ class CallSiteMaker(Analysis):
60
70
  return
61
71
 
62
72
  cc = None
63
- prototype = None
73
+ prototype: SimTypeFunction | None = None
64
74
  func = None
65
75
  stack_arg_locs: list[SimStackArg] = []
66
76
  stackarg_sp_diff = 0
@@ -106,7 +116,9 @@ class CallSiteMaker(Analysis):
106
116
  for typelib_name in prototype_lib.type_collection_names:
107
117
  type_collections.append(SIM_TYPE_COLLECTIONS[typelib_name])
108
118
  if type_collections:
109
- prototype = dereference_simtype(prototype, type_collections).with_arch(self.project.arch)
119
+ prototype = dereference_simtype(prototype, type_collections).with_arch( # type: ignore
120
+ self.project.arch
121
+ )
110
122
 
111
123
  args = []
112
124
  arg_vvars = []
@@ -120,15 +132,18 @@ class CallSiteMaker(Analysis):
120
132
  arg_locs = cc.arg_locs(prototype)
121
133
  if prototype.variadic:
122
134
  # determine the number of variadic arguments
135
+ assert func is not None
123
136
  variadic_args = self._determine_variadic_arguments(func, cc, call_stmt)
124
137
  if variadic_args:
125
138
  callsite_ty = copy.copy(prototype)
126
- callsite_ty.args = list(callsite_ty.args)
139
+ callsite_args = list(callsite_ty.args)
140
+ base_type = SimTypeInt if self.project.arch.bits == 32 else SimTypeLongLong
127
141
  for _ in range(variadic_args):
128
- callsite_ty.args.append(SimTypeInt().with_arch(self.project.arch))
142
+ callsite_args.append(base_type().with_arch(self.project.arch))
143
+ callsite_ty.args = tuple(callsite_args)
129
144
  arg_locs = cc.arg_locs(callsite_ty)
130
145
 
131
- if arg_locs is not None:
146
+ if arg_locs is not None and cc is not None:
132
147
  expanded_arg_locs = []
133
148
  for arg_loc in arg_locs:
134
149
  if isinstance(arg_loc, SimComboArg):
@@ -155,6 +170,38 @@ class CallSiteMaker(Analysis):
155
170
  oident=vvar_def.oident,
156
171
  **vvar_def.tags,
157
172
  )
173
+ vvar_def_reg_offset = None
174
+ if vvar_def.was_reg:
175
+ vvar_def_reg_offset = vvar_def.reg_offset
176
+ elif (
177
+ vvar_def.was_parameter
178
+ and vvar_def.parameter_category == Expr.VirtualVariableCategory.REGISTER
179
+ ):
180
+ vvar_def_reg_offset = vvar_def.parameter_reg_offset
181
+
182
+ if vvar_def_reg_offset is not None and offset > vvar_def_reg_offset:
183
+ # we need to shift the value
184
+ vvar_use = Expr.BinaryOp(
185
+ self._ail_manager.next_atom(),
186
+ "Shr",
187
+ [
188
+ vvar_use,
189
+ Expr.Const(
190
+ self._ail_manager.next_atom(), None, (offset - vvar_def_reg_offset) * 8, 8
191
+ ),
192
+ ],
193
+ **vvar_use.tags,
194
+ )
195
+ if vvar_def.size > arg_loc.size:
196
+ # we need to narrow the value
197
+ vvar_use = Expr.Convert(
198
+ self._ail_manager.next_atom(),
199
+ vvar_use.bits,
200
+ arg_loc.size * self.project.arch.byte_width,
201
+ False,
202
+ vvar_use,
203
+ **vvar_use.tags,
204
+ )
158
205
  args.append(vvar_use)
159
206
  else:
160
207
  reg = Expr.Register(
@@ -219,6 +266,7 @@ class CallSiteMaker(Analysis):
219
266
  # calculate stack offsets for arguments that are put on the stack. these offsets will be consumed by
220
267
  # simplification steps in the future, which may decide to remove statements that store arguments on the stack.
221
268
  if stack_arg_locs:
269
+ assert self._stack_pointer_tracker is not None
222
270
  sp_offset = self._stack_pointer_tracker.offset_before(call_stmt.ins_addr, self.project.arch.sp_offset)
223
271
  if sp_offset is None:
224
272
  l.warning(
@@ -233,8 +281,22 @@ class CallSiteMaker(Analysis):
233
281
  }
234
282
 
235
283
  ret_expr = call_stmt.ret_expr
236
- # if ret_expr is None, it means in previous steps (such as during AIL simplification) we have deemed the return
237
- # value of this call statement as useless and is removed.
284
+ fp_ret_expr = call_stmt.fp_ret_expr
285
+ # if ret_expr and fp_ret_expr are None, it means in previous steps (such as during AIL simplification) we have
286
+ # deemed the return value of this call statement as useless and is removed.
287
+
288
+ if (
289
+ ret_expr is not None
290
+ and fp_ret_expr is not None
291
+ and prototype is not None
292
+ and prototype.returnty is not None
293
+ ):
294
+ # we need to determine the return type of this call (ret_expr vs fp_ret_expr)
295
+ is_float = isinstance(prototype.returnty, SimTypeFloat)
296
+ if is_float:
297
+ ret_expr = None
298
+ else:
299
+ fp_ret_expr = None
238
300
 
239
301
  if (
240
302
  ret_expr is not None
@@ -243,9 +305,9 @@ class CallSiteMaker(Analysis):
243
305
  and not isinstance(prototype.returnty, SimTypeBottom)
244
306
  and not isinstance(ret_expr, Expr.VirtualVariable)
245
307
  ):
246
- # try to narrow the return expression if needed
308
+ # try to narrow the non-float return expression if needed
247
309
  ret_type_bits = prototype.returnty.with_arch(self.project.arch).size
248
- if ret_expr.bits > ret_type_bits:
310
+ if ret_type_bits is not None and ret_expr.bits > ret_type_bits:
249
311
  ret_expr = ret_expr.copy()
250
312
  ret_expr.bits = ret_type_bits
251
313
  # TODO: Support narrowing virtual variables
@@ -257,6 +319,7 @@ class CallSiteMaker(Analysis):
257
319
  prototype=prototype,
258
320
  args=args,
259
321
  ret_expr=ret_expr,
322
+ fp_ret_expr=fp_ret_expr,
260
323
  arg_vvars=arg_vvars,
261
324
  **call_stmt.tags,
262
325
  )
@@ -291,7 +354,7 @@ class CallSiteMaker(Analysis):
291
354
  l.warning("TODO: Unsupported statement type %s for definitions.", type(stmt))
292
355
  return None
293
356
 
294
- def _resolve_register_argument(self, arg_loc) -> tuple[int | None, Expr.VirtualVariable] | None:
357
+ def _resolve_register_argument(self, arg_loc) -> tuple[Expr.Expression | None, Expr.VirtualVariable] | None:
295
358
  offset = arg_loc.check_offset(self.project.arch)
296
359
 
297
360
  if self._reaching_definitions is not None:
@@ -310,6 +373,8 @@ class CallSiteMaker(Analysis):
310
373
  return None
311
374
 
312
375
  def _resolve_stack_argument(self, call_stmt, arg_loc) -> tuple[Any, Any]: # pylint:disable=unused-argument
376
+ assert self._stack_pointer_tracker is not None
377
+
313
378
  size = arg_loc.size
314
379
  offset = arg_loc.stack_offset
315
380
  if self.project.arch.call_pushes_ret:
@@ -331,6 +396,7 @@ class CallSiteMaker(Analysis):
331
396
  sp_offset, size, self.block.addr, self.block.idx, len(self.block.statements) - 1, OP_BEFORE
332
397
  )
333
398
  if vvar is not None:
399
+ # FIXME: vvar may be larger than that we ask; we may need to chop the correct value of vvar
334
400
  value = view.get_vvar_value(vvar)
335
401
  if value is not None and not isinstance(value, Expr.Phi):
336
402
  return None, value
@@ -391,8 +457,8 @@ class CallSiteMaker(Analysis):
391
457
 
392
458
  return s
393
459
 
394
- def _determine_variadic_arguments(self, func: Function | None, cc: SimCC, call_stmt) -> int | None:
395
- if (func is not None and "printf" in func.name) or "scanf" in func.name:
460
+ def _determine_variadic_arguments(self, func: Function, cc: SimCC, call_stmt) -> int | None:
461
+ if "printf" in func.name or "scanf" in func.name:
396
462
  return self._determine_variadic_arguments_for_format_strings(func, cc, call_stmt)
397
463
  return None
398
464