1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
(* SPDX-License-Identifier: AGPL-3.0-or-later *)
(* Copyright © 2021-2026 OCamlPro *)
(* Written by the Owi programmers *)

let check_can_divide_by_zero invariants uuid pp instr =
  if Abstract_invariant.can_divide_by_zero invariants ~uuid then
    Log.warn (fun m ->
      m "Possible division by zero for expression:(uuid: %i) %a" uuid pp instr )
  else
    Log.info (fun m ->
      m "Passed division by zero check for expression:(uuid: %i) %a" uuid pp
        instr )

let check_i32 ~uuid ~invariants = function
  | (Div_s | Div_u | Rem_s | Rem_u : Binary.i32_instr) as instr ->
    check_can_divide_by_zero invariants uuid Binary.pp_i32_instr instr
  | Const _ | Clz | Ctz | Popcnt | Add | Sub | Mul | And | Or | Xor | Shl
  | Shr_s | Shr_u | Rotl | Rotr | Eqz | Eq | Ne | Lt_s | Lt_u | Gt_s | Gt_u
  | Le_s | Le_u | Ge_s | Ge_u | Extend8_s | Extend16_s | Wrap_i64 | Trunc_f_s _
  | Trunc_f_u _ | Trunc_sat_f_s _ | Trunc_sat_f_u _ | Reinterpret_f _ | Load _
  | Load8_s _ | Load8_u _ | Load16_s _ | Load16_u _ | Store _ | Store8 _
  | Store16 _ ->
    ()

let check_i64 ~uuid ~invariants = function
  | (Div_s | Div_u | Rem_s | Rem_u : Binary.i64_instr) as instr ->
    check_can_divide_by_zero invariants uuid Binary.pp_i64_instr instr
  | Const _ | Clz | Ctz | Popcnt | Add | Sub | Mul | And | Or | Xor | Shl
  | Shr_s | Shr_u | Rotl | Rotr | Eqz | Eq | Ne | Lt_s | Lt_u | Gt_s | Gt_u
  | Le_s | Le_u | Ge_s | Ge_u | Extend8_s | Extend16_s | Trunc_f_s _
  | Trunc_f_u _ | Trunc_sat_f_s _ | Trunc_sat_f_u _ | Reinterpret_f _ | Load _
  | Load8_s _ | Load8_u _ | Load16_s _ | Load16_u _ | Store _ | Store8 _
  | Store16 _ | Extend32_s | Extend_i32_s | Extend_i32_u | Load32_s _
  | Load32_u _ | Store32 _ ->
    ()

let check_f32 ~uuid ~invariants = function
  | (Div : Binary.f32_instr) as instr ->
    check_can_divide_by_zero invariants uuid Binary.pp_f32_instr instr
  | Const _ | Abs | Neg | Sqrt | Ceil | Floor | Trunc | Nearest | Add | Sub
  | Mul | Min | Max | Copysign | Eq | Ne | Lt | Gt | Le | Ge | Demote_f64
  | Convert_i_s _ | Convert_i_u _ | Reinterpret_i _ | Load _ | Store _ ->
    ()

let check_f64 ~uuid ~invariants = function
  | (Div : Binary.f64_instr) as instr ->
    check_can_divide_by_zero invariants uuid Binary.pp_f64_instr instr
  | Const _ | Abs | Neg | Sqrt | Ceil | Floor | Trunc | Nearest | Add | Sub
  | Mul | Min | Max | Copysign | Eq | Ne | Lt | Gt | Le | Ge | Promote_f32
  | Convert_i_s _ | Convert_i_u _ | Reinterpret_i _ | Load _ | Store _ ->
    ()

let check_simple_instruction ~invariants ~uuid = function
  | (I32 i32_instr : Binary.simple_instruction) ->
    check_i32 ~uuid ~invariants i32_instr
  | I64 i64_instr -> check_i64 ~uuid ~invariants i64_instr
  | F32 f32_instr -> check_f32 ~uuid ~invariants f32_instr
  | F64 f64_instr -> check_f64 ~uuid ~invariants f64_instr
  | V128 _ | I8x16 _ | I16x8 _ | I32x4 _ | I64x2 _ | F32x4 _ | F64x2 _ | Ref _
  | Local _ | Global _ | Table _ | Elem _ | Memory _ | Data _ | I31 _ | Struct _
  | Array _ | Drop | Select _ | Nop | Unreachable | Any_convert_extern
  | Extern_convert_any ->
    ()

let rec check_expr (expr : Binary.expr Annotated.t)
  ~(invariants : Abstract_invariant.t) ~env =
  List.iter (check_instr ~invariants ~env) expr.raw

and check_instr ({ raw; uuid; _ } : Binary.instr Annotated.t)
  ~(invariants : Abstract_invariant.t) ~env =
  match raw with
  | Simple i -> check_simple_instruction ~invariants ~uuid i
  | Block (_str_opt, _, expr) -> check_expr expr ~invariants ~env
  | If_else (_str_opt, _bt, expr_then, expr_else) ->
    check_expr ~invariants ~env expr_then;
    check_expr ~invariants ~env expr_else
  | Loop (_str_opt, _bt, expr) -> check_expr ~invariants ~env expr
  | Call idx ->
    let func = Env.Abstract.get_func ~env idx in
    begin match func with
    | Wasm func -> check_expr ~invariants ~env func.body
    | Extern _ -> ()
    end
  | Br _ | Br_if _
  | Br_table (_, _)
  | Br_on_null _ | Br_on_non_null _
  | Br_on_cast (_, _, _)
  | Br_on_cast_fail (_, _, _)
  | Return | Return_call _
  | Return_call_indirect (_, _)
  | Return_call_ref _
  | Call_indirect (_, _)
  | Call_ref _ ->
    ()

let check_module ~(env : Env.Abstract.t) ~(modul : Env.Abstract.modul)
  (invariants : Abstract_invariant.t) =
  let init_code = Env.Abstract.get_initialization_code ~env ~modul in
  check_expr ~env ~invariants (Annotated.dummy init_code)