diff --git a/document/core/exec/instructions.rst b/document/core/exec/instructions.rst
index db73b5be..d2b33904 100644
--- a/document/core/exec/instructions.rst
+++ b/document/core/exec/instructions.rst
@@ -695,6 +695,9 @@ Memory Instructions
      (\iff n > 1) \\
    \end{array}
 
+.. note::
+   The use of the :math:`\vconst_t` meta function in the rules for this and the following instructions ensures that an overflowing index turns into a :ref:`trap <syntax-trap>`.
+
 
 .. _exec-memory.init:
 
@@ -936,12 +939,12 @@ Memory Instructions
      \end{array} \\
    \end{array}
    \\ \qquad
-     (\iff dst <= src \wedge cnt > 1)
+     (\iff dst \leq src \wedge cnt > 1)
    \\[1ex]
    \begin{array}{lcl@{\qquad}l}
    S; F; (\I32.\CONST~dst)~(\I32.\CONST~src)~(\I32.\CONST~cnt)~\MEMORYCOPY &\stepto& S; F;
      \begin{array}[t]{@{}l@{}}
-     (\I32.\CONST~(dst+cnt-1))~(\I32.\CONST~(src+cnt-1))~(\I32.\CONST~1)~\MEMORYCOPY \\
+     (\vconst_{\I32}(dst+cnt-1))~(\vconst_{\I32}(src+cnt-1))~(\I32.\CONST~1)~\MEMORYCOPY \\
      (\I32.\CONST~dst)~(\I32.\CONST~src)~(\I32.\CONST~(cnt-1))~\MEMORYCOPY \\
      \end{array} \\
    \end{array}
diff --git a/interpreter/exec/eval.ml b/interpreter/exec/eval.ml
index 5aaf42ad..48e923cd 100644
--- a/interpreter/exec/eval.ml
+++ b/interpreter/exec/eval.ml
@@ -117,6 +117,10 @@ let drop n (vs : 'a stack) at =
  *   c : config
  *)
 
+let const_i32_add i j at msg =
+  let k = I32.add i j in
+  if I32.lt_u k i then Trapping msg else Plain (Const (I32 k @@ at))
+
 let rec step (c : config) : config =
   let {frame; code = vs, es; _} = c in
   let e = List.hd es in
@@ -198,11 +202,13 @@ let rec step (c : config) : config =
         with Global.NotMutable -> Crash.error e.at "write to immutable global"
            | Global.Type -> Crash.error e.at "type mismatch at global write")
 
+      (* TODO: turn into small-step, but needs reference values *)
       | TableCopy, I32 n :: I32 s :: I32 d :: vs' ->
         let tab = table frame.inst (0l @@ e.at) in
         (try Table.copy tab d s n; vs', []
         with exn -> vs', [Trapping (table_error e.at exn) @@ e.at])
 
+      (* TODO: turn into small-step, but needs reference values *)
       | TableInit x, I32 n :: I32 s :: I32 d :: vs' ->
         let tab = table frame.inst (0l @@ e.at) in
         (match !(elem frame.inst x) with
@@ -253,30 +259,97 @@ let rec step (c : config) : config =
           with Memory.SizeOverflow | Memory.SizeLimit | Memory.OutOfMemory -> -1l
         in I32 result :: vs', []
 
-      | MemoryFill, I32 n :: I32 b :: I32 i :: vs' ->
-        let mem = memory frame.inst (0l @@ e.at) in
-        let addr = I64_convert.extend_i32_u i in
-        (try Memory.fill mem addr (Int32.to_int b) n; vs', []
-        with exn -> vs', [Trapping (memory_error e.at exn) @@ e.at])
+      | MemoryFill, I32 0l :: v :: I32 i :: vs' ->
+        vs', []
 
-      | MemoryCopy, I32 n :: I32 s :: I32 d :: vs' ->
-        let mem = memory frame.inst (0l @@ e.at) in
-        let dst = I64_convert.extend_i32_u d in
-        let src = I64_convert.extend_i32_u s in
-        (try Memory.copy mem dst src n; vs', []
-        with exn -> vs', [Trapping (memory_error e.at exn) @@ e.at])
+      | MemoryFill, I32 1l :: v :: I32 i :: vs' ->
+        vs', List.map (at e.at) [
+          Plain (Const (I32 i @@ e.at));
+          Plain (Const (v @@ e.at));
+          Plain (Store
+            {ty = I32Type; align = 0; offset = 0l; sz = Some Memory.Pack8});
+        ]
+
+      | MemoryFill, I32 n :: v :: I32 i :: vs' ->
+        vs', List.map (at e.at) [
+          Plain (Const (I32 i @@ e.at));
+          Plain (Const (v @@ e.at));
+          Plain (Const (I32 1l @@ e.at));
+          Plain (MemoryFill);
+          const_i32_add i 1l e.at (memory_error e.at Memory.Bounds);
+          Plain (Const (v @@ e.at));
+          Plain (Const (I32 (I32.sub n 1l) @@ e.at));
+          Plain (MemoryFill);
+        ]
+
+      | MemoryCopy, I32 0l :: I32 s :: I32 d :: vs' ->
+        vs', []
 
-      | MemoryInit x, I32 n :: I32 s :: I32 d :: vs' ->
-        let mem = memory frame.inst (0l @@ e.at) in
+      | MemoryCopy, I32 1l :: I32 s :: I32 d :: vs' ->
+        vs', List.map (at e.at) [
+          Plain (Const (I32 d @@ e.at));
+          Plain (Const (I32 s @@ e.at));
+          Plain (Load
+            {ty = I32Type; align = 0; offset = 0l; sz = Some Memory.(Pack8, ZX)});
+          Plain (Store
+            {ty = I32Type; align = 0; offset = 0l; sz = Some Memory.Pack8});
+        ]
+
+      | MemoryCopy, I32 n :: I32 s :: I32 d :: vs' when d <= s ->
+        vs', List.map (at e.at) [
+          Plain (Const (I32 d @@ e.at));
+          Plain (Const (I32 s @@ e.at));
+          Plain (Const (I32 1l @@ e.at));
+          Plain (MemoryCopy);
+          const_i32_add d 1l e.at (memory_error e.at Memory.Bounds);
+          const_i32_add s 1l e.at (memory_error e.at Memory.Bounds);
+          Plain (Const (I32 (I32.sub n 1l) @@ e.at));
+          Plain (MemoryCopy);
+        ]
+
+      | MemoryCopy, I32 n :: I32 s :: I32 d :: vs' when s < d ->
+        vs', List.map (at e.at) [
+          const_i32_add d (I32.sub n 1l) e.at (memory_error e.at Memory.Bounds);
+          const_i32_add s (I32.sub n 1l) e.at (memory_error e.at Memory.Bounds);
+          Plain (Const (I32 1l @@ e.at));
+          Plain (MemoryCopy);
+          Plain (Const (I32 d @@ e.at));
+          Plain (Const (I32 s @@ e.at));
+          Plain (Const (I32 (I32.sub n 1l) @@ e.at));
+          Plain (MemoryCopy);
+        ]
+
+      | MemoryInit x, I32 0l :: I32 s :: I32 d :: vs' ->
+        vs', []
+
+      | MemoryInit x, I32 1l :: I32 s :: I32 d :: vs' ->
         (match !(data frame.inst x) with
+        | None ->
+          vs', [Trapping "data segment dropped" @@ e.at]
+        | Some bs when Int32.to_int s >= String.length bs  ->
+          vs', [Trapping "out of bounds data segment access" @@ e.at]
         | Some bs ->
-          let dst = I64_convert.extend_i32_u d in
-          let src = I64_convert.extend_i32_u s in
-          (try Memory.init mem bs dst src n; vs', []
-          with exn -> vs', [Trapping (memory_error e.at exn) @@ e.at])
-        | None -> vs', [Trapping "data segment dropped" @@ e.at]
+          let b = Int32.of_int (Char.code bs.[Int32.to_int s]) in
+          vs', List.map (at e.at) [
+            Plain (Const (I32 d @@ e.at));
+            Plain (Const (I32 b @@ e.at));
+            Plain (
+              Store {ty = I32Type; align = 0; offset = 0l; sz = Some Memory.Pack8});
+          ]
         )
 
+      | MemoryInit x, I32 n :: I32 s :: I32 d :: vs' ->
+        vs', List.map (at e.at) [
+          Plain (Const (I32 d @@ e.at));
+          Plain (Const (I32 s @@ e.at));
+          Plain (Const (I32 1l @@ e.at));
+          Plain (MemoryInit x);
+          const_i32_add d 1l e.at (memory_error e.at Memory.Bounds);
+          const_i32_add s 1l e.at (memory_error e.at Memory.Bounds);
+          Plain (Const (I32 (I32.sub n 1l) @@ e.at));
+          Plain (MemoryInit x);
+        ]
+
       | DataDrop x, vs ->
         let seg = data frame.inst x in
         (match !seg with
diff --git a/interpreter/runtime/memory.ml b/interpreter/runtime/memory.ml
index 27a8e6a5..889aa707 100644
--- a/interpreter/runtime/memory.ml
+++ b/interpreter/runtime/memory.ml
@@ -144,34 +144,3 @@ let store_packed sz mem a o v =
     | I64 x -> x
     | _ -> raise Type
   in storen mem a o n x
-
-let init mem bs d s n =
-  let load_str_byte a =
-    try Char.code bs.[Int64.to_int a]
-    with _ -> raise Bounds
-  in let rec loop d s n =
-    if I32.gt_u n 0l then begin
-      store_byte mem d (load_str_byte s);
-      loop (Int64.add d 1L) (Int64.add s 1L) (Int32.sub n 1l)
-    end
-  in loop d s n
-
-let copy mem d s n =
-  let n' = I64_convert.extend_i32_u n in
-  let rec loop d s n dx =
-    if I32.gt_u n 0l then begin
-      store_byte mem d (load_byte mem s);
-      loop (Int64.add d dx) (Int64.add s dx) (Int32.sub n 1l) dx
-    end
-  in (if s < d then
-    loop Int64.(add d (sub n' 1L)) Int64.(add s (sub n' 1L)) n (-1L)
-  else
-    loop d s n 1L)
-
-let fill mem a v n =
-  let rec loop a n =
-    if I32.gt_u n 0l then begin
-      store_byte mem a v;
-      loop (Int64.add a 1L) (Int32.sub n 1l)
-    end
-  in loop a n
diff --git a/interpreter/runtime/memory.mli b/interpreter/runtime/memory.mli
index a2071441..0c42124a 100644
--- a/interpreter/runtime/memory.mli
+++ b/interpreter/runtime/memory.mli
@@ -43,8 +43,3 @@ val load_packed :
 val store_packed :
   pack_size -> memory -> address -> offset -> value -> unit
     (* raises Type, Bounds *)
-
-val init :
-  memory -> string -> address -> address -> count -> unit (* raises Bounds *)
-val copy : memory -> address -> address -> count -> unit (* raises Bounds *)
-val fill : memory -> address -> int -> count -> unit (* raises Bounds *)
diff --git a/interpreter/util/source.ml b/interpreter/util/source.ml
index 0115825c..f659f6f2 100644
--- a/interpreter/util/source.ml
+++ b/interpreter/util/source.ml
@@ -3,6 +3,7 @@ type region = {left : pos; right : pos}
 type 'a phrase = {at : region; it : 'a}
 
 let (@@) x region = {it = x; at = region}
+let at region x = x @@ region
 
 
 (* Positions and regions *)
diff --git a/interpreter/util/source.mli b/interpreter/util/source.mli
index 8cab3db8..240fe641 100644
--- a/interpreter/util/source.mli
+++ b/interpreter/util/source.mli
@@ -9,3 +9,4 @@ val string_of_pos : pos -> string
 val string_of_region : region -> string
 
 val (@@) : 'a -> region -> 'a phrase
+val at : region -> 'a -> 'a phrase
diff --git a/test/core/bulk.wast b/test/core/bulk.wast
index 98c4a49a..b2359d8f 100644
--- a/test/core/bulk.wast
+++ b/test/core/bulk.wast
@@ -148,24 +148,26 @@
 ;; data.drop
 (module
   (memory 1)
-  (data $p "")
-  (data $a 0 (i32.const 0) "")
+  (data $p "x")
+  (data $a 0 (i32.const 0) "x")
 
   (func (export "drop_passive") (data.drop $p))
-  (func (export "init_passive")
-    (memory.init $p (i32.const 0) (i32.const 0) (i32.const 0)))
+  (func (export "init_passive") (param $len i32)
+    (memory.init $p (i32.const 0) (i32.const 0) (local.get $len)))
 
   (func (export "drop_active") (data.drop $a))
-  (func (export "init_active")
-    (memory.init $a (i32.const 0) (i32.const 0) (i32.const 0)))
+  (func (export "init_active") (param $len i32)
+    (memory.init $a (i32.const 0) (i32.const 0) (local.get $len)))
 )
 
-(invoke "init_passive")
+(invoke "init_passive" (i32.const 1))
 (invoke "drop_passive")
 (assert_trap (invoke "drop_passive") "data segment dropped")
-(assert_trap (invoke "init_passive") "data segment dropped")
+(assert_return (invoke "init_passive" (i32.const 0)))
+(assert_trap (invoke "init_passive" (i32.const 1)) "data segment dropped")
 (assert_trap (invoke "drop_active") "data segment dropped")
-(assert_trap (invoke "init_active") "data segment dropped")
+(assert_return (invoke "init_active" (i32.const 0)))
+(assert_trap (invoke "init_active" (i32.const 1)) "data segment dropped")
 
 
 ;; table.init