diff --git a/src/kernel_services/abstract_interp/locations.ml b/src/kernel_services/abstract_interp/locations.ml
index 6af4807ad9b794215796de279d6fd12c749bf790..64b75c8765fc4219d2c831d6d1f165cd59197ca2 100644
--- a/src/kernel_services/abstract_interp/locations.ml
+++ b/src/kernel_services/abstract_interp/locations.ml
@@ -286,41 +286,6 @@ module Location_Bytes = struct
      | Base.Variable { Base.weak } -> not weak
    with Not_found -> false
 
- let iter_on_strings =
-   let z = "\000" in
-   fun ~skip f l ->
-     match l with
-     | Top _ ->
-         assert false
-     | Map m ->
-         M.iter
-           (fun base offs ->
-             match skip with
-               Some base_to_skip when Base.equal base base_to_skip -> ()
-             | _ ->
-                 match base with
-                   Base.String (_, strid) ->
-                     let str = 
-		       match strid with
-		       | Base.CSString s -> s
-		       | Base.CSWstring _ -> 
-			   failwith "Unimplemented: wide strings"
-		     in
-                     let strz = str ^ z in
-                     let len = String.length str in
-                     let range =
-                       Ival.inject_range
-                         (Some Int.zero)
-                         (Some (Int.of_int len))
-                     in
-                     let roffs = Ival.narrow range offs in
-                     Ival.fold_int
-                       (fun i () -> f base strz (Int.to_int i) len)
-                       roffs
-                       ()
-                 | _ -> ())
-           m
-
  let topify_merge_origin v =
    topify_with_origin_kind Origin.K_Merge v
 
diff --git a/src/kernel_services/abstract_interp/locations.mli b/src/kernel_services/abstract_interp/locations.mli
index f96d8f6bccf067d275003dec375f9f13a8bb9d5d..b8de6dce344930a6d04a3a1b97088f0a966d7976 100644
--- a/src/kernel_services/abstract_interp/locations.mli
+++ b/src/kernel_services/abstract_interp/locations.mli
@@ -194,9 +194,6 @@ module Location_Bytes : sig
 
   (** {2 Misc} *)
 
-  val iter_on_strings :
-    skip:Base.t option -> (Base.t -> string -> int -> int -> unit) -> t -> unit
-
   (** [is_relationable loc] returns [true] iff [loc] represents a single
       memory location. *)
   val is_relationable: t -> bool
diff --git a/src/plugins/value/values/cvalue_forward.ml b/src/plugins/value/values/cvalue_forward.ml
index 6822e5f5a5c0218144b3776f9d489431f8c63b9e..91263cb385821059a3a9b72b50ad514a788a35f0 100644
--- a/src/plugins/value/values/cvalue_forward.ml
+++ b/src/plugins/value/values/cvalue_forward.ml
@@ -28,28 +28,54 @@ open Abstract_value
                                Comparison
    -------------------------------------------------------------------------- *)
 
+(* Representation of a pointer value to a literal string, with the string
+   itself, its length and the offsets pointed to in the string. *)
+type str = { string: string; offset: Ival.t; length: int; }
+
+(* Returns true if one string is a suffix of the other, and if there are offsets
+   pointing to the same point in this suffix. *)
+let may_be_suffix s1 s2 =
+  (* Requires [s1.length] <= [s2.length]. *)
+  let is_suffix s1 s2 =
+    let sub2 = String.sub s2.string (s2.length - s1.length) s1.length in
+    String.equal s1.string sub2 &&
+    let delta = Integer.of_int (s1.length - s2.length) in
+    let off2 = Ival.add_singleton_int delta s2.offset in
+    not (Ival.is_bottom (Ival.narrow s1.offset off2))
+  in
+  if s1.length <= s2.length then is_suffix s1 s2 else is_suffix s2 s1
+
+(* Returns true if [s1] may be shared with [s2] or a substring of [s2]. *)
+let rec may_be_shared_within s1 s2 =
+  may_be_suffix s1 s2 ||
+  match String.rindex_opt s2.string '\x00' with
+  | None -> false
+  | Some i ->
+    let s2 = { s2 with string = String.sub s2.string 0 i; length = i } in
+    may_be_shared_within s1 s2
+
+(* Returns true if [s1] and [s2] might point to the same literal string. *)
+let may_be_shared str1 off1 str2 off2 =
+  let s1 = { string = str1; offset = off1; length = String.length str1 }
+  and s2 = { string = str2; offset = off2; length = String.length str2 } in
+  may_be_shared_within s1 s2 || may_be_shared_within s2 s1
+
+(* Checks if all string bases of [v] satisfy [f]. *)
+let for_all_string v f =
+  Locations.Location_Bytes.for_all
+    (fun base off -> match base with
+       | Base.String (_, Base.CSString str) -> f base str off
+       | Base.String (_, Base.CSWstring _ ) -> false (* Not implemented yet *)
+       | _ -> true)
+    v
+
 (* Literal strings can only be compared if their contents are recognizably
    different (or the strings are physically the same). *)
-let are_comparable_string pointer1 pointer2 =
-  try
-    Locations.Location_Bytes.iter_on_strings ~skip:None
-      (fun base1 s1 offs1 len1 ->
-         Locations.Location_Bytes.iter_on_strings ~skip:(Some base1)
-           (fun _ s2 offs2 len2 ->
-              let delta = offs1 - offs2 in
-              let start = if delta <= 0 then -delta else 0
-              and max = min len2 (len1 - delta) in
-              let length = max - start + 1 in
-              let sub1 = String.sub s1 (start + delta) length
-              and sub2 = String.sub s2 start length in
-              if String.compare sub1 sub2 = 0
-              then raise Not_found)
-           pointer1)
-      pointer2;
-    true
-  with
-  | Not_found -> false
-  | Invalid_argument _s -> assert false
+let are_comparable_string v1 v2 =
+  for_all_string v1 (fun base1 str1 off1 ->
+      for_all_string v2 (fun base2 str2 off2 ->
+          Base.equal base1 base2 ||
+          not (may_be_shared str1 off1 str2 off2)))
 
 (* Under-approximation of the fact that a pointer is actually correct w.r.t.
    what can be created through pointer arithmetics. See C99 6.5.6 and 6.5.8