Skip to content

Commit

Permalink
Init/copy/fill with offsets at the end of the memory or table (WebAss…
Browse files Browse the repository at this point in the history
…embly#86)

* Fix expected trap messages.

The spec interpreter says "element segment dropped", rather than
"elements segment dropped".

* Fix "zero len, but dst offset out of bounds" test.

Fix this test to test what it's comment says it's testing.

* Add more tests for zero-length operations.

* Update the Overview text to reflect the zero-length at-the-end semantics.
  • Loading branch information
sunfishcode authored and binji committed May 13, 2019
1 parent 6eb26a3 commit 08be05a
Show file tree
Hide file tree
Showing 11 changed files with 292 additions and 13 deletions.
10 changes: 5 additions & 5 deletions proposals/bulk-memory-operations/Overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,8 +277,8 @@ A trap occurs if:
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 is outside the source data segment
* the destination offset is outside the target memory
* the source offset is greater than the length of the source data segment
* the destination offset 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.
Expand Down Expand Up @@ -342,8 +342,8 @@ The instruction has the signature `[i32 i32 i32] -> []`. The parameters are, in

A trap occurs if:
* any of the accessed bytes lies outside the source or target memory
* the source offset is outside the source memory
* the destination offset is outside the target memory
* the source offset is greater than the length of the source memory
* the destination offset 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
Expand All @@ -367,7 +367,7 @@ The instruction has the signature `[i32 i32 i32] -> []`. The parameters are, in

A trap occurs if:
* any of the accessed bytes lies outside the target memory
* the destination offset is outside the target memory
* the destination offset 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
Expand Down
18 changes: 18 additions & 0 deletions test/core/memory_copy.wast
Original file line number Diff line number Diff line change
Expand Up @@ -4997,12 +4997,30 @@
(memory.copy (i32.const 0x10000) (i32.const 0x7000) (i32.const 0))))
(invoke "test")

(module
(memory 1 1)
(func (export "test")
(memory.copy (i32.const 0x20000) (i32.const 0x7000) (i32.const 0))))
(assert_trap (invoke "test") "out of bounds")

(module
(memory 1 1)
(func (export "test")
(memory.copy (i32.const 0x9000) (i32.const 0x10000) (i32.const 0))))
(invoke "test")

(module
(memory 1 1)
(func (export "test")
(memory.copy (i32.const 0x9000) (i32.const 0x20000) (i32.const 0))))
(assert_trap (invoke "test") "out of bounds")

(module
(memory 1 1)
(func (export "test")
(memory.copy (i32.const 0x10000) (i32.const 0x10000) (i32.const 0))))
(invoke "test")

(module
(memory 1 1)
(func (export "test")
Expand Down
18 changes: 18 additions & 0 deletions test/core/memory_fill.wast
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,24 @@
(memory.fill (i32.const 0x10000) (i32.const 0x55) (i32.const 0))))
(invoke "test")

(module
(memory 1 1)

(func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32)
(loop $cont
(if (i32.eq (local.get $from) (local.get $to))
(then
(return (i32.const -1))))
(if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected))
(then
(local.set $from (i32.add (local.get $from) (i32.const 1)))
(br $cont))))
(return (local.get $from)))

(func (export "test")
(memory.fill (i32.const 0x20000) (i32.const 0x55) (i32.const 0))))
(assert_trap (invoke "test") "out of bounds memory access")

(module
(memory 1 1)

Expand Down
23 changes: 22 additions & 1 deletion test/core/memory_init.wast
Original file line number Diff line number Diff line change
Expand Up @@ -276,9 +276,30 @@
(memory 1)
(data "\37")
(func (export "test")
(memory.init 0 (i32.const 0x10000) (i32.const 2) (i32.const 0))))
(memory.init 0 (i32.const 1234) (i32.const 1) (i32.const 0))))
(invoke "test")

(module
(memory 1)
(data passive "\37")
(func (export "test")
(memory.init 0 (i32.const 0x10001) (i32.const 0) (i32.const 0))))
(assert_trap (invoke "test") "out of bounds")

(module
(memory 1)
(data passive "\37")
(func (export "test")
(memory.init 0 (i32.const 0x10000) (i32.const 0) (i32.const 0))))
(invoke "test")

(module
(memory 1)
(data passive "\37")
(func (export "test")
(memory.init 0 (i32.const 0x10000) (i32.const 1) (i32.const 0))))
(invoke "test")

(assert_invalid
(module
(memory 1)
Expand Down
66 changes: 66 additions & 0 deletions test/core/table_copy.wast
Original file line number Diff line number Diff line change
Expand Up @@ -625,12 +625,78 @@
(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 15) (i32.const 0))
))

(assert_trap (invoke "test") "out of bounds")

(module
(table 30 30 funcref)
(elem (i32.const 2) 3 1 4 1)
(elem passive funcref 2 7 1 8)
(elem (i32.const 12) 7 5 2 3 6)
(elem passive funcref 5 9 2 7 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 15) (i32.const 30) (i32.const 0))
))

(invoke "test")

(module
(table 30 30 funcref)
(elem (i32.const 2) 3 1 4 1)
(elem passive funcref 2 7 1 8)
(elem (i32.const 12) 7 5 2 3 6)
(elem passive funcref 5 9 2 7 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 15) (i32.const 31) (i32.const 0))
))

(assert_trap (invoke "test") "out of bounds")

(module
(table 30 30 funcref)
(elem (i32.const 2) 3 1 4 1)
(elem passive funcref 2 7 1 8)
(elem (i32.const 12) 7 5 2 3 6)
(elem passive funcref 5 9 2 7 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 30) (i32.const 30) (i32.const 0))
))

(invoke "test")

(module
(type (func (result i32)))
(table 32 64 funcref)
Expand Down
63 changes: 63 additions & 0 deletions test/core/table_init.wast
Original file line number Diff line number Diff line change
Expand Up @@ -439,11 +439,74 @@
(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 12) (i32.const 5) (i32.const 0))
))
(assert_trap (invoke "test") "out of bounds")

(module
(table 30 30 funcref)
(elem (i32.const 2) 3 1 4 1)
(elem passive funcref 2 7 1 8)
(elem (i32.const 12) 7 5 2 3 6)
(elem passive funcref 5 9 2 7 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 30) (i32.const 2) (i32.const 0))
))
(invoke "test")

(module
(table 30 30 funcref)
(elem (i32.const 2) 3 1 4 1)
(elem passive funcref 2 7 1 8)
(elem (i32.const 12) 7 5 2 3 6)
(elem passive funcref 5 9 2 7 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 2) (i32.const 0))
))
(assert_trap (invoke "test") "out of bounds")

(module
(table 30 30 funcref)
(elem (i32.const 2) 3 1 4 1)
(elem passive funcref 2 7 1 8)
(elem (i32.const 12) 7 5 2 3 6)
(elem passive funcref 5 9 2 7 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 30) (i32.const 4) (i32.const 0))
))
(invoke "test")

(assert_invalid
(module
(table 10 funcref)
Expand Down
27 changes: 27 additions & 0 deletions test/meta/generate_memory_copy.js
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,15 @@ print(
(invoke "test")
`);

// 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))))
(assert_trap (invoke "test") "out of bounds")
`);

// Zero len with src offset out-of-bounds at the end of memory is allowed
print(
`(module
Expand All @@ -313,6 +322,24 @@ print(
(invoke "test")
`);

// 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))))
(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
print(
`(module
(memory 1 1)
(func (export "test")
(memory.copy (i32.const 0x10000) (i32.const 0x10000) (i32.const 0))))
(invoke "test")
`);

// 100 random fills followed by 100 random copies, in a single-page buffer,
// followed by verification of the (now heavily mashed-around) buffer.
print(
Expand Down
9 changes: 9 additions & 0 deletions test/meta/generate_memory_fill.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,15 @@ print(
(invoke "test")
`);

// 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))))
(assert_trap (invoke "test") "out of bounds memory access")
`);

// Very large range
print(
`(module
Expand Down
33 changes: 30 additions & 3 deletions test/meta/generate_memory_init.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ print(
(assert_trap (invoke "test") "out of bounds")
`);

// init: seg ix is valid passive, zero len, but src offset out of bounds
// init: seg ix is valid passive, zero len, but src offset past the end
print(
`(module
${PREAMBLE}
Expand All @@ -173,15 +173,42 @@ print(
(assert_trap (invoke "test") "out of bounds")
`);

// init: seg ix is valid passive, zero len, but dst offset out of bounds
// init: seg ix is valid passive, zero len, src offset at the end
print(
`(module
${PREAMBLE}
(func (export "test")
(memory.init 0 (i32.const 0x10000) (i32.const 2) (i32.const 0))))
(memory.init 0 (i32.const 1234) (i32.const 1) (i32.const 0))))
(invoke "test")
`);

// init: seg ix is valid passive, zero len, but dst offset past the end
print(
`(module
${PREAMBLE}
(func (export "test")
(memory.init 0 (i32.const 0x10001) (i32.const 0) (i32.const 0))))
(assert_trap (invoke "test") "out of bounds")
`);

// init: seg ix is valid passive, zero len, but dst offset at the end
print(
`(module
${PREAMBLE}
(func (export "test")
(memory.init 0 (i32.const 0x10000) (i32.const 0) (i32.const 0))))
(invoke "test")
`);

// init: seg ix is valid passive, zero len, dst and src offsets at the end
print(
`(module
${PREAMBLE}
(func (export "test")
(memory.init 0 (i32.const 0x10000) (i32.const 1) (i32.const 0))))
(invoke "test")
`);

// invalid argument types. TODO: can add anyfunc etc here.
{
const tys = ['i32', 'f32', 'i64', 'f64'];
Expand Down
Loading

0 comments on commit 08be05a

Please sign in to comment.