From 02f323979c95a168bcc5889bc69449f9087271df Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20B=C3=BChler?= <david.buhler@cea.fr>
Date: Mon, 10 Jun 2024 22:17:21 +0200
Subject: [PATCH] [Eva] Improves precision of memset builtin.

---
 .../eva/domains/cvalue/builtins_memory.ml     |  88 ++++++++------
 tests/builtins/oracle/memset.res.oracle       | 109 ++++++++++++------
 2 files changed, 129 insertions(+), 68 deletions(-)

diff --git a/src/plugins/eva/domains/cvalue/builtins_memory.ml b/src/plugins/eva/domains/cvalue/builtins_memory.ml
index f0c86e9e19d..30e1e753191 100644
--- a/src/plugins/eva/domains/cvalue/builtins_memory.ml
+++ b/src/plugins/eva/domains/cvalue/builtins_memory.ml
@@ -174,14 +174,14 @@ let imprecise_copy ~name ~src_loc ~dst_loc ~dst_expr state =
      return [bottom]. In this case, return the previously computed state *)
   if Model.is_reachable new_state then new_state else state
 
-(* Creates the location {loc + [0..max_size-1]} of size char. *)
-let char_location loc max_size =
+(* Creates the location {loc + [min_size..max_size-1]} of size char. *)
+let char_location loc ?(min_size=Int.zero) max_size =
   let size_char = Bit_utils.sizeofchar () in
   let max = Int.sub max_size size_char in
   (* Use ranges modulo char_bits to read and write byte-by-byte, which can
      preserve some precision.*)
   let shift =
-    Ival.inject_interval ~min:(Some Int.zero) ~max:(Some max)
+    Ival.inject_interval ~min:(Some min_size) ~max:(Some max)
       ~rem:Int.zero ~modu:size_char
   in
   let loc = Location_Bits.shift shift loc in
@@ -271,45 +271,65 @@ let () =
 (*  Implementation of [memset] that accepts imprecise arguments. *)
 let frama_c_memset_imprecise state dst_expr dst v size =
   let size_min, size_max = min_max_size size in
+  let exact = Location_Bits.cardinal_zero_or_one dst in
+  (* Write [v] everywhere that is written, between [dst] and [dst+size_min]. *)
+  let state, min_zone =
+    if Int.gt size_min Int.zero then
+      let size = size_min in
+      let value = Cvalue.V_Or_Uninitialized.initialized v in
+      let from = Cvalue.V_Offsetmap.create ~size ~size_v:Integer.eight value in
+      warn_imprecise_offsm_write ~name:"memset" dst_expr from;
+      let state =
+        Cvalue.Model.paste_offsetmap ~from ~size ~exact ~dst_loc:dst state
+      in
+      let loc = make_loc dst (Int_Base.Value size_min) in
+      let written_zone = enumerate_valid_bits Locations.Write loc in
+      state, written_zone
+    else state, Zone.bottom
+  in
   (* Write [v] everywhere that might be written, ie between
-     [dst] and [dst+size-1]. *)
-  let state, over_zone =
-    if Int.gt size_max Int.zero then
-      let loc = char_location dst size_max in
+     [dst+size_min] and [dst+size_max-1]. *)
+  let state, extra_zone =
+    if Int.gt size_max size_min then
+      let loc = char_location dst ~min_size:size_min size_max in
       warn_imprecise_write ~name:"memset" dst_expr loc v;
       let state = Cvalue.Model.add_binding ~exact:false state loc v in
       let written_zone = enumerate_valid_bits Locations.Write loc in
       state, written_zone
     else state, Zone.bottom
   in
-  (* Write "sure" bytes in an exact way: they exist only if there is only
-     one base, and within it, size_min+leftmost_loc > rightmost_loc *)
-  let state, sure_zone =
-    try
-      let base, offset = Location_Bits.find_lonely_key dst in
-      let minb, maxb = match Ival.min_and_max offset with
-        | Some minb, Some maxb -> minb, maxb
-        | _ -> raise Not_found
-      in
-      let sure = Int.sub (Int.add minb size_min) maxb in
-      if Int.gt sure Int.zero then
-        let dst_loc = Location_Bits.inject base (Ival.inject_singleton maxb) in
-        let vuninit = V_Or_Uninitialized.initialized v in
-        let size_v = Bit_utils.sizeofchar () in
-        let from = V_Offsetmap.create ~size:sure vuninit ~size_v in
-        warn_imprecise_offsm_write ~name:"memset" dst_expr from;
-        let state =
-          Cvalue.Model.paste_offsetmap
-            ~from ~dst_loc ~size:sure ~exact:true state
+  let over_zone = Zone.join min_zone extra_zone in
+  if exact
+  then state, min_zone, over_zone
+  else
+    (* Write "sure" bytes in an exact way: they exist only if there is only
+       one base, and within it, size_min+leftmost_loc > rightmost_loc *)
+    let state, sure_zone =
+      try
+        let base, offset = Location_Bits.find_lonely_key dst in
+        let minb, maxb = match Ival.min_and_max offset with
+          | Some minb, Some maxb -> minb, maxb
+          | _ -> raise Not_found
         in
-        let sure_loc = make_loc dst_loc (Int_Base.inject sure) in
-        let sure_zone = enumerate_valid_bits Locations.Write sure_loc in
-        state, sure_zone
-      else
-        state, Zone.bottom
-    with Not_found -> state, Zone.bottom (* from find_lonely_key + explicit raise *)
-  in
-  state, sure_zone, over_zone
+        let sure = Int.sub (Int.add minb size_min) maxb in
+        if Int.gt sure Int.zero then
+          let dst_loc = Location_Bits.inject base (Ival.inject_singleton maxb) in
+          let vuninit = V_Or_Uninitialized.initialized v in
+          let size_v = Bit_utils.sizeofchar () in
+          let from = V_Offsetmap.create ~size:sure vuninit ~size_v in
+          warn_imprecise_offsm_write ~name:"memset" dst_expr from;
+          let state =
+            Cvalue.Model.paste_offsetmap
+              ~from ~dst_loc ~size:sure ~exact:true state
+          in
+          let sure_loc = make_loc dst_loc (Int_Base.inject sure) in
+          let sure_zone = enumerate_valid_bits Locations.Write sure_loc in
+          state, sure_zone
+        else
+          state, Zone.bottom
+      with Not_found -> state, Zone.bottom (* from find_lonely_key + explicit raise *)
+    in
+    state, sure_zone, over_zone
 
 (* Type that describes why the 'precise memset' builtin may fail. *)
 type imprecise_memset_reason =
diff --git a/tests/builtins/oracle/memset.res.oracle b/tests/builtins/oracle/memset.res.oracle
index 61a577a188d..ce26d8999c1 100644
--- a/tests/builtins/oracle/memset.res.oracle
+++ b/tests/builtins/oracle/memset.res.oracle
@@ -129,9 +129,6 @@
                                   {0},{12})) failed; destination is not exact
 [eva:alarm] memset.c:107: Warning: 
   accessing uninitialized left-value. assert \initialized(&s->ptr);
-[eva:alarm] memset.c:109: Warning: 
-  pointer comparison. assert \pointer_comparable((void *)q, (void *)0);
-[eva:alarm] memset.c:109: Warning: out of bounds write. assert \valid(q);
 [eva] Recording results for memset_weak_base
 [from] Computing for function memset_weak_base
 [from] Done for function memset_weak_base
@@ -142,26 +139,19 @@
 [from] Computing for function main
 [from] Done for function main
 [eva] Done for function main
-[eva:garbled-mix:summary] 
-  Origins of garbled mix generated during analysis:
-    memset.c:106: imprecise merge of addresses
-      (read in 3 statements, propagated through 1 statement)
-      garbled mix of &{x}
 [eva] memset.c:85: assertion 'Eva,initialization' got final status invalid.
 [eva] ====== VALUES COMPUTED ======
 [eva:final-states] Values at end of function memset_weak_base:
-  NULL[rbits 128 to 1927] ∈ [--..--]
   __fc_heap_status ∈ [--..--]
   x ∈ {42} or UNINITIALIZED
   s ∈ {{ &__malloc_w_memset_weak_base_l101[0] }}
-  q ∈ {{ NULL + [0..237] ; &x }}
-  __malloc_w_memset_weak_base_l101[0].i ∈ [--..--] or UNINITIALIZED
+  q ∈ {{ NULL ; &x }}
+  __malloc_w_memset_weak_base_l101[0].i ∈ {0; 421} or UNINITIALIZED
                                   [0].c ∈ {0; 1} or UNINITIALIZED
                                   [0].[bits 40 to 63] ∈
                                   {0} or UNINITIALIZED
                                   [0].ptr ∈
-                                  {{ garbled mix of &{x}
-                                   (origin: Merge {memset.c:106}) }} or UNINITIALIZED
+                                  {{ NULL ; &x }} or UNINITIALIZED
 [eva:final-states] Values at end of function test:
   NULL[rbits 128 to 143] ∈ {66} repeated %8 
       [rbits 144 to 1927] ∈ [--..--]
@@ -190,7 +180,31 @@
      [3]# ∈ {153} repeated %8 
      [4..6]# ∈ {0; 153} repeated %8 
      [7..99] ∈ {0}
-  t12[0..96]# ∈ {0; 1} repeated %8 
+  t12[0]# ∈ {0; 1} repeated %8 
+     [1..7] ∈ {0}
+     [8]# ∈ {0; 1} repeated %8 
+     [9..15] ∈ {0}
+     [16]# ∈ {0; 1} repeated %8 
+     [17..23] ∈ {0}
+     [24]# ∈ {0; 1} repeated %8 
+     [25..31] ∈ {0}
+     [32]# ∈ {0; 1} repeated %8 
+     [33..39] ∈ {0}
+     [40]# ∈ {0; 1} repeated %8 
+     [41..47] ∈ {0}
+     [48]# ∈ {0; 1} repeated %8 
+     [49..55] ∈ {0}
+     [56]# ∈ {0; 1} repeated %8 
+     [57..63] ∈ {0}
+     [64]# ∈ {0; 1} repeated %8 
+     [65..71] ∈ {0}
+     [72]# ∈ {0; 1} repeated %8 
+     [73..79] ∈ {0}
+     [80]# ∈ {0; 1} repeated %8 
+     [81..87] ∈ {0}
+     [88]# ∈ {0; 1} repeated %8 
+     [89..95] ∈ {0}
+     [96]# ∈ {0; 1} repeated %8 
      [97..99] ∈ {0}
   ts[0].f1 ∈ {-2; 0}
     [0].[bits 8 to 15] ∈ {0; 254}
@@ -224,7 +238,8 @@
   x ∈ {{ (void *)&a }}
   a ∈ {0}
 [eva:final-states] Values at end of function main:
-  NULL[rbits 128 to 1927] ∈ [--..--]
+  NULL[rbits 128 to 143] ∈ {66} repeated %8 
+      [rbits 144 to 1927] ∈ [--..--]
   __fc_heap_status ∈ [--..--]
   t1[0..99] ∈ {286331153}
   t2[0..99] ∈ {303174162}
@@ -251,7 +266,31 @@
      [3]# ∈ {153} repeated %8 
      [4..6]# ∈ {0; 153} repeated %8 
      [7..99] ∈ {0}
-  t12[0..96]# ∈ {0; 1} repeated %8 
+  t12[0]# ∈ {0; 1} repeated %8 
+     [1..7] ∈ {0}
+     [8]# ∈ {0; 1} repeated %8 
+     [9..15] ∈ {0}
+     [16]# ∈ {0; 1} repeated %8 
+     [17..23] ∈ {0}
+     [24]# ∈ {0; 1} repeated %8 
+     [25..31] ∈ {0}
+     [32]# ∈ {0; 1} repeated %8 
+     [33..39] ∈ {0}
+     [40]# ∈ {0; 1} repeated %8 
+     [41..47] ∈ {0}
+     [48]# ∈ {0; 1} repeated %8 
+     [49..55] ∈ {0}
+     [56]# ∈ {0; 1} repeated %8 
+     [57..63] ∈ {0}
+     [64]# ∈ {0; 1} repeated %8 
+     [65..71] ∈ {0}
+     [72]# ∈ {0; 1} repeated %8 
+     [73..79] ∈ {0}
+     [80]# ∈ {0; 1} repeated %8 
+     [81..87] ∈ {0}
+     [88]# ∈ {0; 1} repeated %8 
+     [89..95] ∈ {0}
+     [96]# ∈ {0; 1} repeated %8 
      [97..99] ∈ {0}
   ts[0].f1 ∈ {-2; 0}
     [0].[bits 8 to 15] ∈ {0; 254}
@@ -275,12 +314,12 @@
     [4]{.f3; .f4[0..2]} ∈ {-16843010; 0}
   S_incomplete_type[bits 0 to 7] ∈ {65}
                    [bits 8 to ..] ∈ [--..--] or UNINITIALIZED
-  __malloc_w_memset_weak_base_l101[0].i ∈ [--..--] or UNINITIALIZED
+  __malloc_w_memset_weak_base_l101[0].i ∈ {0; 421} or UNINITIALIZED
                                   [0].c ∈ {0; 1} or UNINITIALIZED
                                   [0].[bits 40 to 63] ∈
                                   {0} or UNINITIALIZED
                                   [0].ptr ∈
-                                  [--..--] or UNINITIALIZED or ESCAPINGADDR
+                                  {0} or UNINITIALIZED or ESCAPINGADDR
 [from] ====== DISPLAYING CALLWISE DEPENDENCIES ======
 [from] call to malloc at memset.c:101 (by memset_weak_base):
   __fc_heap_status FROM __fc_heap_status; size (and SELF)
@@ -325,7 +364,9 @@
   ts[0..4] FROM c
   \result FROM s
 [from] call to memset at memset.c:72 (by test):
-  t12[0..96] FROM c (and SELF)
+  t12{[0]; [8]; [16]; [24]; [32]; [40]; [48]; [56]; [64]; [72]; [80]; [88];
+      [96]}
+     FROM c (and SELF)
   \result FROM s
 [from] call to memset at memset.c:74 (by test):
   \result FROM s
@@ -353,22 +394,18 @@
      [7..12] FROM \nothing (and SELF)
   t11{[2]; [4..6]} FROM \nothing (and SELF)
      [3] FROM \nothing
-  t12[0..96] FROM \nothing (and SELF)
+  t12{[0]; [8]; [16]; [24]; [32]; [40]; [48]; [56]; [64]; [72]; [80]; [88];
+      [96]}
+     FROM \nothing (and SELF)
   ts[0..4] FROM vol (and SELF)
   S_incomplete_type[bits 0 to 7] FROM \nothing
 [from] call to uninit at memset.c:114 (by main):
   NO EFFECTS
 [from] call to memset_weak_base at memset.c:115 (by main):
-  NULL[16..240]
-      FROM __fc_heap_status;
-           __malloc_w_memset_weak_base_l101[0].ptr (and SELF)
   __fc_heap_status FROM __fc_heap_status (and SELF)
   __malloc_w_memset_weak_base_l101[0] FROM __fc_heap_status (and SELF)
 [from] entry point:
-  NULL[16..17] FROM __fc_heap_status; __malloc_w_memset_weak_base_l101[0].ptr
-      [18..240]
-      FROM __fc_heap_status;
-           __malloc_w_memset_weak_base_l101[0].ptr (and SELF)
+  NULL[16..17] FROM \nothing
   __fc_heap_status FROM __fc_heap_status (and SELF)
   t1[0..99] FROM \nothing
   t2[0..99] FROM \nothing
@@ -383,14 +420,15 @@
      [7..12] FROM \nothing (and SELF)
   t11{[2]; [4..6]} FROM \nothing (and SELF)
      [3] FROM \nothing
-  t12[0..96] FROM \nothing (and SELF)
+  t12{[0]; [8]; [16]; [24]; [32]; [40]; [48]; [56]; [64]; [72]; [80]; [88];
+      [96]}
+     FROM \nothing (and SELF)
   ts[0..4] FROM vol (and SELF)
   S_incomplete_type[bits 0 to 7] FROM \nothing
   __malloc_w_memset_weak_base_l101[0] FROM __fc_heap_status (and SELF)
 [from] ====== END OF CALLWISE DEPENDENCIES ======
 [inout] Out (internal) for function memset_weak_base:
-    NULL[16..240]; __fc_heap_status; x; s; q;
-    __malloc_w_memset_weak_base_l101[0]
+    __fc_heap_status; x; s; q; __malloc_w_memset_weak_base_l101[0]
 [inout] Inputs for function memset_weak_base:
     __fc_heap_status; __malloc_w_memset_weak_base_l101[0].ptr
 [inout] InOut (internal) for function memset_weak_base:
@@ -403,7 +441,8 @@
 [inout] Out (internal) for function test:
     NULL[16..17]; t1[0..99]; t2[0..99]; t3[10..99]; t4[1..99]; t5[0..99];
     t6[10..13]; t7[0..3]; t8[0..3]; t9[20..99]; t10[4..12]; t11[2..6];
-    t12[0..96]; ts[0..4]; dst; p; tmp_0; s; s1; k; absolute_valid_range;
+    t12{[0]; [8]; [16]; [24]; [32]; [40]; [48]; [56]; [64]; [72]; [80]; [88];
+        [96]}; ts[0..4]; dst; p; tmp_0; s; s1; k; absolute_valid_range;
     S_incomplete_type[bits 0 to 7]
 [inout] Inputs for function test:
     incomplete_type; vol
@@ -428,10 +467,12 @@
   Sure outputs:
     a
 [inout] Out (internal) for function main:
-    NULL[16..240]; __fc_heap_status; t1[0..99]; t2[0..99]; t3[10..99];
+    NULL[16..17]; __fc_heap_status; t1[0..99]; t2[0..99]; t3[10..99];
     t4[1..99]; t5[0..99]; t6[10..13]; t7[0..3]; t8[0..3]; t9[20..99];
-    t10[4..12]; t11[2..6]; t12[0..96]; ts[0..4];
-    S_incomplete_type[bits 0 to 7]; __malloc_w_memset_weak_base_l101[0]
+    t10[4..12]; t11[2..6];
+    t12{[0]; [8]; [16]; [24]; [32]; [40]; [48]; [56]; [64]; [72]; [80]; [88];
+        [96]}; ts[0..4]; S_incomplete_type[bits 0 to 7];
+    __malloc_w_memset_weak_base_l101[0]
 [inout] Inputs for function main:
     __fc_heap_status; incomplete_type; vol;
     __malloc_w_memset_weak_base_l101[0].ptr
-- 
GitLab