diff --git a/document/core/exec/instructions.rst b/document/core/exec/instructions.rst index 8dc36e76..eecf0f8d 100644 --- a/document/core/exec/instructions.rst +++ b/document/core/exec/instructions.rst @@ -728,7 +728,7 @@ Memory Instructions 8. Assert: due to :ref:`validation `, :math:`S.\SDATA[\X{da}]` exists. -9. Let :math:`\X{data}^?` be the optional :ref:`data instance ` :math:`S.\SDATA[\X{da}]`. +9. Let :math:`\X{data}` be the :ref:`data instance ` :math:`S.\SDATA[\X{da}]`. 10. Assert: due to :ref:`validation `, a value of :ref:`value type ` |I32| is on the top of the stack. @@ -746,11 +746,7 @@ Memory Instructions a. Return. -17. If :math:`\X{data}^? = \epsilon`, then: - - a. Trap. - -18. If :math:`cnt = 1`, then: +17. If :math:`cnt = 1`, then: a. Push the value :math:`\I32.\CONST~dst` to the stack. @@ -766,21 +762,21 @@ Memory Instructions f. Return. -19. Push the value :math:`\I32.\CONST~dst` to the stack. +18. Push the value :math:`\I32.\CONST~dst` to the stack. -20. Push the value :math:`\I32.\CONST~src` to the stack. +19. Push the value :math:`\I32.\CONST~src` to the stack. -21. Push the value :math:`\I32.\CONST~1` to the stack. +20. Push the value :math:`\I32.\CONST~1` to the stack. -22. Execute the instruction :math:`\MEMORYINIT~x`. +21. Execute the instruction :math:`\MEMORYINIT~x`. -23. Push the value :math:`\vconst_{\I32}(dst+1)` to the stack. +22. Push the value :math:`\vconst_{\I32}(dst+1)` to the stack. -24. Push the value :math:`\vconst_{\I32}(src+1)` to the stack. +23. Push the value :math:`\vconst_{\I32}(src+1)` to the stack. -25. Push the value :math:`\I32.\CONST~(cnt-1)` to the stack. +24. Push the value :math:`\I32.\CONST~(cnt-1)` to the stack. -26. Execute the instruction :math:`\MEMORYINIT~x`. +25. Execute the instruction :math:`\MEMORYINIT~x`. .. math:: ~\\[-1ex] @@ -830,13 +826,7 @@ Memory Instructions 4. Assert: due to :ref:`validation `, :math:`S.\SDATA[a]` exists. -5. Let :math:`\X{data}^?` be the optional :ref:`data instance ` :math:`S.\SDATA[a]`. - -6. If :math:`\X{data}^? = \epsilon`, then: - - a. Trap. - -7. Replace :math:`S.\SDATA[a]` with :math:`\epsilon`. +5. Replace :math:`S.\SDATA[a]` with the :ref:`data instance ` :math:`\{\DIINIT~\epsilon\}`. .. math:: ~\\[-1ex] @@ -845,16 +835,7 @@ Memory Instructions S; F; (\DATADROP~x) &\stepto& S'; F; \epsilon \end{array} \\ \qquad - \begin{array}[t]{@{}r@{~}l@{}} - (\iff & S.\SDATA[F.\AMODULE.\MIDATAS[x]] \ne \epsilon \\ - \wedge & S' = S \with \SDATA[F.\AMODULE.\MIDATAS[x]] = \epsilon) \\ - \end{array} - \\[1ex] - \begin{array}{lcl@{\qquad}l} - S; F; (\DATADROP~x) &\stepto& S; F; \TRAP - \end{array} - \\ \qquad - (\otherwise) + (\iff S' = S \with \SDATA[F.\AMODULE.\MIDATAS[x]] = \{ \DIINIT~\epsilon \}) \\ \end{array} @@ -1089,7 +1070,7 @@ Table Instructions 8. Assert: due to :ref:`validation `, :math:`S.\SELEM[\X{ea}]` exists. -9. Let :math:`\X{elem}^?` be the optional :ref:`element instance ` :math:`S.\SELEM[\X{ea}]`. +9. Let :math:`\X{elem}` be the :ref:`element instance ` :math:`S.\SELEM[\X{ea}]`. 10. Assert: due to :ref:`validation `, a value of :ref:`value type ` |I32| is on the top of the stack. @@ -1107,11 +1088,7 @@ Table Instructions a. Return. -17. If :math:`\X{elem}^? = \epsilon`, then: - - a. Trap. - -18. If :math:`cnt = 1`, then: +17. If :math:`cnt = 1`, then: a. Push the value :math:`\I32.\CONST~dst` to the stack. @@ -1127,21 +1104,21 @@ Table Instructions f. Return. -19. Push the value :math:`\I32.\CONST~dst` to the stack. +18. Push the value :math:`\I32.\CONST~dst` to the stack. -20. Push the value :math:`\I32.\CONST~src` to the stack. +19. Push the value :math:`\I32.\CONST~src` to the stack. -21. Push the value :math:`\I32.\CONST~1` to the stack. +20. Push the value :math:`\I32.\CONST~1` to the stack. -22. Execute the instruction :math:`\TABLEINIT~x`. +21. Execute the instruction :math:`\TABLEINIT~x`. -23. Push the value :math:`\vconst_{\I32}(dst+1)` to the stack. +22. Push the value :math:`\vconst_{\I32}(dst+1)` to the stack. -24. Push the value :math:`\vconst_{\I32}(src+1)` to the stack. +23. Push the value :math:`\vconst_{\I32}(src+1)` to the stack. -25. Push the value :math:`\I32.\CONST~(cnt-1)` to the stack. +24. Push the value :math:`\I32.\CONST~(cnt-1)` to the stack. -26. Execute the instruction :math:`\TABLEINIT~x`. +25. Execute the instruction :math:`\TABLEINIT~x`. .. math:: ~\\[-1ex] @@ -1191,13 +1168,7 @@ Table Instructions 4. Assert: due to :ref:`validation `, :math:`S.\SELEM[a]` exists. -5. Let :math:`\X{elem}^?` be the optional :ref:`elem instance ` :math:`S.\SELEM[a]`. - -6. If :math:`\X{elem}^? = \epsilon`, then: - - a. Trap. - -7. Replace :math:`S.\SELEM[a]` with :math:`\epsilon`. +5. Replace :math:`S.\SELEM[a]` with the :ref:`element instance ` :math:`\{\EIINIT~\epsilon\}`. .. math:: ~\\[-1ex] @@ -1206,16 +1177,7 @@ Table Instructions S; F; (\ELEMDROP~x) &\stepto& S'; F; \epsilon \end{array} \\ \qquad - \begin{array}[t]{@{}r@{~}l@{}} - (\iff & S.\SELEM[F.\AMODULE.\MIELEMS[x]] \ne \epsilon \\ - \wedge & S' = S \with \SELEM[F.\AMODULE.\MIELEMS[x]] = \epsilon) \\ - \end{array} - \\[1ex] - \begin{array}{lcl@{\qquad}l} - S; F; (\ELEMDROP~x) &\stepto& S; F; \TRAP - \end{array} - \\ \qquad - (\otherwise) + (\iff S' = S \with \SELEM[F.\AMODULE.\MIELEMS[x]] = \{ \EIINIT~\epsilon \}) \\ \end{array} diff --git a/document/core/exec/runtime.rst b/document/core/exec/runtime.rst index 06ab4ecc..58896560 100644 --- a/document/core/exec/runtime.rst +++ b/document/core/exec/runtime.rst @@ -75,7 +75,6 @@ Store The *store* represents all global state that can be manipulated by WebAssembly programs. It consists of the runtime representation of all *instances* of :ref:`functions `, :ref:`tables `, :ref:`memories `, and :ref:`globals `, :ref:`element segments `, and :ref:`data segments ` that have been :ref:`allocated ` during the life time of the abstract machine. [#gc]_ -Element and data segments can be dropped by the owning module, in which case the respective instances are replaced with :math:`\epsilon`. It is an invariant of the semantics that no element or data instance is :ref:`addressed ` from anywhere else but the owning module instances. Syntactically, the store is defined as a :ref:`record ` listing the existing instances of each category: @@ -88,8 +87,8 @@ Syntactically, the store is defined as a :ref:`record ` listing \STABLES & \tableinst^\ast, \\ \SMEMS & \meminst^\ast, \\ \SGLOBALS & \globalinst^\ast, \\ - \SELEM & (\eleminst^?)^\ast, \\ - \SDATA & (\datainst^?)^\ast ~\} \\ + \SELEM & \eleminst^\ast, \\ + \SDATA & \datainst^\ast ~\} \\ \end{array} \end{array} diff --git a/interpreter/exec/eval.ml b/interpreter/exec/eval.ml index b64ce7f7..fcc88b40 100644 --- a/interpreter/exec/eval.ml +++ b/interpreter/exec/eval.ml @@ -122,22 +122,16 @@ let mem_oob frame x i n = (Memory.bound (memory frame.inst x)) let data_oob frame x i n = - match !(data frame.inst x) with - | None -> false - | Some bs -> - I64.gt_u (I64.add (I64_convert.extend_i32_u i) (I64_convert.extend_i32_u n)) - (I64.of_int_u (String.length bs)) + I64.gt_u (I64.add (I64_convert.extend_i32_u i) (I64_convert.extend_i32_u n)) + (I64.of_int_u (String.length !(data frame.inst x))) let table_oob frame x i n = I64.gt_u (I64.add (I64_convert.extend_i32_u i) (I64_convert.extend_i32_u n)) (I64_convert.extend_i32_u (Table.size (table frame.inst x))) let elem_oob frame x i n = - match !(elem frame.inst x) with - | None -> false - | Some es -> - I64.gt_u (I64.add (I64_convert.extend_i32_u i) (I64_convert.extend_i32_u n)) - (I64.of_int_u (List.length es)) + I64.gt_u (I64.add (I64_convert.extend_i32_u i) (I64_convert.extend_i32_u n)) + (I64.of_int_u (List.length !(elem frame.inst x))) let rec step (c : config) : config = let {frame; code = vs, es; _} = c in @@ -220,42 +214,37 @@ 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") - | TableCopy, I32 0l :: I32 s :: I32 d :: vs' -> - vs', [] - | TableCopy, I32 n :: I32 s :: I32 d :: vs' when table_oob frame (0l @@ e.at) s n || table_oob frame (0l @@ e.at) d n -> vs', [Trapping (table_error e.at Table.Bounds) @@ e.at] + | TableCopy, I32 0l :: I32 s :: I32 d :: vs' -> + vs', [] + (* 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]) - | TableInit x, I32 0l :: I32 s :: I32 d :: vs' -> - vs', [] - | TableInit x, I32 n :: I32 s :: I32 d :: vs' when table_oob frame (0l @@ e.at) d n || elem_oob frame x s n -> vs', [Trapping (table_error e.at Table.Bounds) @@ e.at] + | TableInit x, I32 0l :: I32 s :: I32 d :: vs' -> + vs', [] + (* 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 - | Some es -> - (try Table.init tab es d s n; vs', [] - with exn -> vs', [Trapping (table_error e.at exn) @@ e.at]) - | None -> vs', [Trapping "element segment dropped" @@ e.at] - ) + let seg = !(elem frame.inst x) in + (try Table.init tab seg d s n; vs', [] + with exn -> vs', [Trapping (table_error e.at exn) @@ e.at]) | ElemDrop x, vs -> let seg = elem frame.inst x in - (match !seg with - | Some _ -> seg := None; vs, [] - | None -> vs, [Trapping "element segment dropped" @@ e.at] - ) + seg := []; + vs, [] | Load {offset; ty; sz; _}, I32 i :: vs' -> let mem = memory frame.inst (0l @@ e.at) in @@ -291,13 +280,13 @@ let rec step (c : config) : config = with Memory.SizeOverflow | Memory.SizeLimit | Memory.OutOfMemory -> -1l in I32 result :: vs', [] - | MemoryFill, I32 0l :: v :: I32 i :: vs' -> - vs', [] - | MemoryFill, I32 n :: v :: I32 i :: vs' when mem_oob frame (0l @@ e.at) i n -> vs', [Trapping (memory_error e.at Memory.Bounds) @@ e.at] + | MemoryFill, I32 0l :: v :: I32 i :: vs' -> + vs', [] + | MemoryFill, I32 n :: v :: I32 i :: vs' -> vs', List.map (at e.at) [ Plain (Const (I32 i @@ e.at)); @@ -310,13 +299,13 @@ let rec step (c : config) : config = Plain (MemoryFill); ] - | MemoryCopy, I32 0l :: I32 s :: I32 d :: vs' -> - vs', [] - | MemoryCopy, I32 n :: I32 s :: I32 d :: vs' when mem_oob frame (0l @@ e.at) s n || mem_oob frame (0l @@ e.at) d n -> vs', [Trapping (memory_error e.at Memory.Bounds) @@ e.at] + | MemoryCopy, I32 0l :: I32 s :: I32 d :: vs' -> + vs', [] + | MemoryCopy, I32 n :: I32 s :: I32 d :: vs' when d <= s -> vs', List.map (at e.at) [ Plain (Const (I32 d @@ e.at)); @@ -345,37 +334,31 @@ let rec step (c : config) : config = {ty = I32Type; align = 0; offset = 0l; sz = Some Memory.Pack8}); ] - | MemoryInit x, I32 0l :: I32 s :: I32 d :: vs' -> - vs', [] - | MemoryInit x, I32 n :: I32 s :: I32 d :: vs' when mem_oob frame (0l @@ e.at) d n || data_oob frame x s n -> vs', [Trapping (memory_error e.at Memory.Bounds) @@ e.at] + | MemoryInit x, I32 0l :: I32 s :: I32 d :: vs' -> + vs', [] + | MemoryInit x, I32 n :: I32 s :: I32 d :: vs' -> - (match !(data frame.inst x) with - | None -> - vs', [Trapping "data segment dropped" @@ e.at] - | Some bs -> - 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}); - Plain (Const (I32 (I32.add d 1l) @@ e.at)); - Plain (Const (I32 (I32.add s 1l) @@ e.at)); - Plain (Const (I32 (I32.sub n 1l) @@ e.at)); - Plain (MemoryInit x); - ] - ) + let seg = !(data frame.inst x) in + let b = Int32.of_int (Char.code seg.[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}); + Plain (Const (I32 (I32.add d 1l) @@ e.at)); + Plain (Const (I32 (I32.add s 1l) @@ e.at)); + 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 - | Some _ -> seg := None; vs, [] - | None -> vs, [Trapping "data segment dropped" @@ e.at] - ) + seg := ""; + vs, [] | Const v, vs -> v.it :: vs, [] @@ -536,11 +519,11 @@ let elem_list inst init = let create_elem (inst : module_inst) (seg : elem_segment) : elem_inst = let {etype; einit; _} = seg.it in - ref (Some (elem_list inst einit)) + ref (elem_list inst einit) let create_data (inst : module_inst) (seg : data_segment) : data_inst = let {dinit; _} = seg.it in - ref (Some dinit) + ref dinit let add_import (m : module_) (ext : extern) (im : import) (inst : module_inst) diff --git a/interpreter/runtime/instance.ml b/interpreter/runtime/instance.ml index de0e8141..a58859ac 100644 --- a/interpreter/runtime/instance.ml +++ b/interpreter/runtime/instance.ml @@ -17,8 +17,8 @@ and table_inst = Table.t and memory_inst = Memory.t and global_inst = Global.t and export_inst = Ast.name * extern -and elem_inst = Table.elem list option ref -and data_inst = string option ref +and elem_inst = Table.elem list ref +and data_inst = string ref and extern = | ExternFunc of func_inst diff --git a/proposals/bulk-memory-operations/Overview.md b/proposals/bulk-memory-operations/Overview.md index 69fb55ff..a7435045 100644 --- a/proposals/bulk-memory-operations/Overview.md +++ b/proposals/bulk-memory-operations/Overview.md @@ -273,10 +273,9 @@ The instruction has the signature `[i32 i32 i32] -> []`. The parameters are, in It is a validation error to use `memory.init` with an out-of-bounds segment index. A trap occurs if: -* the segment is used after it has been dropped via `data.drop`. This includes - active segments that were dropped after being copied into memory during module - instantiation. -* any of the accessed bytes lies outside the source data segment or the target memory +* the source offset plus size is greater than the length of the source data segment; + this includes the case that the segment has been dropped via `data.drop` +* the destination offset plus size is greater than the length of the target memory Note that it is allowed to use `memory.init` on the same data segment more than once. @@ -337,7 +336,8 @@ The instruction has the signature `[i32 i32 i32] -> []`. The parameters are, in - top-0: size of memory region in bytes A trap occurs if: -* any of the accessed bytes lies outside the source or target memory +* the source offset plus size is greater than the length of the source memory +* the destination offset plus size is greater than the length of the target memory A trap resulting from an access outside the source or target region only occurs once the first byte that is outside the source or target @@ -360,7 +360,7 @@ The instruction has the signature `[i32 i32 i32] -> []`. The parameters are, in - top-0: size of memory region in bytes A trap occurs if: -* any of the accessed bytes lies outside the target memory +* the destination offset plus size is greater than the length of the target memory Filling takes place bytewise from lower addresses toward higher addresses. A trap resulting from an access outside the target memory diff --git a/test/core/bulk.wast b/test/core/bulk.wast index 9e26519e..fe71939d 100644 --- a/test/core/bulk.wast +++ b/test/core/bulk.wast @@ -48,8 +48,9 @@ ;; Succeed when writing 0 bytes at the end of the region. (invoke "fill" (i32.const 0x10000) (i32.const 0) (i32.const 0)) -;; OK to write 0 bytes outside of memory. -(invoke "fill" (i32.const 0x10001) (i32.const 0) (i32.const 0)) +;; Writing 0 bytes outside the memory traps. +(assert_trap (invoke "fill" (i32.const 0x10001) (i32.const 0) (i32.const 0)) + "out of bounds memory access") ;; memory.copy @@ -103,9 +104,11 @@ (invoke "copy" (i32.const 0x10000) (i32.const 0) (i32.const 0)) (invoke "copy" (i32.const 0) (i32.const 0x10000) (i32.const 0)) -;; OK to copy 0 bytes outside of memory. -(invoke "copy" (i32.const 0x10001) (i32.const 0) (i32.const 0)) -(invoke "copy" (i32.const 0) (i32.const 0x10001) (i32.const 0)) +;; Copying 0 bytes outside the memory traps. +(assert_trap (invoke "copy" (i32.const 0x10001) (i32.const 0) (i32.const 0)) + "out of bounds memory access") +(assert_trap (invoke "copy" (i32.const 0) (i32.const 0x10001) (i32.const 0)) + "out of bounds memory access") ;; memory.init @@ -141,9 +144,11 @@ (invoke "init" (i32.const 0x10000) (i32.const 0) (i32.const 0)) (invoke "init" (i32.const 0) (i32.const 4) (i32.const 0)) -;; OK to write 0 bytes outside of memory or segment. -(invoke "init" (i32.const 0x10001) (i32.const 0) (i32.const 0)) -(invoke "init" (i32.const 0) (i32.const 5) (i32.const 0)) +;; Writing 0 bytes outside the memory traps. +(assert_trap (invoke "init" (i32.const 0x10001) (i32.const 0) (i32.const 0)) + "out of bounds memory access") +(assert_trap (invoke "init" (i32.const 0) (i32.const 5) (i32.const 0)) + "out of bounds memory access") ;; data.drop (module @@ -162,12 +167,14 @@ (invoke "init_passive" (i32.const 1)) (invoke "drop_passive") -(assert_trap (invoke "drop_passive") "data segment dropped") +(invoke "drop_passive") (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_passive" (i32.const 1)) "out of bounds") +(invoke "init_passive" (i32.const 0)) +(invoke "drop_active") (assert_return (invoke "init_active" (i32.const 0))) -(assert_trap (invoke "init_active" (i32.const 1)) "data segment dropped") +(assert_trap (invoke "init_active" (i32.const 1)) "out of bounds") +(invoke "init_active" (i32.const 0)) ;; table.init @@ -208,9 +215,11 @@ (invoke "init" (i32.const 3) (i32.const 0) (i32.const 0)) (invoke "init" (i32.const 0) (i32.const 4) (i32.const 0)) -;; OK to storing 0 elements outside of table or segment. -(invoke "init" (i32.const 4) (i32.const 0) (i32.const 0)) -(invoke "init" (i32.const 0) (i32.const 5) (i32.const 0)) +;; Writing 0 elements outside the table traps. +(assert_trap (invoke "init" (i32.const 4) (i32.const 0) (i32.const 0)) + "out of bounds table access") +(assert_trap (invoke "init" (i32.const 0) (i32.const 5) (i32.const 0)) + "out of bounds table access") ;; elem.drop @@ -233,12 +242,14 @@ (invoke "init_passive" (i32.const 1)) (invoke "drop_passive") -(assert_trap (invoke "drop_passive") "element segment dropped") +(invoke "drop_passive") (assert_return (invoke "init_passive" (i32.const 0))) -(assert_trap (invoke "init_passive" (i32.const 1)) "element segment dropped") -(assert_trap (invoke "drop_active") "element segment dropped") +(assert_trap (invoke "init_passive" (i32.const 1)) "out of bounds") +(invoke "init_passive" (i32.const 0)) +(invoke "drop_active") (assert_return (invoke "init_active" (i32.const 0))) -(assert_trap (invoke "init_active" (i32.const 1)) "element segment dropped") +(assert_trap (invoke "init_active" (i32.const 1)) "out of bounds") +(invoke "init_active" (i32.const 0)) ;; table.copy @@ -290,5 +301,7 @@ (invoke "copy" (i32.const 0) (i32.const 10) (i32.const 0)) ;; Fail on out-of-bounds when copying 0 elements outside of table. -(invoke "copy" (i32.const 11) (i32.const 0) (i32.const 0)) -(invoke "copy" (i32.const 0) (i32.const 11) (i32.const 0)) +(assert_trap (invoke "copy" (i32.const 11) (i32.const 0) (i32.const 0)) + "out of bounds") +(assert_trap (invoke "copy" (i32.const 0) (i32.const 11) (i32.const 0)) + "out of bounds") diff --git a/test/core/data.wast b/test/core/data.wast index a343fdc5..aabe1021 100644 --- a/test/core/data.wast +++ b/test/core/data.wast @@ -193,16 +193,19 @@ ) "out of bounds" ) - -;; Writing 0 bytes outside of bounds is allowed now. -(module - (memory 0) - (data (i32.const 1)) +(assert_trap + (module + (memory 0) + (data (i32.const 1)) + ) + "out of bounds" ) - -(module - (memory 0 1) - (data (i32.const 1)) +(assert_trap + (module + (memory 0 1) + (data (i32.const 1)) + ) + "out of bounds" ) ;; This seems to cause a time-out on Travis. diff --git a/test/core/elem.wast b/test/core/elem.wast index 90647a4a..bb622ee6 100644 --- a/test/core/elem.wast +++ b/test/core/elem.wast @@ -219,12 +219,13 @@ "out of bounds" ) -;; Writing 0 elems outside of bounds is allowed now. -(module - (table 0 funcref) - (elem (i32.const 1)) +(assert_trap + (module + (table 0 funcref) + (elem (i32.const 1)) + ) + "out of bounds" ) - (assert_trap (module (table 10 funcref) diff --git a/test/core/memory_copy.wast b/test/core/memory_copy.wast index cb46144d..692e1ad8 100644 --- a/test/core/memory_copy.wast +++ b/test/core/memory_copy.wast @@ -4869,7 +4869,7 @@ (memory 1 1) (func (export "test") (memory.copy (i32.const 0x20000) (i32.const 0x7000) (i32.const 0)))) -(invoke "test") +(assert_trap (invoke "test") "out of bounds") (module (memory 1 1) @@ -4881,7 +4881,7 @@ (memory 1 1) (func (export "test") (memory.copy (i32.const 0x9000) (i32.const 0x20000) (i32.const 0)))) -(invoke "test") +(assert_trap (invoke "test") "out of bounds") (module (memory 1 1) @@ -4889,6 +4889,12 @@ (memory.copy (i32.const 0x10000) (i32.const 0x10000) (i32.const 0)))) (invoke "test") +(module + (memory 1 1) + (func (export "test") + (memory.copy (i32.const 0x20000) (i32.const 0x20000) (i32.const 0)))) +(assert_trap (invoke "test") "out of bounds") + (module (memory 1 1) (func (export "test") diff --git a/test/core/memory_fill.wast b/test/core/memory_fill.wast index bbce9d8e..caca80b5 100644 --- a/test/core/memory_fill.wast +++ b/test/core/memory_fill.wast @@ -114,7 +114,7 @@ (func (export "test") (memory.fill (i32.const 0x20000) (i32.const 0x55) (i32.const 0)))) -(invoke "test") +(assert_trap (invoke "test") "out of bounds") (module (memory 1 1) diff --git a/test/core/memory_init.wast b/test/core/memory_init.wast index 670bddc2..c647079d 100644 --- a/test/core/memory_init.wast +++ b/test/core/memory_init.wast @@ -205,7 +205,7 @@ (func (export "test") (data.drop 0) (data.drop 0))) -(assert_trap (invoke "test") "data segment dropped") +(invoke "test") (module (memory 1) @@ -213,14 +213,14 @@ (func (export "test") (data.drop 0) (memory.init 0 (i32.const 1234) (i32.const 1) (i32.const 1)))) -(assert_trap (invoke "test") "data segment dropped") +(assert_trap (invoke "test") "out of bounds") (module (memory 1) (data (i32.const 0) "\37") (func (export "test") (memory.init 0 (i32.const 1234) (i32.const 1) (i32.const 1)))) -(assert_trap (invoke "test") "data segment dropped") +(assert_trap (invoke "test") "out of bounds") (assert_invalid (module @@ -270,7 +270,7 @@ (data "\37") (func (export "test") (memory.init 0 (i32.const 1234) (i32.const 4) (i32.const 0)))) -(invoke "test") +(assert_trap (invoke "test") "out of bounds") (module (memory 1) @@ -284,7 +284,7 @@ (data "\37") (func (export "test") (memory.init 0 (i32.const 0x10001) (i32.const 0) (i32.const 0)))) -(invoke "test") +(assert_trap (invoke "test") "out of bounds") (module (memory 1) @@ -300,6 +300,13 @@ (memory.init 0 (i32.const 0x10000) (i32.const 1) (i32.const 0)))) (invoke "test") +(module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 0x10001) (i32.const 4) (i32.const 0)))) +(assert_trap (invoke "test") "out of bounds") + (assert_invalid (module (memory 1) diff --git a/test/core/table_copy.wast b/test/core/table_copy.wast index 20949b30..d0aa77b2 100644 --- a/test/core/table_copy.wast +++ b/test/core/table_copy.wast @@ -633,7 +633,7 @@ (table.copy (i32.const 31) (i32.const 15) (i32.const 0)) )) -(invoke "test") +(assert_trap (invoke "test") "out of bounds") (module (table 30 30 funcref) @@ -681,7 +681,7 @@ (table.copy (i32.const 15) (i32.const 31) (i32.const 0)) )) -(invoke "test") +(assert_trap (invoke "test") "out of bounds") (module (table 30 30 funcref) @@ -707,6 +707,30 @@ (invoke "test") +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy (i32.const 31) (i32.const 31) (i32.const 0)) + )) + +(assert_trap (invoke "test") "out of bounds") + (module (type (func (result i32))) (table 32 64 funcref) diff --git a/test/core/table_init.wast b/test/core/table_init.wast index 658deb68..80eaf065 100644 --- a/test/core/table_init.wast +++ b/test/core/table_init.wast @@ -239,7 +239,7 @@ (func (export "test") (elem.drop 2) )) -(assert_trap (invoke "test") "element segment dropped") +(invoke "test") (module (table 30 30 funcref) @@ -262,7 +262,7 @@ (func (export "test") (table.init 2 (i32.const 12) (i32.const 1) (i32.const 1)) )) -(assert_trap (invoke "test") "element segment dropped") +(assert_trap (invoke "test") "out of bounds") (module (table 30 30 funcref) @@ -308,7 +308,7 @@ (func (export "test") (elem.drop 1) (elem.drop 1))) -(assert_trap (invoke "test") "element segment dropped") +(invoke "test") (module (table 30 30 funcref) @@ -331,7 +331,7 @@ (func (export "test") (elem.drop 1) (table.init 1 (i32.const 12) (i32.const 1) (i32.const 1)))) -(assert_trap (invoke "test") "element segment dropped") +(assert_trap (invoke "test") "out of bounds") (module (table 30 30 funcref) @@ -446,7 +446,7 @@ (func (export "test") (table.init 1 (i32.const 12) (i32.const 5) (i32.const 0)) )) -(invoke "test") +(assert_trap (invoke "test") "out of bounds") (module (table 30 30 funcref) @@ -492,7 +492,7 @@ (func (export "test") (table.init 1 (i32.const 31) (i32.const 2) (i32.const 0)) )) -(invoke "test") +(assert_trap (invoke "test") "out of bounds") (module (table 30 30 funcref) @@ -517,6 +517,29 @@ )) (invoke "test") +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 1 (i32.const 31) (i32.const 5) (i32.const 0)) + )) +(assert_trap (invoke "test") "out of bounds") + (assert_invalid (module (table 10 funcref) diff --git a/test/meta/generate_memory_copy.js b/test/meta/generate_memory_copy.js index 687ce6c7..e5b7c0c9 100644 --- a/test/meta/generate_memory_copy.js +++ b/test/meta/generate_memory_copy.js @@ -280,13 +280,13 @@ print( (invoke "test") `); -// Zero len with dest offset out-of-bounds past the end of memory is allowed +// Zero len with dest offset out-of-bounds past the end of memory is not allowed print( `(module (memory 1 1) (func (export "test") (memory.copy (i32.const 0x20000) (i32.const 0x7000) (i32.const 0)))) -(invoke "test") +(assert_trap (invoke "test") "out of bounds") `); // Zero len with src offset out-of-bounds at the end of memory is allowed @@ -298,13 +298,13 @@ print( (invoke "test") `); -// Zero len with src offset out-of-bounds past the end of memory is allowed +// Zero len with src offset out-of-bounds past the end of memory is not allowed print( `(module (memory 1 1) (func (export "test") (memory.copy (i32.const 0x9000) (i32.const 0x20000) (i32.const 0)))) -(invoke "test") +(assert_trap (invoke "test") "out of bounds") `); // Zero len with both dest and src offsets out-of-bounds at the end of memory is allowed @@ -316,6 +316,15 @@ print( (invoke "test") `); +// Zero len with both dest and src offsets out-of-bounds past the end of memory is not allowed +print( +`(module + (memory 1 1) + (func (export "test") + (memory.copy (i32.const 0x20000) (i32.const 0x20000) (i32.const 0)))) +(assert_trap (invoke "test") "out of bounds") +`); + // 100 random fills followed by 100 random copies, in a single-page buffer, // followed by verification of the (now heavily mashed-around) buffer. print( diff --git a/test/meta/generate_memory_fill.js b/test/meta/generate_memory_fill.js index cb048f6a..0dba569e 100644 --- a/test/meta/generate_memory_fill.js +++ b/test/meta/generate_memory_fill.js @@ -56,13 +56,13 @@ print( (invoke "test") `); -// Zero len with offset out-of-bounds past the end of memory is allowed +// Zero len with offset out-of-bounds past the end of memory is not allowed print( `(module ${PREAMBLE} (func (export "test") (memory.fill (i32.const 0x20000) (i32.const 0x55) (i32.const 0)))) -(invoke "test") +(assert_trap (invoke "test") "out of bounds") `); // Very large range @@ -136,7 +136,7 @@ function mem_fill(min, max, shared, backup, write=backup*2) { (func (export "run") (param $offs i32) (param $val i32) (param $len i32) (memory.fill (local.get $offs) (local.get $val) (local.get $len)))) `); - // A fill past the end should throw *and* have filled all the way up to the end + // A fill past the end should throw *and* not have performed a partial fill let offs = min*PAGESIZE - backup; let val = 37; print( diff --git a/test/meta/generate_memory_init.js b/test/meta/generate_memory_init.js index eccbabfe..0b5d7f65 100644 --- a/test/meta/generate_memory_init.js +++ b/test/meta/generate_memory_init.js @@ -85,7 +85,7 @@ print( (func (export "test") (data.drop 0) (data.drop 0))) -(assert_trap (invoke "test") "data segment dropped") +(invoke "test") `); // drop, then init @@ -95,7 +95,7 @@ print( (func (export "test") (data.drop 0) (memory.init 0 (i32.const 1234) (i32.const 1) (i32.const 1)))) -(assert_trap (invoke "test") "data segment dropped") +(assert_trap (invoke "test") "out of bounds") `); // init with data seg ix indicating an active segment @@ -105,7 +105,7 @@ print( (data (i32.const 0) "\\37") (func (export "test") (memory.init 0 (i32.const 1234) (i32.const 1) (i32.const 1)))) -(assert_trap (invoke "test") "data segment dropped") +(assert_trap (invoke "test") "out of bounds") `); // init with no memory @@ -164,13 +164,13 @@ print( (assert_trap (invoke "test") "out of bounds") `); -// init: seg ix is valid passive, src offset past the end, zero len is always valid +// init: seg ix is valid passive, src offset past the end, zero len is invalid print( `(module ${PREAMBLE} (func (export "test") (memory.init 0 (i32.const 1234) (i32.const 4) (i32.const 0)))) -(invoke "test") +(assert_trap (invoke "test") "out of bounds") `); // init: seg ix is valid passive, zero len, src offset at the end @@ -182,13 +182,13 @@ print( (invoke "test") `); -// init: seg ix is valid passive, dst offset past the end, zero len is always valid +// init: seg ix is valid passive, dst offset past the end, zero len is invalid print( `(module ${PREAMBLE} (func (export "test") (memory.init 0 (i32.const 0x10001) (i32.const 0) (i32.const 0)))) -(invoke "test") +(assert_trap (invoke "test") "out of bounds") `); // init: seg ix is valid passive, zero len, but dst offset at the end @@ -209,6 +209,16 @@ print( (invoke "test") `); +// init: seg ix is valid passive, src and dst offset past the end, zero len is +// invalid +print( +`(module + ${PREAMBLE} + (func (export "test") + (memory.init 0 (i32.const 0x10001) (i32.const 4) (i32.const 0)))) +(assert_trap (invoke "test") "out of bounds") +`); + // invalid argument types. TODO: can add anyfunc etc here. { const tys = ['i32', 'f32', 'i64', 'f64']; diff --git a/test/meta/generate_table_copy.js b/test/meta/generate_table_copy.js index d399fdc4..86718944 100644 --- a/test/meta/generate_table_copy.js +++ b/test/meta/generate_table_copy.js @@ -189,26 +189,31 @@ tab_test2("(table.copy (i32.const 30) (i32.const 15) (i32.const 0))", "", undefined); -// copy: zero length with dst offset out of bounds past the end of the table is allowed +// copy: zero length with dst offset out of bounds past the end of the table is not allowed tab_test2("(table.copy (i32.const 31) (i32.const 15) (i32.const 0))", "", - undefined); + "out of bounds"); // copy: zero length with src offset out of bounds at the end of the table is allowed tab_test2("(table.copy (i32.const 15) (i32.const 30) (i32.const 0))", "", undefined); -// copy: zero length with src offset out of bounds past the end of the table is allowed +// copy: zero length with src offset out of bounds past the end of the table is not allowed tab_test2("(table.copy (i32.const 15) (i32.const 31) (i32.const 0))", "", - undefined); + "out of bounds"); // copy: zero length with both dst and src offset out of bounds at the end of the table is allowed tab_test2("(table.copy (i32.const 30) (i32.const 30) (i32.const 0))", "", undefined); +// copy: zero length with both dst and src offset out of bounds past the end of the table is not allowed +tab_test2("(table.copy (i32.const 31) (i32.const 31) (i32.const 0))", + "", + "out of bounds"); + // table.copy: out of bounds of the table for the source or target, but should // perform the operation up to the appropriate bound. Major cases: // diff --git a/test/meta/generate_table_init.js b/test/meta/generate_table_init.js index bc594090..9329d937 100644 --- a/test/meta/generate_table_init.js +++ b/test/meta/generate_table_init.js @@ -181,32 +181,29 @@ function tab_test2(insn1, insn2, errText) { do_test(insn1, insn2, errText); } -function tab_test_nofail(insn1, insn2) { - do_test(insn1, insn2, undefined); -} - // drop with elem seg ix indicating an active segment tab_test1("(elem.drop 2)", - "element segment dropped"); + undefined); // init with elem seg ix indicating an active segment tab_test1("(table.init 2 (i32.const 12) (i32.const 1) (i32.const 1))", - "element segment dropped"); + "out of bounds"); // init, using an elem seg ix more than once is OK -tab_test_nofail( +tab_test2( "(table.init 1 (i32.const 12) (i32.const 1) (i32.const 1))", - "(table.init 1 (i32.const 21) (i32.const 1) (i32.const 1))"); + "(table.init 1 (i32.const 21) (i32.const 1) (i32.const 1))", + undefined); // drop, then drop tab_test2("(elem.drop 1)", "(elem.drop 1)", - "element segment dropped"); + undefined); // drop, then init tab_test2("(elem.drop 1)", "(table.init 1 (i32.const 12) (i32.const 1) (i32.const 1))", - "element segment dropped"); + "out of bounds"); // init: seg ix is valid passive, but length to copy > len of seg tab_test1("(table.init 1 (i32.const 12) (i32.const 0) (i32.const 5))", @@ -226,9 +223,9 @@ tab_test1("(table.init 1 (i32.const 12) (i32.const 4) (i32.const 0))", undefined); // init: seg ix is valid passive, zero len, and src offset out of bounds past the -// end of the table - this is allowed +// end of the table - this is not allowed tab_test1("(table.init 1 (i32.const 12) (i32.const 5) (i32.const 0))", - undefined); + "out of bounds"); // init: seg ix is valid passive, zero len, and dst offset out of bounds at the // end of the table - this is allowed @@ -236,15 +233,20 @@ tab_test1("(table.init 1 (i32.const 30) (i32.const 2) (i32.const 0))", undefined); // init: seg ix is valid passive, zero len, and dst offset out of bounds past the -// end of the table - this is allowed +// end of the table - this is not allowed tab_test1("(table.init 1 (i32.const 31) (i32.const 2) (i32.const 0))", - undefined); + "out of bounds"); // init: seg ix is valid passive, zero len, and dst and src offsets out of bounds // at the end of the table - this is allowed tab_test1("(table.init 1 (i32.const 30) (i32.const 4) (i32.const 0))", undefined); +// init: seg ix is valid passive, zero len, and src/dst offset out of bounds past the +// end of the table - this is not allowed +tab_test1("(table.init 1 (i32.const 31) (i32.const 5) (i32.const 0))", + "out of bounds"); + // invalid argument types { const tys = ['i32', 'f32', 'i64', 'f64'];