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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
module Backend = struct
  module Map = Map.Make (Int32)

  type t =
    { mutable data : Symbolic_value.i32 Map.t
    ; mutable chunks : Symbolic_value.i32 Map.t
    }

  let make () = { data = Map.empty; chunks = Map.empty }

  (* WARNING: because we are doing an optimization in `Symbolic_choice`, the cloned state should not refer to a mutable value of the previous state. Assuming that the original state is not mutated is wrong. *)
  let clone { data; chunks } =
    (* WARNING: it is tempting not to rebuild the record here, but...
       it must be! otherwise the mutable data points to the same location *)
    { data; chunks }

  let address a =
    let open Symbolic_choice_without_memory in
    match Smtml.Expr.view a with
    | Val (Bitv bv) when Smtml.Bitvector.numbits bv <= 32 ->
      return (Smtml.Bitvector.to_int32 bv)
    | Ptr { base; offset } ->
      let base =
        (* TODO: it seems possible to avoid this conversion *)
        Smtml.Bitvector.to_int32 base |> Symbolic_value.I32.of_concrete
      in
      let addr = Symbolic_value.I32.add base offset in
      select_i32 addr
    | _ -> select_i32 a

  let address_i32 a = a

  let load_byte a { data; _ } =
    match Map.find_opt a data with
    | None -> Smtml.Expr.value (Bitv (Smtml.Bitvector.of_int8 0))
    | Some v -> v

  let loadn m a n =
    let rec loop addr size i acc =
      if i = size then acc
      else
        let addr' = Int32.(add addr (of_int i)) in
        let byte = load_byte addr' m in
        loop addr size (i + 1) (Smtml.Expr.concat byte acc)
    in
    let v0 = load_byte a m in
    loop a n 1 v0

  let replace m k v = m.data <- Map.add k v m.data

  let storen m a v n =
    for i = 0 to n - 1 do
      let a' = Int32.add a (Int32.of_int i) in
      let v' = Smtml.Expr.extract v ~low:i ~high:(i + 1) in
      replace m a' v'
    done

  let validate_address m a range =
    let open Symbolic_choice_without_memory in
    match Smtml.Expr.view a with
    | Val (Bitv _) ->
      (* An i32 is not a pointer to a heap chunk, so its valid *)
      return (Ok a)
    | Ptr { base; offset = start_offset } -> (
      let open Symbolic_value in
      let base = Smtml.Bitvector.to_int32 base in
      match Map.find_opt base m.chunks with
      | None -> return (Error `Memory_leak_use_after_free)
      | Some chunk_size ->
        let+ is_out_of_bounds =
          let range = I32.of_int (range - 1) in
          (* end_offset: last byte we will read/write *)
          let end_offset = I32.add start_offset range in
          select
            (Symbolic_boolean.or_
               (I32.ge_u start_offset chunk_size)
               (I32.ge_u end_offset chunk_size) )
            ~prio_true:Prio.Default ~prio_false:Prio.Default
        in
        if is_out_of_bounds then Error `Memory_heap_buffer_overflow else Ok a )
    | _ ->
      (* A symbolic expression is valid, but we print to check if Ptr's are passing through here  *)
      Log.warn (fun m -> m "Saw a symbolic address: %a" Smtml.Expr.pp a);
      return (Ok a)

  let ptr v =
    let open Symbolic_choice_without_memory in
    match Smtml.Expr.view v with
    | Ptr { base; _ } ->
      let base = Smtml.Bitvector.to_int32 base in
      return base
    | _ ->
      Log.err (fun m ->
        m {|free: cannot fetch pointer base of "%a"|} Smtml.Expr.pp v );
      assert false

  let free m p =
    let open Symbolic_choice_without_memory in
    match Smtml.Expr.view p with
    | Val (Bitv bv) when Smtml.Bitvector.eqz bv ->
      return (Symbolic_value.I32.of_concrete 0l)
    | _ ->
      let* base = ptr p in
      if not @@ Map.mem base m.chunks then trap `Double_free
      else begin
        let chunks = Map.remove base m.chunks in
        m.chunks <- chunks;
        return (Symbolic_value.I32.of_concrete base)
      end

  let realloc m ~ptr ~size =
    let open Symbolic_choice_without_memory in
    let+ base = address ptr in
    let chunks = Map.add base size m.chunks in
    m.chunks <- chunks;
    Smtml.Expr.ptr base (Symbolic_value.I32.of_concrete 0l)
end

let page_size = Symbolic_value.I32.of_concrete 65_536l

type t =
  { data : Backend.t
  ; mutable size : Symbolic_value.i32
  }

let create size =
  { data = Backend.make (); size = Symbolic_value.I32.of_concrete size }

let i32 v =
  match Smtml.Expr.view v with
  | Val (Bitv i) when Smtml.Bitvector.numbits i = 32 ->
    Smtml.Bitvector.to_int32 i
  | _ -> assert false

let grow m delta =
  let old_size = Symbolic_value.I32.mul m.size page_size in
  let new_size = Symbolic_value.I32.(div (add old_size delta) page_size) in
  m.size <-
    Symbolic_boolean.select_expr
      (Symbolic_value.I32.gt new_size m.size)
      ~if_true:new_size ~if_false:m.size

let size { size; _ } = Symbolic_value.I32.mul size page_size

let size_in_pages { size; _ } = size

(* WARNING: because we are doing an optimization in `Symbolic_choice`, the cloned state should not refer to a mutable value of the previous state. Assuming that the original state is not mutated is wrong. *)
let clone_memory m = { data = Backend.clone m.data; size = m.size }

let must_be_valid_address m a n =
  let open Symbolic_choice_without_memory in
  let* addr = Backend.validate_address m a n in
  match addr with Error t -> trap t | Ok ptr -> Backend.address ptr

let load_8_s m a =
  let open Symbolic_choice_without_memory in
  let+ a = must_be_valid_address m.data a 1 in
  let v = Backend.loadn m.data a 1 in
  match Smtml.Expr.view v with
  | Val (Bitv i8) when Smtml.Bitvector.numbits i8 = 8 ->
    let i8 = Smtml.Bitvector.to_int32 i8 in
    Symbolic_value.I32.of_concrete (Int32.extend_s 8 i8)
  | _ -> Smtml.Expr.cvtop (Ty_bitv 32) (Sign_extend 24) v

let load_8_u m a =
  let open Symbolic_choice_without_memory in
  let+ a = must_be_valid_address m.data a 1 in
  let v = Backend.loadn m.data a 1 in
  match Smtml.Expr.view v with
  | Val (Bitv i) when Smtml.Bitvector.numbits i = 8 ->
    let i = Smtml.Bitvector.to_int32 i in
    Symbolic_value.I32.of_concrete i
  | _ -> Smtml.Expr.cvtop (Ty_bitv 32) (Zero_extend 24) v

let load_16_s m a =
  let open Symbolic_choice_without_memory in
  let+ a = must_be_valid_address m.data a 2 in
  let v = Backend.loadn m.data a 2 in
  match Smtml.Expr.view v with
  | Val (Bitv i16) when Smtml.Bitvector.numbits i16 = 16 ->
    let i16 = Smtml.Bitvector.to_int32 i16 in
    Symbolic_value.I32.of_concrete (Int32.extend_s 16 i16)
  | _ -> Smtml.Expr.cvtop (Ty_bitv 32) (Sign_extend 16) v

let load_16_u m a =
  let open Symbolic_choice_without_memory in
  let+ a = must_be_valid_address m.data a 2 in
  let v = Backend.loadn m.data a 2 in
  match Smtml.Expr.view v with
  | Val (Bitv i16) when Smtml.Bitvector.numbits i16 = 16 ->
    let i16 = Smtml.Bitvector.to_int32 i16 in
    Symbolic_value.I32.of_concrete i16
  | _ -> Smtml.Expr.cvtop (Ty_bitv 32) (Zero_extend 16) v

let load_32 m a =
  let open Symbolic_choice_without_memory in
  let+ a = must_be_valid_address m.data a 4 in
  let res = Backend.loadn m.data a 4 in
  Smtml.Expr.simplify res

let load_64 m a =
  let open Symbolic_choice_without_memory in
  let+ a = must_be_valid_address m.data a 8 in
  Backend.loadn m.data a 8

let store_8 m ~addr v =
  let open Symbolic_choice_without_memory in
  let+ a = must_be_valid_address m.data addr 1 in
  Backend.storen m.data a v 1

let store_16 m ~addr v =
  let open Symbolic_choice_without_memory in
  let+ a = must_be_valid_address m.data addr 2 in
  Backend.storen m.data a v 2

let store_32 m ~addr v =
  let open Symbolic_choice_without_memory in
  let+ a = must_be_valid_address m.data addr 4 in
  Backend.storen m.data a v 4

let store_64 m ~(addr : Smtml.Expr.t) v =
  let open Symbolic_choice_without_memory in
  let+ a = must_be_valid_address m.data addr 8 in
  Backend.storen m.data a v 8

let fill m ~(pos : Smtml.Expr.t) ~(len : Smtml.Expr.t) (c : char) =
  let open Symbolic_choice_without_memory in
  let* len = select_i32 len in
  let len = Int32.to_int len in
  let* pos = select_i32 pos in
  let pos = Int32.to_int pos in
  let c = Symbolic_value.I32.of_int (int_of_char c) in

  let rec aux i =
    if i = len then return ()
    else
      let addr = Symbolic_value.I32.of_int (pos + i) in
      let* () = store_8 m ~addr c in
      aux (i + 1)
  in
  aux 0

let blit m ~src ~dst ~len =
  let open Symbolic_choice_without_memory in
  let* len = select_i32 len in
  let len = Int32.to_int len in
  let* src = select_i32 src in
  let src = Int32.to_int src in
  let* dst = select_i32 dst in
  let dst = Int32.to_int dst in

  let rec aux i =
    if i = len then return ()
    else
      let addr = Symbolic_value.I32.of_int (src + i) in
      let* v = load_8_s m addr in
      let addr = Symbolic_value.I32.of_int (dst + i) in
      let* () = store_8 m ~addr v in
      aux (i + 1)
  in
  aux 0

let blit_string m str ~src ~dst ~len =
  (* This function is only used in memory init so everything will be concrete *)
  (* TODO: I am not sure this is true, this should be investigated and fixed at some point *)
  let src = Int32.to_int @@ i32 src in
  let dst = Int32.to_int @@ i32 dst in
  let len = Int32.to_int @@ i32 len in
  for i = 0 to len - 1 do
    let byte = Char.code @@ String.get str (src + i) in
    let a = Backend.address_i32 (Int32.of_int (dst + i)) in
    Backend.storen m.data a
      (Smtml.Expr.value (Bitv (Smtml.Bitvector.of_int8 byte)))
      1
  done

let get_limit_max _m = None (* TODO *)

let free m base = Backend.free m.data base

let realloc m ~ptr ~size = Backend.realloc m.data ~ptr ~size

let convert (orig_mem : Concrete_memory.t) : t =
  let s = Concrete_memory.size_in_pages orig_mem in
  create s

(** Collections of memories *)

type collection = (int * int, t) Hashtbl.t

let init () = Hashtbl.create 16

let clone collection =
  let collection' = init () in
  Hashtbl.iter
    (fun loc memory ->
      let memory' = clone_memory memory in
      Hashtbl.add collection' loc memory' )
    collection;
  collection'

let get_memory env_id (orig_memory : Concrete_memory.t) collection g_id =
  let loc = (env_id, g_id) in
  match Hashtbl.find_opt collection loc with
  | None ->
    let g = convert orig_memory in
    Hashtbl.add collection loc g;
    g
  | Some t -> t