Skip to content

Commit 08be05a

Browse files
sunfishcodebinji
authored andcommitted
Init/copy/fill with offsets at the end of the memory or table (WebAssembly#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.
1 parent 6eb26a3 commit 08be05a

File tree

11 files changed

+292
-13
lines changed

11 files changed

+292
-13
lines changed

proposals/bulk-memory-operations/Overview.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -277,8 +277,8 @@ A trap occurs if:
277277
active segments that were dropped after being copied into memory during module
278278
instantiation.
279279
* any of the accessed bytes lies outside the source data segment or the target memory
280-
* the source offset is outside the source data segment
281-
* the destination offset is outside the target memory
280+
* the source offset is greater than the length of the source data segment
281+
* the destination offset is greater than the length of the target memory
282282

283283
Note that it is allowed to use `memory.init` on the same data segment more than
284284
once.
@@ -342,8 +342,8 @@ The instruction has the signature `[i32 i32 i32] -> []`. The parameters are, in
342342

343343
A trap occurs if:
344344
* any of the accessed bytes lies outside the source or target memory
345-
* the source offset is outside the source memory
346-
* the destination offset is outside the target memory
345+
* the source offset is greater than the length of the source memory
346+
* the destination offset is greater than the length of the target memory
347347

348348
A trap resulting from an access outside the source or target region
349349
only occurs once the first byte that is outside the source or target
@@ -367,7 +367,7 @@ The instruction has the signature `[i32 i32 i32] -> []`. The parameters are, in
367367

368368
A trap occurs if:
369369
* any of the accessed bytes lies outside the target memory
370-
* the destination offset is outside the target memory
370+
* the destination offset is greater than the length of the target memory
371371

372372
Filling takes place bytewise from lower addresses toward higher
373373
addresses. A trap resulting from an access outside the target memory

test/core/memory_copy.wast

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4997,12 +4997,30 @@
49974997
(memory.copy (i32.const 0x10000) (i32.const 0x7000) (i32.const 0))))
49984998
(invoke "test")
49994999

5000+
(module
5001+
(memory 1 1)
5002+
(func (export "test")
5003+
(memory.copy (i32.const 0x20000) (i32.const 0x7000) (i32.const 0))))
5004+
(assert_trap (invoke "test") "out of bounds")
5005+
50005006
(module
50015007
(memory 1 1)
50025008
(func (export "test")
50035009
(memory.copy (i32.const 0x9000) (i32.const 0x10000) (i32.const 0))))
50045010
(invoke "test")
50055011

5012+
(module
5013+
(memory 1 1)
5014+
(func (export "test")
5015+
(memory.copy (i32.const 0x9000) (i32.const 0x20000) (i32.const 0))))
5016+
(assert_trap (invoke "test") "out of bounds")
5017+
5018+
(module
5019+
(memory 1 1)
5020+
(func (export "test")
5021+
(memory.copy (i32.const 0x10000) (i32.const 0x10000) (i32.const 0))))
5022+
(invoke "test")
5023+
50065024
(module
50075025
(memory 1 1)
50085026
(func (export "test")

test/core/memory_fill.wast

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,24 @@
9898
(memory.fill (i32.const 0x10000) (i32.const 0x55) (i32.const 0))))
9999
(invoke "test")
100100

101+
(module
102+
(memory 1 1)
103+
104+
(func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32)
105+
(loop $cont
106+
(if (i32.eq (local.get $from) (local.get $to))
107+
(then
108+
(return (i32.const -1))))
109+
(if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected))
110+
(then
111+
(local.set $from (i32.add (local.get $from) (i32.const 1)))
112+
(br $cont))))
113+
(return (local.get $from)))
114+
115+
(func (export "test")
116+
(memory.fill (i32.const 0x20000) (i32.const 0x55) (i32.const 0))))
117+
(assert_trap (invoke "test") "out of bounds memory access")
118+
101119
(module
102120
(memory 1 1)
103121

test/core/memory_init.wast

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,9 +276,30 @@
276276
(memory 1)
277277
(data "\37")
278278
(func (export "test")
279-
(memory.init 0 (i32.const 0x10000) (i32.const 2) (i32.const 0))))
279+
(memory.init 0 (i32.const 1234) (i32.const 1) (i32.const 0))))
280+
(invoke "test")
281+
282+
(module
283+
(memory 1)
284+
(data passive "\37")
285+
(func (export "test")
286+
(memory.init 0 (i32.const 0x10001) (i32.const 0) (i32.const 0))))
280287
(assert_trap (invoke "test") "out of bounds")
281288

289+
(module
290+
(memory 1)
291+
(data passive "\37")
292+
(func (export "test")
293+
(memory.init 0 (i32.const 0x10000) (i32.const 0) (i32.const 0))))
294+
(invoke "test")
295+
296+
(module
297+
(memory 1)
298+
(data passive "\37")
299+
(func (export "test")
300+
(memory.init 0 (i32.const 0x10000) (i32.const 1) (i32.const 0))))
301+
(invoke "test")
302+
282303
(assert_invalid
283304
(module
284305
(memory 1)

test/core/table_copy.wast

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,12 +625,78 @@
625625
(func (result i32) (i32.const 7))
626626
(func (result i32) (i32.const 8))
627627
(func (result i32) (i32.const 9))
628+
(func (export "test")
629+
(table.copy (i32.const 31) (i32.const 15) (i32.const 0))
630+
))
631+
632+
(assert_trap (invoke "test") "out of bounds")
633+
634+
(module
635+
(table 30 30 funcref)
636+
(elem (i32.const 2) 3 1 4 1)
637+
(elem passive funcref 2 7 1 8)
638+
(elem (i32.const 12) 7 5 2 3 6)
639+
(elem passive funcref 5 9 2 7 6)
640+
(func (result i32) (i32.const 0))
641+
(func (result i32) (i32.const 1))
642+
(func (result i32) (i32.const 2))
643+
(func (result i32) (i32.const 3))
644+
(func (result i32) (i32.const 4))
645+
(func (result i32) (i32.const 5))
646+
(func (result i32) (i32.const 6))
647+
(func (result i32) (i32.const 7))
648+
(func (result i32) (i32.const 8))
649+
(func (result i32) (i32.const 9))
628650
(func (export "test")
629651
(table.copy (i32.const 15) (i32.const 30) (i32.const 0))
630652
))
631653

632654
(invoke "test")
633655

656+
(module
657+
(table 30 30 funcref)
658+
(elem (i32.const 2) 3 1 4 1)
659+
(elem passive funcref 2 7 1 8)
660+
(elem (i32.const 12) 7 5 2 3 6)
661+
(elem passive funcref 5 9 2 7 6)
662+
(func (result i32) (i32.const 0))
663+
(func (result i32) (i32.const 1))
664+
(func (result i32) (i32.const 2))
665+
(func (result i32) (i32.const 3))
666+
(func (result i32) (i32.const 4))
667+
(func (result i32) (i32.const 5))
668+
(func (result i32) (i32.const 6))
669+
(func (result i32) (i32.const 7))
670+
(func (result i32) (i32.const 8))
671+
(func (result i32) (i32.const 9))
672+
(func (export "test")
673+
(table.copy (i32.const 15) (i32.const 31) (i32.const 0))
674+
))
675+
676+
(assert_trap (invoke "test") "out of bounds")
677+
678+
(module
679+
(table 30 30 funcref)
680+
(elem (i32.const 2) 3 1 4 1)
681+
(elem passive funcref 2 7 1 8)
682+
(elem (i32.const 12) 7 5 2 3 6)
683+
(elem passive funcref 5 9 2 7 6)
684+
(func (result i32) (i32.const 0))
685+
(func (result i32) (i32.const 1))
686+
(func (result i32) (i32.const 2))
687+
(func (result i32) (i32.const 3))
688+
(func (result i32) (i32.const 4))
689+
(func (result i32) (i32.const 5))
690+
(func (result i32) (i32.const 6))
691+
(func (result i32) (i32.const 7))
692+
(func (result i32) (i32.const 8))
693+
(func (result i32) (i32.const 9))
694+
(func (export "test")
695+
(table.copy (i32.const 30) (i32.const 30) (i32.const 0))
696+
))
697+
698+
(invoke "test")
699+
634700
(module
635701
(type (func (result i32)))
636702
(table 32 64 funcref)

test/core/table_init.wast

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,11 +439,74 @@
439439
(func (result i32) (i32.const 7))
440440
(func (result i32) (i32.const 8))
441441
(func (result i32) (i32.const 9))
442+
(func (export "test")
443+
(table.init 1 (i32.const 12) (i32.const 5) (i32.const 0))
444+
))
445+
(assert_trap (invoke "test") "out of bounds")
446+
447+
(module
448+
(table 30 30 funcref)
449+
(elem (i32.const 2) 3 1 4 1)
450+
(elem passive funcref 2 7 1 8)
451+
(elem (i32.const 12) 7 5 2 3 6)
452+
(elem passive funcref 5 9 2 7 6)
453+
(func (result i32) (i32.const 0))
454+
(func (result i32) (i32.const 1))
455+
(func (result i32) (i32.const 2))
456+
(func (result i32) (i32.const 3))
457+
(func (result i32) (i32.const 4))
458+
(func (result i32) (i32.const 5))
459+
(func (result i32) (i32.const 6))
460+
(func (result i32) (i32.const 7))
461+
(func (result i32) (i32.const 8))
462+
(func (result i32) (i32.const 9))
442463
(func (export "test")
443464
(table.init 1 (i32.const 30) (i32.const 2) (i32.const 0))
444465
))
445466
(invoke "test")
446467

468+
(module
469+
(table 30 30 funcref)
470+
(elem (i32.const 2) 3 1 4 1)
471+
(elem passive funcref 2 7 1 8)
472+
(elem (i32.const 12) 7 5 2 3 6)
473+
(elem passive funcref 5 9 2 7 6)
474+
(func (result i32) (i32.const 0))
475+
(func (result i32) (i32.const 1))
476+
(func (result i32) (i32.const 2))
477+
(func (result i32) (i32.const 3))
478+
(func (result i32) (i32.const 4))
479+
(func (result i32) (i32.const 5))
480+
(func (result i32) (i32.const 6))
481+
(func (result i32) (i32.const 7))
482+
(func (result i32) (i32.const 8))
483+
(func (result i32) (i32.const 9))
484+
(func (export "test")
485+
(table.init 1 (i32.const 31) (i32.const 2) (i32.const 0))
486+
))
487+
(assert_trap (invoke "test") "out of bounds")
488+
489+
(module
490+
(table 30 30 funcref)
491+
(elem (i32.const 2) 3 1 4 1)
492+
(elem passive funcref 2 7 1 8)
493+
(elem (i32.const 12) 7 5 2 3 6)
494+
(elem passive funcref 5 9 2 7 6)
495+
(func (result i32) (i32.const 0))
496+
(func (result i32) (i32.const 1))
497+
(func (result i32) (i32.const 2))
498+
(func (result i32) (i32.const 3))
499+
(func (result i32) (i32.const 4))
500+
(func (result i32) (i32.const 5))
501+
(func (result i32) (i32.const 6))
502+
(func (result i32) (i32.const 7))
503+
(func (result i32) (i32.const 8))
504+
(func (result i32) (i32.const 9))
505+
(func (export "test")
506+
(table.init 1 (i32.const 30) (i32.const 4) (i32.const 0))
507+
))
508+
(invoke "test")
509+
447510
(assert_invalid
448511
(module
449512
(table 10 funcref)

test/meta/generate_memory_copy.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,15 @@ print(
304304
(invoke "test")
305305
`);
306306

307+
// Zero len with dest offset out-of-bounds past the end of memory is not allowed
308+
print(
309+
`(module
310+
(memory 1 1)
311+
(func (export "test")
312+
(memory.copy (i32.const 0x20000) (i32.const 0x7000) (i32.const 0))))
313+
(assert_trap (invoke "test") "out of bounds")
314+
`);
315+
307316
// Zero len with src offset out-of-bounds at the end of memory is allowed
308317
print(
309318
`(module
@@ -313,6 +322,24 @@ print(
313322
(invoke "test")
314323
`);
315324

325+
// Zero len with src offset out-of-bounds past the end of memory is not allowed
326+
print(
327+
`(module
328+
(memory 1 1)
329+
(func (export "test")
330+
(memory.copy (i32.const 0x9000) (i32.const 0x20000) (i32.const 0))))
331+
(assert_trap (invoke "test") "out of bounds")
332+
`);
333+
334+
// Zero len with both dest and src offsets out-of-bounds at the end of memory is allowed
335+
print(
336+
`(module
337+
(memory 1 1)
338+
(func (export "test")
339+
(memory.copy (i32.const 0x10000) (i32.const 0x10000) (i32.const 0))))
340+
(invoke "test")
341+
`);
342+
316343
// 100 random fills followed by 100 random copies, in a single-page buffer,
317344
// followed by verification of the (now heavily mashed-around) buffer.
318345
print(

test/meta/generate_memory_fill.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,15 @@ print(
5656
(invoke "test")
5757
`);
5858

59+
// Zero len with offset out-of-bounds past the end of memory is not allowed
60+
print(
61+
`(module
62+
${PREAMBLE}
63+
(func (export "test")
64+
(memory.fill (i32.const 0x20000) (i32.const 0x55) (i32.const 0))))
65+
(assert_trap (invoke "test") "out of bounds memory access")
66+
`);
67+
5968
// Very large range
6069
print(
6170
`(module

test/meta/generate_memory_init.js

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ print(
164164
(assert_trap (invoke "test") "out of bounds")
165165
`);
166166

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

176-
// init: seg ix is valid passive, zero len, but dst offset out of bounds
176+
// init: seg ix is valid passive, zero len, src offset at the end
177177
print(
178178
`(module
179179
${PREAMBLE}
180180
(func (export "test")
181-
(memory.init 0 (i32.const 0x10000) (i32.const 2) (i32.const 0))))
181+
(memory.init 0 (i32.const 1234) (i32.const 1) (i32.const 0))))
182+
(invoke "test")
183+
`);
184+
185+
// init: seg ix is valid passive, zero len, but dst offset past the end
186+
print(
187+
`(module
188+
${PREAMBLE}
189+
(func (export "test")
190+
(memory.init 0 (i32.const 0x10001) (i32.const 0) (i32.const 0))))
182191
(assert_trap (invoke "test") "out of bounds")
183192
`);
184193

194+
// init: seg ix is valid passive, zero len, but dst offset at the end
195+
print(
196+
`(module
197+
${PREAMBLE}
198+
(func (export "test")
199+
(memory.init 0 (i32.const 0x10000) (i32.const 0) (i32.const 0))))
200+
(invoke "test")
201+
`);
202+
203+
// init: seg ix is valid passive, zero len, dst and src offsets at the end
204+
print(
205+
`(module
206+
${PREAMBLE}
207+
(func (export "test")
208+
(memory.init 0 (i32.const 0x10000) (i32.const 1) (i32.const 0))))
209+
(invoke "test")
210+
`);
211+
185212
// invalid argument types. TODO: can add anyfunc etc here.
186213
{
187214
const tys = ['i32', 'f32', 'i64', 'f64'];

0 commit comments

Comments
 (0)