diff --git a/src/plugins/wp/MemBytes.ml b/src/plugins/wp/MemBytes.ml
index 39bf08ddf99638abeb4bbe8e7bcb099eae66cf20..da54a47b5d7ac3621202044296020f96339bb34c 100644
--- a/src/plugins/wp/MemBytes.ml
+++ b/src/plugins/wp/MemBytes.ml
@@ -41,10 +41,17 @@ struct
     | Some l :: _ -> l
     | _ -> raise Not_found
   let l_havoc = Qed.Engine.F_call "havoc"
-  let f_havoc = Lang.extern_f ~library ~typecheck:ty_fst_arg ~link:l_havoc "havoc"
+  let f_havoc =
+    Lang.extern_f ~library ~typecheck:ty_fst_arg ~link:l_havoc "havoc"
   let havoc fresh current loc n =
     Lang.F.e_fun f_havoc [fresh;current;loc;n]
 
+  let l_memcpy = Qed.Engine.F_call "memcpy"
+  let f_memcpy =
+    Lang.extern_f ~library ~typecheck:ty_fst_arg ~link:l_memcpy "memcpy"
+  let memcpy mdst msrc ldst lsrc n =
+    Lang.F.e_fun f_memcpy [mdst;msrc;ldst;lsrc;n]
+
   let p_cinits = Lang.extern_fp ~coloring:true ~library "cinits"
   let cinits m = p_call p_cinits [m]
   let p_sconst = Lang.extern_fp ~coloring:true ~library "sconst"
@@ -673,6 +680,12 @@ module Model = struct
       Why3.havoc fresh current loc n
     else fresh
 
+  let memcpy obj ~lsrc ~ldst ~length chunk ~msrc ~mdst =
+    if chunk <> Chunk.Alloc then
+      let n = e_mul (e_int @@ sizeof_object obj) length in
+      Why3.memcpy mdst msrc ldst lsrc n
+    else msrc
+
   let eqmem_forall obj loc _chunk m1 m2 =
     let xp = Lang.freshvar ~basename:"p" MemAddr.t_addr in
     let p = e_var xp in
@@ -1138,3 +1151,4 @@ let is_init_range = Model.is_init_range
 let value_footprint = Model.value_footprint
 let init_footprint = Model.init_footprint
 let havoc = Model.havoc
+let memcpy = Model.memcpy
diff --git a/src/plugins/wp/MemLoader.ml b/src/plugins/wp/MemLoader.ml
index 55ed3be3ff08ee79d2f13eec8c10fba383b1bd48..b450512c3bf49b87a5c39a2c4aa715198a538f27 100644
--- a/src/plugins/wp/MemLoader.ml
+++ b/src/plugins/wp/MemLoader.ml
@@ -65,6 +65,8 @@ sig
 
   val havoc : c_object -> loc -> length:term ->
     Chunk.t -> fresh:term -> current:term -> term
+  val memcpy : c_object -> lsrc:loc -> ldst:loc -> length:term ->
+    Chunk.t -> msrc:term -> mdst:term -> term
 
   val eqmem_forall :
     c_object -> loc -> Chunk.t -> term -> term -> var list * pred * pred
@@ -576,11 +578,31 @@ struct
   let load_init = INIT_LOADER.load
   let () = INIT_LOAD_INFO.load_rec := load_init
 
+  (* -------------------------------------------------------------------------- *)
+  (* --- Memcpy                                                             --- *)
+  (* -------------------------------------------------------------------------- *)
+
+  (*let gen_memcpy_length get_domain s obj ~lsrc ~ldst length =
+    let ps = ref [] in
+    Domain.iter
+      (fun chunk ->
+        let pre  = Sigma.value s.pre  chunk in
+        let post = Sigma.value s.post chunk in
+        let tau = Chunk.tau_of_chunk chunk in
+        let basename = Chunk.basename_of_chunk chunk ^ "_undef" in
+        let mdst = F.e_var (Lang.freshvar ~basename tau) in
+        let havoc = M.memcpy obj ~ldst ~lsrc ~length chunk ~mdst ~msrc:pre in
+        ps := Set(post,havoc) :: !ps
+     ) (get_domain obj ldst) ; !ps
+  *)
   (* -------------------------------------------------------------------------- *)
   (* --- Havocs                                                             --- *)
   (* -------------------------------------------------------------------------- *)
 
   let gen_havoc_length get_domain s obj loc length =
+(*
+    gen_memcpy_length get_domain s obj ~ldst:loc ~lsrc:loc length
+*)
     let ps = ref [] in
     Domain.iter
       (fun chunk ->
diff --git a/src/plugins/wp/MemLoader.mli b/src/plugins/wp/MemLoader.mli
index 4af63f304bf109469c031dc32e7e45936b08ff39..3d56fa90c97503e8b64bd28b7993961d5865de02 100644
--- a/src/plugins/wp/MemLoader.mli
+++ b/src/plugins/wp/MemLoader.mli
@@ -61,6 +61,8 @@ sig
 
   val havoc : c_object -> loc -> length:term ->
     Chunk.t -> fresh:term -> current:term -> term
+  val memcpy : c_object -> lsrc:loc -> ldst:loc -> length:term ->
+    Chunk.t -> msrc:term -> mdst:term -> term
 
   val eqmem_forall :
     c_object -> loc -> Chunk.t -> term -> term -> var list * pred * pred
diff --git a/src/plugins/wp/MemMemory.ml b/src/plugins/wp/MemMemory.ml
index 3272f7f9dbdee6c37f63f20941b9d00df3269351..1882b4b4fe25e4eab020a1a0a8a449b054015553 100644
--- a/src/plugins/wp/MemMemory.ml
+++ b/src/plugins/wp/MemMemory.ml
@@ -36,10 +36,12 @@ let ty_fst_arg = function
   | _ -> raise Not_found
 
 let l_havoc = Qed.Engine.F_call "havoc"
+let l_memcpy = Qed.Engine.F_call "memcpy"
 let l_set_init = Qed.Engine.F_call "set_init"
 
 let p_eqmem = Lang.extern_fp ~library "eqmem"
 let f_havoc = Lang.extern_f ~library ~typecheck:ty_fst_arg ~link:l_havoc "havoc"
+let f_memcpy = Lang.extern_f ~library ~typecheck:ty_fst_arg ~link:l_memcpy "memcpy"
 let p_framed = Lang.extern_fp ~coloring:true ~library "framed" (* m-pointer -> prop *)
 let p_sconst = Lang.extern_fp ~coloring:true ~library "sconst" (* int-memory -> prop *)
 let f_set_init =
diff --git a/src/plugins/wp/MemMemory.mli b/src/plugins/wp/MemMemory.mli
index b3a51ba5741471d92e9f22c9ec85a0dce60ff1e4..3e5cb9ad732bcead4b17d951a67e027bd4783ca2 100644
--- a/src/plugins/wp/MemMemory.mli
+++ b/src/plugins/wp/MemMemory.mli
@@ -36,6 +36,7 @@ val t_init : tau (** initialization tables *)
 val t_mem : tau -> tau (** t_addr indexed array *)
 
 val f_havoc : lfun
+val f_memcpy : lfun
 val f_set_init : lfun
 
 val p_is_init_r : lfun
diff --git a/src/plugins/wp/MemRegion.ml b/src/plugins/wp/MemRegion.ml
index 9f19ab4a33b1115f8392506b381534179d5d803d..3d33335c0a76ca32c2ea0fc87147b3986ad1631f 100644
--- a/src/plugins/wp/MemRegion.ml
+++ b/src/plugins/wp/MemRegion.ml
@@ -83,6 +83,8 @@ module type ModelWithLoader = sig
   val frames : c_object -> loc -> chunk -> frame list
 
   val havoc : c_object -> loc -> length:term -> chunk -> fresh:term -> current:term -> term
+  val memcpy : c_object -> lsrc:loc -> ldst:loc -> length:term ->
+    chunk -> msrc:term -> mdst:term -> term
 
   val eqmem_forall : c_object -> loc -> chunk -> term -> term -> var list * pred * pred
 
@@ -369,6 +371,16 @@ struct
         | Value _ | ValInit -> fresh
         | Array _ | ArrInit -> e_fun f_havoc [fresh;current;to_addr l;length]
 
+    let memcpy ty ~lsrc ~ldst ~length chunk ~msrc ~mdst =
+      match chunk with
+      | M c ->
+        M.memcpy ty ~lsrc:(loc lsrc) ~ldst:(loc ldst) ~length c ~msrc ~mdst
+      | R c ->
+        match c.mu with
+        | Value _ | ValInit -> msrc
+        | Array _ | ArrInit ->
+          e_fun f_memcpy [mdst;msrc;to_addr ldst;to_addr lsrc;length]
+
     let eqmem_forall ty l chunk m1 m2 =
       match chunk with
       | M c -> M.eqmem_forall ty (loc l) c m1 m2
diff --git a/src/plugins/wp/MemRegion.mli b/src/plugins/wp/MemRegion.mli
index dc3ce4a0e103ed40d6f0b948b4aa5640063938cf..e35fedf766bd58b0a0a1ed41a8591d7a8336560b 100644
--- a/src/plugins/wp/MemRegion.mli
+++ b/src/plugins/wp/MemRegion.mli
@@ -59,7 +59,10 @@ sig
   val last : sigma -> c_object -> loc -> term
   val frames : c_object -> loc -> chunk -> frame list
 
-  val havoc : c_object -> loc -> length:term -> chunk -> fresh:term -> current:term -> term
+  val havoc : c_object -> loc -> length:term ->
+    chunk -> fresh:term -> current:term -> term
+  val memcpy : c_object -> lsrc:loc -> ldst:loc -> length:term ->
+    chunk -> msrc:term -> mdst:term -> term
 
   val eqmem_forall : c_object -> loc -> chunk -> term -> term -> var list * pred * pred
 
diff --git a/src/plugins/wp/MemTyped.ml b/src/plugins/wp/MemTyped.ml
index 22fbe398905c310bffba7c979501d39b3e37d7e2..146cce9b681a6c97418c2f12008485db207ea521 100644
--- a/src/plugins/wp/MemTyped.ml
+++ b/src/plugins/wp/MemTyped.ml
@@ -1048,6 +1048,12 @@ struct
       F.e_fun f_havoc [fresh;current;loc;n]
     else fresh
 
+  let memcpy obj ~lsrc ~ldst ~length chunk ~msrc ~mdst =
+    if chunk <> T_alloc then
+      let n = F.e_mul (length_of_object obj) length in
+      F.e_fun f_memcpy [mdst;msrc;ldst;lsrc;n]
+    else msrc
+
   let eqmem_forall obj loc _chunk m1 m2 =
     let xp = Lang.freshvar ~basename:"p" MemAddr.t_addr in
     let p = F.e_var xp in
diff --git a/src/plugins/wp/share/why3/frama_c_wp/memory.mlw b/src/plugins/wp/share/why3/frama_c_wp/memory.mlw
index a5ffa059552627285f167d1c8c9a6425c4edbdb1..f0baf1c96d4bdf638bfeacbae3e9c0ffd1adf0a3 100644
--- a/src/plugins/wp/share/why3/frama_c_wp/memory.mlw
+++ b/src/plugins/wp/share/why3/frama_c_wp/memory.mlw
@@ -36,7 +36,16 @@ theory Memory
   predicate eqmem (m1 m2 : map addr 'a) (p:addr) (a:int) =
     forall q:addr [m1[p]|m2[q]]. included q 1 p a -> m1[q] = m2[q]
 
-  function havoc (m0 m1 : map addr 'a) (p:addr) (a:int) : map addr 'a
+
+  (* copies the [s] bytes from [m1].[b1] into [m0].[m0] *)
+  function memcpy (m0 m1: map addr 'a) (b0 b1:addr) (s:int) : map addr 'a =
+    fun (p:addr) ->
+    if separated p 1 b0 s then m0 p else
+    let offset = b1.offset - b0.offset + p.offset in
+    m1 { b1 with offset = offset }
+
+  function havoc (m0 m1 : map addr 'a) (p:addr) (a:int) : map addr 'a =
+    memcpy m1 m0 p p a
 
   lemma eqmem_included :
     forall m1 m2 : map addr 'a.
@@ -51,7 +60,7 @@ theory Memory
     forall a : int.
     eqmem m1 m2 p a -> eqmem m2 m1 p a
 
-  axiom havoc_access :
+  lemma havoc_access :
     forall m0 m1 : map addr 'a.
     forall q p : addr.
     forall a : int.