1 // Copyright 2019 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/wasm/wasm-module-builder.h"
6 #include "test/cctest/cctest.h"
7 #include "test/cctest/wasm/wasm-run-utils.h"
8 #include "test/common/wasm/test-signatures.h"
9 #include "test/common/wasm/wasm-macro-gen.h"
10
11 namespace v8 {
12 namespace internal {
13 namespace wasm {
14 namespace test_run_wasm_bulk_memory {
15
16 namespace {
CheckMemoryEquals(TestingModuleBuilder * builder,size_t index,const std::vector<byte> & expected)17 void CheckMemoryEquals(TestingModuleBuilder* builder, size_t index,
18 const std::vector<byte>& expected) {
19 const byte* mem_start = builder->raw_mem_start<byte>();
20 const byte* mem_end = builder->raw_mem_end<byte>();
21 size_t mem_size = mem_end - mem_start;
22 CHECK_LE(index, mem_size);
23 CHECK_LE(index + expected.size(), mem_size);
24 for (size_t i = 0; i < expected.size(); ++i) {
25 CHECK_EQ(expected[i], mem_start[index + i]);
26 }
27 }
28
CheckMemoryEqualsZero(TestingModuleBuilder * builder,size_t index,size_t length)29 void CheckMemoryEqualsZero(TestingModuleBuilder* builder, size_t index,
30 size_t length) {
31 const byte* mem_start = builder->raw_mem_start<byte>();
32 const byte* mem_end = builder->raw_mem_end<byte>();
33 size_t mem_size = mem_end - mem_start;
34 CHECK_LE(index, mem_size);
35 CHECK_LE(index + length, mem_size);
36 for (size_t i = 0; i < length; ++i) {
37 CHECK_EQ(0, mem_start[index + i]);
38 }
39 }
40
CheckMemoryEqualsFollowedByZeroes(TestingModuleBuilder * builder,const std::vector<byte> & expected)41 void CheckMemoryEqualsFollowedByZeroes(TestingModuleBuilder* builder,
42 const std::vector<byte>& expected) {
43 CheckMemoryEquals(builder, 0, expected);
44 CheckMemoryEqualsZero(builder, expected.size(),
45 builder->mem_size() - expected.size());
46 }
47 } // namespace
48
WASM_EXEC_TEST(MemoryInit)49 WASM_EXEC_TEST(MemoryInit) {
50 WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
51 r.builder().AddMemory(kWasmPageSize);
52 const byte data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
53 r.builder().AddPassiveDataSegment(base::ArrayVector(data));
54 BUILD(r,
55 WASM_MEMORY_INIT(0, WASM_LOCAL_GET(0), WASM_LOCAL_GET(1),
56 WASM_LOCAL_GET(2)),
57 kExprI32Const, 0);
58
59 // All zeroes.
60 CheckMemoryEqualsZero(&r.builder(), 0, kWasmPageSize);
61
62 // Copy all bytes from data segment 0, to memory at [10, 20).
63 CHECK_EQ(0, r.Call(10, 0, 10));
64 CheckMemoryEqualsFollowedByZeroes(
65 &r.builder(),
66 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
67
68 // Copy bytes in range [5, 10) from data segment 0, to memory at [0, 5).
69 CHECK_EQ(0, r.Call(0, 5, 5));
70 CheckMemoryEqualsFollowedByZeroes(
71 &r.builder(),
72 {5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
73
74 // Copy 0 bytes does nothing.
75 CHECK_EQ(0, r.Call(10, 1, 0));
76 CheckMemoryEqualsFollowedByZeroes(
77 &r.builder(),
78 {5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
79
80 // Copy 0 at end of memory region or data segment is OK.
81 CHECK_EQ(0, r.Call(kWasmPageSize, 0, 0));
82 CHECK_EQ(0, r.Call(0, sizeof(data), 0));
83 }
84
WASM_EXEC_TEST(MemoryInitOutOfBoundsData)85 WASM_EXEC_TEST(MemoryInitOutOfBoundsData) {
86 WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
87 r.builder().AddMemory(kWasmPageSize);
88 const byte data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
89 r.builder().AddPassiveDataSegment(base::ArrayVector(data));
90 BUILD(r,
91 WASM_MEMORY_INIT(0, WASM_LOCAL_GET(0), WASM_LOCAL_GET(1),
92 WASM_LOCAL_GET(2)),
93 kExprI32Const, 0);
94
95 const uint32_t last_5_bytes = kWasmPageSize - 5;
96
97 // Failing memory.init should not have any effect.
98 CHECK_EQ(0xDEADBEEF, r.Call(kWasmPageSize - 5, 0, 6));
99 CheckMemoryEquals(&r.builder(), last_5_bytes, {0, 0, 0, 0, 0});
100
101 r.builder().BlankMemory();
102 CHECK_EQ(0xDEADBEEF, r.Call(0, 5, 6));
103 CheckMemoryEquals(&r.builder(), last_5_bytes, {0, 0, 0, 0, 0});
104 }
105
WASM_EXEC_TEST(MemoryInitOutOfBounds)106 WASM_EXEC_TEST(MemoryInitOutOfBounds) {
107 WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
108 r.builder().AddMemory(kWasmPageSize);
109 const byte data[kWasmPageSize] = {};
110 r.builder().AddPassiveDataSegment(base::ArrayVector(data));
111 BUILD(r,
112 WASM_MEMORY_INIT(0, WASM_LOCAL_GET(0), WASM_LOCAL_GET(1),
113 WASM_LOCAL_GET(2)),
114 kExprI32Const, 0);
115
116 // OK, copy the full data segment to memory.
117 r.Call(0, 0, kWasmPageSize);
118
119 // Source range must not be out of bounds.
120 CHECK_EQ(0xDEADBEEF, r.Call(0, 1, kWasmPageSize));
121 CHECK_EQ(0xDEADBEEF, r.Call(0, 1000, kWasmPageSize));
122 CHECK_EQ(0xDEADBEEF, r.Call(0, kWasmPageSize, 1));
123
124 // Destination range must not be out of bounds.
125 CHECK_EQ(0xDEADBEEF, r.Call(1, 0, kWasmPageSize));
126 CHECK_EQ(0xDEADBEEF, r.Call(1000, 0, kWasmPageSize));
127 CHECK_EQ(0xDEADBEEF, r.Call(kWasmPageSize, 0, 1));
128
129 // Copy 0 out-of-bounds fails if target is invalid.
130 CHECK_EQ(0xDEADBEEF, r.Call(kWasmPageSize + 1, 0, 0));
131 CHECK_EQ(0xDEADBEEF, r.Call(0, kWasmPageSize + 1, 0));
132
133 // Make sure bounds aren't checked with 32-bit wrapping.
134 CHECK_EQ(0xDEADBEEF, r.Call(1, 1, 0xFFFFFFFF));
135 }
136
WASM_EXEC_TEST(MemoryCopy)137 WASM_EXEC_TEST(MemoryCopy) {
138 WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
139 byte* mem = r.builder().AddMemory(kWasmPageSize);
140 BUILD(
141 r,
142 WASM_MEMORY_COPY(WASM_LOCAL_GET(0), WASM_LOCAL_GET(1), WASM_LOCAL_GET(2)),
143 kExprI32Const, 0);
144
145 const byte initial[] = {0, 11, 22, 33, 44, 55, 66, 77};
146 memcpy(mem, initial, sizeof(initial));
147
148 // Copy from [1, 8] to [10, 16].
149 CHECK_EQ(0, r.Call(10, 1, 8));
150 CheckMemoryEqualsFollowedByZeroes(
151 &r.builder(),
152 {0, 11, 22, 33, 44, 55, 66, 77, 0, 0, 11, 22, 33, 44, 55, 66, 77});
153
154 // Copy 0 bytes does nothing.
155 CHECK_EQ(0, r.Call(10, 2, 0));
156 CheckMemoryEqualsFollowedByZeroes(
157 &r.builder(),
158 {0, 11, 22, 33, 44, 55, 66, 77, 0, 0, 11, 22, 33, 44, 55, 66, 77});
159
160 // Copy 0 at end of memory region is OK.
161 CHECK_EQ(0, r.Call(kWasmPageSize, 0, 0));
162 CHECK_EQ(0, r.Call(0, kWasmPageSize, 0));
163 }
164
WASM_EXEC_TEST(MemoryCopyOverlapping)165 WASM_EXEC_TEST(MemoryCopyOverlapping) {
166 WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
167 byte* mem = r.builder().AddMemory(kWasmPageSize);
168 BUILD(
169 r,
170 WASM_MEMORY_COPY(WASM_LOCAL_GET(0), WASM_LOCAL_GET(1), WASM_LOCAL_GET(2)),
171 kExprI32Const, 0);
172
173 const byte initial[] = {10, 20, 30};
174 memcpy(mem, initial, sizeof(initial));
175
176 // Copy from [0, 3] -> [2, 5]. The copy must not overwrite 30 before copying
177 // it (i.e. cannot copy forward in this case).
178 CHECK_EQ(0, r.Call(2, 0, 3));
179 CheckMemoryEqualsFollowedByZeroes(&r.builder(), {10, 20, 10, 20, 30});
180
181 // Copy from [2, 5] -> [0, 3]. The copy must not write the first 10 (i.e.
182 // cannot copy backward in this case).
183 CHECK_EQ(0, r.Call(0, 2, 3));
184 CheckMemoryEqualsFollowedByZeroes(&r.builder(), {10, 20, 30, 20, 30});
185 }
186
WASM_EXEC_TEST(MemoryCopyOutOfBoundsData)187 WASM_EXEC_TEST(MemoryCopyOutOfBoundsData) {
188 WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
189 byte* mem = r.builder().AddMemory(kWasmPageSize);
190 BUILD(
191 r,
192 WASM_MEMORY_COPY(WASM_LOCAL_GET(0), WASM_LOCAL_GET(1), WASM_LOCAL_GET(2)),
193 kExprI32Const, 0);
194
195 const byte data[] = {11, 22, 33, 44, 55, 66, 77, 88};
196 memcpy(mem, data, sizeof(data));
197
198 const uint32_t last_5_bytes = kWasmPageSize - 5;
199
200 CheckMemoryEquals(&r.builder(), last_5_bytes, {0, 0, 0, 0, 0});
201 CHECK_EQ(0xDEADBEEF, r.Call(last_5_bytes, 0, 6));
202 CheckMemoryEquals(&r.builder(), last_5_bytes, {0, 0, 0, 0, 0});
203
204 r.builder().BlankMemory();
205 memcpy(mem + last_5_bytes, data, 5);
206 CHECK_EQ(0xDEADBEEF, r.Call(0, last_5_bytes, kWasmPageSize));
207 CheckMemoryEquals(&r.builder(), last_5_bytes, {11, 22, 33, 44, 55});
208
209 r.builder().BlankMemory();
210 memcpy(mem + last_5_bytes, data, 5);
211 CHECK_EQ(0xDEADBEEF, r.Call(last_5_bytes, 0, kWasmPageSize));
212 CheckMemoryEquals(&r.builder(), last_5_bytes, {11, 22, 33, 44, 55});
213 }
214
WASM_EXEC_TEST(MemoryCopyOutOfBounds)215 WASM_EXEC_TEST(MemoryCopyOutOfBounds) {
216 WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
217 r.builder().AddMemory(kWasmPageSize);
218 BUILD(
219 r,
220 WASM_MEMORY_COPY(WASM_LOCAL_GET(0), WASM_LOCAL_GET(1), WASM_LOCAL_GET(2)),
221 kExprI32Const, 0);
222
223 // Copy full range is OK.
224 CHECK_EQ(0, r.Call(0, 0, kWasmPageSize));
225
226 // Source range must not be out of bounds.
227 CHECK_EQ(0xDEADBEEF, r.Call(0, 1, kWasmPageSize));
228 CHECK_EQ(0xDEADBEEF, r.Call(0, 1000, kWasmPageSize));
229 CHECK_EQ(0xDEADBEEF, r.Call(0, kWasmPageSize, 1));
230
231 // Destination range must not be out of bounds.
232 CHECK_EQ(0xDEADBEEF, r.Call(1, 0, kWasmPageSize));
233 CHECK_EQ(0xDEADBEEF, r.Call(1000, 0, kWasmPageSize));
234 CHECK_EQ(0xDEADBEEF, r.Call(kWasmPageSize, 0, 1));
235
236 // Copy 0 out-of-bounds fails if target is invalid.
237 CHECK_EQ(0xDEADBEEF, r.Call(kWasmPageSize + 1, 0, 0));
238 CHECK_EQ(0xDEADBEEF, r.Call(0, kWasmPageSize + 1, 0));
239
240 // Make sure bounds aren't checked with 32-bit wrapping.
241 CHECK_EQ(0xDEADBEEF, r.Call(1, 1, 0xFFFFFFFF));
242 }
243
WASM_EXEC_TEST(MemoryFill)244 WASM_EXEC_TEST(MemoryFill) {
245 WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
246 r.builder().AddMemory(kWasmPageSize);
247 BUILD(
248 r,
249 WASM_MEMORY_FILL(WASM_LOCAL_GET(0), WASM_LOCAL_GET(1), WASM_LOCAL_GET(2)),
250 kExprI32Const, 0);
251 CHECK_EQ(0, r.Call(1, 33, 5));
252 CheckMemoryEqualsFollowedByZeroes(&r.builder(), {0, 33, 33, 33, 33, 33});
253
254 CHECK_EQ(0, r.Call(4, 66, 4));
255 CheckMemoryEqualsFollowedByZeroes(&r.builder(),
256 {0, 33, 33, 33, 66, 66, 66, 66});
257
258 // Fill 0 bytes does nothing.
259 CHECK_EQ(0, r.Call(4, 66, 0));
260 CheckMemoryEqualsFollowedByZeroes(&r.builder(),
261 {0, 33, 33, 33, 66, 66, 66, 66});
262
263 // Fill 0 at end of memory region is OK.
264 CHECK_EQ(0, r.Call(kWasmPageSize, 66, 0));
265 }
266
WASM_EXEC_TEST(MemoryFillValueWrapsToByte)267 WASM_EXEC_TEST(MemoryFillValueWrapsToByte) {
268 WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
269 r.builder().AddMemory(kWasmPageSize);
270 BUILD(
271 r,
272 WASM_MEMORY_FILL(WASM_LOCAL_GET(0), WASM_LOCAL_GET(1), WASM_LOCAL_GET(2)),
273 kExprI32Const, 0);
274 CHECK_EQ(0, r.Call(0, 1000, 3));
275 const byte expected = 1000 & 255;
276 CheckMemoryEqualsFollowedByZeroes(&r.builder(),
277 {expected, expected, expected});
278 }
279
WASM_EXEC_TEST(MemoryFillOutOfBoundsData)280 WASM_EXEC_TEST(MemoryFillOutOfBoundsData) {
281 WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
282 r.builder().AddMemory(kWasmPageSize);
283 BUILD(
284 r,
285 WASM_MEMORY_FILL(WASM_LOCAL_GET(0), WASM_LOCAL_GET(1), WASM_LOCAL_GET(2)),
286 kExprI32Const, 0);
287 const byte v = 123;
288 CHECK_EQ(0xDEADBEEF, r.Call(kWasmPageSize - 5, v, 999));
289 CheckMemoryEquals(&r.builder(), kWasmPageSize - 6, {0, 0, 0, 0, 0, 0});
290 }
291
WASM_EXEC_TEST(MemoryFillOutOfBounds)292 WASM_EXEC_TEST(MemoryFillOutOfBounds) {
293 WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
294 r.builder().AddMemory(kWasmPageSize);
295 BUILD(
296 r,
297 WASM_MEMORY_FILL(WASM_LOCAL_GET(0), WASM_LOCAL_GET(1), WASM_LOCAL_GET(2)),
298 kExprI32Const, 0);
299
300 const byte v = 123;
301
302 // Destination range must not be out of bounds.
303 CHECK_EQ(0xDEADBEEF, r.Call(1, v, kWasmPageSize));
304 CHECK_EQ(0xDEADBEEF, r.Call(1000, v, kWasmPageSize));
305 CHECK_EQ(0xDEADBEEF, r.Call(kWasmPageSize, v, 1));
306
307 // Fill 0 out-of-bounds still fails.
308 CHECK_EQ(0xDEADBEEF, r.Call(kWasmPageSize + 1, v, 0));
309
310 // Make sure bounds aren't checked with 32-bit wrapping.
311 CHECK_EQ(0xDEADBEEF, r.Call(1, v, 0xFFFFFFFF));
312 }
313
WASM_EXEC_TEST(DataDropTwice)314 WASM_EXEC_TEST(DataDropTwice) {
315 WasmRunner<uint32_t> r(execution_tier);
316 r.builder().AddMemory(kWasmPageSize);
317 const byte data[] = {0};
318 r.builder().AddPassiveDataSegment(base::ArrayVector(data));
319 BUILD(r, WASM_DATA_DROP(0), kExprI32Const, 0);
320
321 CHECK_EQ(0, r.Call());
322 CHECK_EQ(0, r.Call());
323 }
324
WASM_EXEC_TEST(DataDropThenMemoryInit)325 WASM_EXEC_TEST(DataDropThenMemoryInit) {
326 WasmRunner<uint32_t> r(execution_tier);
327 r.builder().AddMemory(kWasmPageSize);
328 const byte data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
329 r.builder().AddPassiveDataSegment(base::ArrayVector(data));
330 BUILD(r, WASM_DATA_DROP(0),
331 WASM_MEMORY_INIT(0, WASM_I32V_1(0), WASM_I32V_1(1), WASM_I32V_1(2)),
332 kExprI32Const, 0);
333
334 CHECK_EQ(0xDEADBEEF, r.Call());
335 }
336
TestTableCopyInbounds(TestExecutionTier execution_tier,int table_dst,int table_src)337 void TestTableCopyInbounds(TestExecutionTier execution_tier, int table_dst,
338 int table_src) {
339 WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
340 const uint32_t kTableSize = 5;
341 // Add 10 function tables, even though we only test one table.
342 for (int i = 0; i < 10; ++i) {
343 r.builder().AddIndirectFunctionTable(nullptr, kTableSize);
344 }
345 BUILD(r,
346 WASM_TABLE_COPY(table_dst, table_src, WASM_LOCAL_GET(0),
347 WASM_LOCAL_GET(1), WASM_LOCAL_GET(2)),
348 kExprI32Const, 0);
349
350 for (uint32_t i = 0; i <= kTableSize; ++i) {
351 r.CheckCallViaJS(0, 0, 0, i); // nop
352 r.CheckCallViaJS(0, 0, i, kTableSize - i);
353 r.CheckCallViaJS(0, i, 0, kTableSize - i);
354 }
355 }
356
WASM_COMPILED_EXEC_TEST(TableCopyInboundsFrom0To0)357 WASM_COMPILED_EXEC_TEST(TableCopyInboundsFrom0To0) {
358 TestTableCopyInbounds(execution_tier, 0, 0);
359 }
360
WASM_COMPILED_EXEC_TEST(TableCopyInboundsFrom3To0)361 WASM_COMPILED_EXEC_TEST(TableCopyInboundsFrom3To0) {
362 EXPERIMENTAL_FLAG_SCOPE(reftypes);
363 TestTableCopyInbounds(execution_tier, 3, 0);
364 }
365
WASM_COMPILED_EXEC_TEST(TableCopyInboundsFrom5To9)366 WASM_COMPILED_EXEC_TEST(TableCopyInboundsFrom5To9) {
367 EXPERIMENTAL_FLAG_SCOPE(reftypes);
368 TestTableCopyInbounds(execution_tier, 5, 9);
369 }
370
WASM_COMPILED_EXEC_TEST(TableCopyInboundsFrom6To6)371 WASM_COMPILED_EXEC_TEST(TableCopyInboundsFrom6To6) {
372 EXPERIMENTAL_FLAG_SCOPE(reftypes);
373 TestTableCopyInbounds(execution_tier, 6, 6);
374 }
375
376 namespace {
377 template <typename... Args>
CheckTable(Isolate * isolate,Handle<WasmTableObject> table,Args...args)378 void CheckTable(Isolate* isolate, Handle<WasmTableObject> table, Args... args) {
379 uint32_t args_length = static_cast<uint32_t>(sizeof...(args));
380 CHECK_EQ(table->current_length(), args_length);
381 Handle<Object> handles[] = {args...};
382 for (uint32_t i = 0; i < args_length; ++i) {
383 CHECK(WasmTableObject::Get(isolate, table, i).is_identical_to(handles[i]));
384 }
385 }
386
387 template <typename WasmRunner, typename... Args>
CheckTableCall(Isolate * isolate,Handle<WasmTableObject> table,WasmRunner * r,uint32_t function_index,Args...args)388 void CheckTableCall(Isolate* isolate, Handle<WasmTableObject> table,
389 WasmRunner* r, uint32_t function_index, Args... args) {
390 uint32_t args_length = static_cast<uint32_t>(sizeof...(args));
391 CHECK_EQ(table->current_length(), args_length);
392 double expected[] = {args...};
393 for (uint32_t i = 0; i < args_length; ++i) {
394 Handle<Object> buffer[] = {isolate->factory()->NewNumber(i)};
395 r->CheckCallApplyViaJS(expected[i], function_index, buffer, 1);
396 }
397 }
398 } // namespace
399
TestTableInitElems(TestExecutionTier execution_tier,int table_index)400 void TestTableInitElems(TestExecutionTier execution_tier, int table_index) {
401 Isolate* isolate = CcTest::InitIsolateOnce();
402 HandleScope scope(isolate);
403 TestSignatures sigs;
404 WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
405 const uint32_t kTableSize = 5;
406 std::vector<uint32_t> function_indexes;
407 const uint32_t sig_index = r.builder().AddSignature(sigs.i_v());
408
409 for (uint32_t i = 0; i < kTableSize; ++i) {
410 WasmFunctionCompiler& fn = r.NewFunction(sigs.i_v(), "f");
411 BUILD(fn, WASM_I32V_1(i));
412 fn.SetSigIndex(sig_index);
413 function_indexes.push_back(fn.function_index());
414 }
415
416 // Passive element segment has [f0, f1, f2, f3, f4, null].
417 function_indexes.push_back(WasmModuleBuilder::kNullIndex);
418
419 // Add 10 function tables, even though we only test one table.
420 for (int i = 0; i < 10; ++i) {
421 r.builder().AddIndirectFunctionTable(nullptr, kTableSize);
422 }
423 r.builder().AddPassiveElementSegment(function_indexes);
424
425 WasmFunctionCompiler& call = r.NewFunction(sigs.i_i(), "call");
426 BUILD(call,
427 WASM_CALL_INDIRECT_TABLE(table_index, sig_index, WASM_LOCAL_GET(0)));
428 const uint32_t call_index = call.function_index();
429
430 BUILD(r,
431 WASM_TABLE_INIT(table_index, 0, WASM_LOCAL_GET(0), WASM_LOCAL_GET(1),
432 WASM_LOCAL_GET(2)),
433 kExprI32Const, 0);
434
435 auto table =
436 handle(WasmTableObject::cast(
437 r.builder().instance_object()->tables().get(table_index)),
438 isolate);
439 const double null = 0xDEADBEEF;
440
441 CheckTableCall(isolate, table, &r, call_index, null, null, null, null, null);
442
443 // 0 count is ok in bounds, and at end of regions.
444 r.CheckCallViaJS(0, 0, 0, 0);
445 r.CheckCallViaJS(0, kTableSize, 0, 0);
446 r.CheckCallViaJS(0, 0, kTableSize, 0);
447
448 // Test actual writes.
449 r.CheckCallViaJS(0, 0, 0, 1);
450 CheckTableCall(isolate, table, &r, call_index, 0.0, null, null, null, null);
451 r.CheckCallViaJS(0, 0, 0, 2);
452 CheckTableCall(isolate, table, &r, call_index, 0.0, 1.0, null, null, null);
453 r.CheckCallViaJS(0, 0, 0, 3);
454 CheckTableCall(isolate, table, &r, call_index, 0.0, 1.0, 2.0, null, null);
455 r.CheckCallViaJS(0, 3, 0, 2);
456 CheckTableCall(isolate, table, &r, call_index, 0.0, 1.0, 2.0, 0.0, 1.0);
457 r.CheckCallViaJS(0, 3, 1, 2);
458 CheckTableCall(isolate, table, &r, call_index, 0.0, 1.0, 2.0, 1.0, 2.0);
459 r.CheckCallViaJS(0, 3, 2, 2);
460 CheckTableCall(isolate, table, &r, call_index, 0.0, 1.0, 2.0, 2.0, 3.0);
461 r.CheckCallViaJS(0, 3, 3, 2);
462 CheckTableCall(isolate, table, &r, call_index, 0.0, 1.0, 2.0, 3.0, 4.0);
463 }
464
WASM_COMPILED_EXEC_TEST(TableInitElems0)465 WASM_COMPILED_EXEC_TEST(TableInitElems0) {
466 TestTableInitElems(execution_tier, 0);
467 }
WASM_COMPILED_EXEC_TEST(TableInitElems7)468 WASM_COMPILED_EXEC_TEST(TableInitElems7) {
469 EXPERIMENTAL_FLAG_SCOPE(reftypes);
470 TestTableInitElems(execution_tier, 7);
471 }
WASM_COMPILED_EXEC_TEST(TableInitElems9)472 WASM_COMPILED_EXEC_TEST(TableInitElems9) {
473 EXPERIMENTAL_FLAG_SCOPE(reftypes);
474 TestTableInitElems(execution_tier, 9);
475 }
476
TestTableInitOob(TestExecutionTier execution_tier,int table_index)477 void TestTableInitOob(TestExecutionTier execution_tier, int table_index) {
478 Isolate* isolate = CcTest::InitIsolateOnce();
479 HandleScope scope(isolate);
480 TestSignatures sigs;
481 WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
482 const uint32_t kTableSize = 5;
483 std::vector<uint32_t> function_indexes;
484 const uint32_t sig_index = r.builder().AddSignature(sigs.i_v());
485
486 for (uint32_t i = 0; i < kTableSize; ++i) {
487 WasmFunctionCompiler& fn = r.NewFunction(sigs.i_v(), "f");
488 BUILD(fn, WASM_I32V_1(i));
489 fn.SetSigIndex(sig_index);
490 function_indexes.push_back(fn.function_index());
491 }
492
493 for (int i = 0; i < 10; ++i) {
494 r.builder().AddIndirectFunctionTable(nullptr, kTableSize);
495 }
496 r.builder().AddPassiveElementSegment(function_indexes);
497
498 WasmFunctionCompiler& call = r.NewFunction(sigs.i_i(), "call");
499 BUILD(call,
500 WASM_CALL_INDIRECT_TABLE(table_index, sig_index, WASM_LOCAL_GET(0)));
501 const uint32_t call_index = call.function_index();
502
503 BUILD(r,
504 WASM_TABLE_INIT(table_index, 0, WASM_LOCAL_GET(0), WASM_LOCAL_GET(1),
505 WASM_LOCAL_GET(2)),
506 kExprI32Const, 0);
507
508 auto table =
509 handle(WasmTableObject::cast(
510 r.builder().instance_object()->tables().get(table_index)),
511 isolate);
512 const double null = 0xDEADBEEF;
513
514 CheckTableCall(isolate, table, &r, call_index, null, null, null, null, null);
515
516 // Out-of-bounds table.init should not have any effect.
517 r.CheckCallViaJS(0xDEADBEEF, 3, 0, 3);
518 CheckTableCall(isolate, table, &r, call_index, null, null, null, null, null);
519
520 r.CheckCallViaJS(0xDEADBEEF, 0, 3, 3);
521 CheckTableCall(isolate, table, &r, call_index, null, null, null, null, null);
522
523 // 0-count is still oob if target is invalid.
524 r.CheckCallViaJS(0xDEADBEEF, kTableSize + 1, 0, 0);
525 r.CheckCallViaJS(0xDEADBEEF, 0, kTableSize + 1, 0);
526
527 r.CheckCallViaJS(0xDEADBEEF, 0, 0, 6);
528 r.CheckCallViaJS(0xDEADBEEF, 0, 1, 5);
529 r.CheckCallViaJS(0xDEADBEEF, 0, 2, 4);
530 r.CheckCallViaJS(0xDEADBEEF, 0, 3, 3);
531 r.CheckCallViaJS(0xDEADBEEF, 0, 4, 2);
532 r.CheckCallViaJS(0xDEADBEEF, 0, 5, 1);
533
534 r.CheckCallViaJS(0xDEADBEEF, 0, 0, 6);
535 r.CheckCallViaJS(0xDEADBEEF, 1, 0, 5);
536 r.CheckCallViaJS(0xDEADBEEF, 2, 0, 4);
537 r.CheckCallViaJS(0xDEADBEEF, 3, 0, 3);
538 r.CheckCallViaJS(0xDEADBEEF, 4, 0, 2);
539 r.CheckCallViaJS(0xDEADBEEF, 5, 0, 1);
540
541 r.CheckCallViaJS(0xDEADBEEF, 10, 0, 1);
542 r.CheckCallViaJS(0xDEADBEEF, 0, 10, 1);
543 }
544
WASM_COMPILED_EXEC_TEST(TableInitOob0)545 WASM_COMPILED_EXEC_TEST(TableInitOob0) { TestTableInitOob(execution_tier, 0); }
WASM_COMPILED_EXEC_TEST(TableInitOob7)546 WASM_COMPILED_EXEC_TEST(TableInitOob7) {
547 EXPERIMENTAL_FLAG_SCOPE(reftypes);
548 TestTableInitOob(execution_tier, 7);
549 }
WASM_COMPILED_EXEC_TEST(TableInitOob9)550 WASM_COMPILED_EXEC_TEST(TableInitOob9) {
551 EXPERIMENTAL_FLAG_SCOPE(reftypes);
552 TestTableInitOob(execution_tier, 9);
553 }
554
TestTableCopyElems(TestExecutionTier execution_tier,int table_dst,int table_src)555 void TestTableCopyElems(TestExecutionTier execution_tier, int table_dst,
556 int table_src) {
557 Isolate* isolate = CcTest::InitIsolateOnce();
558 HandleScope scope(isolate);
559 TestSignatures sigs;
560 WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
561 const uint32_t kTableSize = 5;
562 uint16_t function_indexes[kTableSize];
563 const uint32_t sig_index = r.builder().AddSignature(sigs.i_v());
564
565 for (uint32_t i = 0; i < kTableSize; ++i) {
566 WasmFunctionCompiler& fn = r.NewFunction(sigs.i_v(), "f");
567 BUILD(fn, WASM_I32V_1(i));
568 fn.SetSigIndex(sig_index);
569 function_indexes[i] = fn.function_index();
570 }
571
572 for (int i = 0; i < 10; ++i) {
573 r.builder().AddIndirectFunctionTable(function_indexes, kTableSize);
574 }
575
576 BUILD(r,
577 WASM_TABLE_COPY(table_dst, table_src, WASM_LOCAL_GET(0),
578 WASM_LOCAL_GET(1), WASM_LOCAL_GET(2)),
579 kExprI32Const, 0);
580
581 r.builder().FreezeSignatureMapAndInitializeWrapperCache();
582
583 auto table =
584 handle(WasmTableObject::cast(
585 r.builder().instance_object()->tables().get(table_dst)),
586 isolate);
587 r.CheckCallViaJS(0, 0, 0, kTableSize);
588 auto f0 = WasmTableObject::Get(isolate, table, 0);
589 auto f1 = WasmTableObject::Get(isolate, table, 1);
590 auto f2 = WasmTableObject::Get(isolate, table, 2);
591 auto f3 = WasmTableObject::Get(isolate, table, 3);
592 auto f4 = WasmTableObject::Get(isolate, table, 4);
593
594 if (table_dst == table_src) {
595 CheckTable(isolate, table, f0, f1, f2, f3, f4);
596 r.CheckCallViaJS(0, 0, 1, 1);
597 CheckTable(isolate, table, f1, f1, f2, f3, f4);
598 r.CheckCallViaJS(0, 0, 1, 2);
599 CheckTable(isolate, table, f1, f2, f2, f3, f4);
600 r.CheckCallViaJS(0, 3, 0, 2);
601 CheckTable(isolate, table, f1, f2, f2, f1, f2);
602 r.CheckCallViaJS(0, 1, 0, 2);
603 CheckTable(isolate, table, f1, f1, f2, f1, f2);
604 } else {
605 CheckTable(isolate, table, f0, f1, f2, f3, f4);
606 r.CheckCallViaJS(0, 0, 1, 1);
607 CheckTable(isolate, table, f1, f1, f2, f3, f4);
608 r.CheckCallViaJS(0, 0, 1, 2);
609 CheckTable(isolate, table, f1, f2, f2, f3, f4);
610 r.CheckCallViaJS(0, 3, 0, 2);
611 CheckTable(isolate, table, f1, f2, f2, f0, f1);
612 r.CheckCallViaJS(0, 1, 0, 2);
613 CheckTable(isolate, table, f1, f0, f1, f0, f1);
614 }
615 }
616
WASM_COMPILED_EXEC_TEST(TableCopyElemsFrom0To0)617 WASM_COMPILED_EXEC_TEST(TableCopyElemsFrom0To0) {
618 TestTableCopyElems(execution_tier, 0, 0);
619 }
620
WASM_COMPILED_EXEC_TEST(TableCopyElemsFrom3To0)621 WASM_COMPILED_EXEC_TEST(TableCopyElemsFrom3To0) {
622 EXPERIMENTAL_FLAG_SCOPE(reftypes);
623 TestTableCopyElems(execution_tier, 3, 0);
624 }
625
WASM_COMPILED_EXEC_TEST(TableCopyElemsFrom5To9)626 WASM_COMPILED_EXEC_TEST(TableCopyElemsFrom5To9) {
627 EXPERIMENTAL_FLAG_SCOPE(reftypes);
628 TestTableCopyElems(execution_tier, 5, 9);
629 }
630
WASM_COMPILED_EXEC_TEST(TableCopyElemsFrom6To6)631 WASM_COMPILED_EXEC_TEST(TableCopyElemsFrom6To6) {
632 EXPERIMENTAL_FLAG_SCOPE(reftypes);
633 TestTableCopyElems(execution_tier, 6, 6);
634 }
635
TestTableCopyCalls(TestExecutionTier execution_tier,int table_dst,int table_src)636 void TestTableCopyCalls(TestExecutionTier execution_tier, int table_dst,
637 int table_src) {
638 Isolate* isolate = CcTest::InitIsolateOnce();
639 HandleScope scope(isolate);
640 TestSignatures sigs;
641 WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
642 const uint32_t kTableSize = 5;
643 uint16_t function_indexes[kTableSize];
644 const uint32_t sig_index = r.builder().AddSignature(sigs.i_v());
645
646 for (uint32_t i = 0; i < kTableSize; ++i) {
647 WasmFunctionCompiler& fn = r.NewFunction(sigs.i_v(), "f");
648 BUILD(fn, WASM_I32V_1(i));
649 fn.SetSigIndex(sig_index);
650 function_indexes[i] = fn.function_index();
651 }
652
653 for (int i = 0; i < 10; ++i) {
654 r.builder().AddIndirectFunctionTable(function_indexes, kTableSize);
655 }
656
657 WasmFunctionCompiler& call = r.NewFunction(sigs.i_i(), "call");
658 BUILD(call,
659 WASM_CALL_INDIRECT_TABLE(table_dst, sig_index, WASM_LOCAL_GET(0)));
660 const uint32_t call_index = call.function_index();
661
662 BUILD(r,
663 WASM_TABLE_COPY(table_dst, table_src, WASM_LOCAL_GET(0),
664 WASM_LOCAL_GET(1), WASM_LOCAL_GET(2)),
665 kExprI32Const, 0);
666
667 auto table =
668 handle(WasmTableObject::cast(
669 r.builder().instance_object()->tables().get(table_dst)),
670 isolate);
671
672 if (table_dst == table_src) {
673 CheckTableCall(isolate, table, &r, call_index, 0.0, 1.0, 2.0, 3.0, 4.0);
674 r.CheckCallViaJS(0, 0, 1, 1);
675 CheckTableCall(isolate, table, &r, call_index, 1.0, 1.0, 2.0, 3.0, 4.0);
676 r.CheckCallViaJS(0, 0, 1, 2);
677 CheckTableCall(isolate, table, &r, call_index, 1.0, 2.0, 2.0, 3.0, 4.0);
678 r.CheckCallViaJS(0, 3, 0, 2);
679 CheckTableCall(isolate, table, &r, call_index, 1.0, 2.0, 2.0, 1.0, 2.0);
680 } else {
681 CheckTableCall(isolate, table, &r, call_index, 0.0, 1.0, 2.0, 3.0, 4.0);
682 r.CheckCallViaJS(0, 0, 1, 1);
683 CheckTableCall(isolate, table, &r, call_index, 1.0, 1.0, 2.0, 3.0, 4.0);
684 r.CheckCallViaJS(0, 0, 1, 2);
685 CheckTableCall(isolate, table, &r, call_index, 1.0, 2.0, 2.0, 3.0, 4.0);
686 r.CheckCallViaJS(0, 3, 0, 2);
687 CheckTableCall(isolate, table, &r, call_index, 1.0, 2.0, 2.0, 0.0, 1.0);
688 }
689 }
690
WASM_COMPILED_EXEC_TEST(TableCopyCallsTo0From0)691 WASM_COMPILED_EXEC_TEST(TableCopyCallsTo0From0) {
692 TestTableCopyCalls(execution_tier, 0, 0);
693 }
694
WASM_COMPILED_EXEC_TEST(TableCopyCallsTo3From0)695 WASM_COMPILED_EXEC_TEST(TableCopyCallsTo3From0) {
696 EXPERIMENTAL_FLAG_SCOPE(reftypes);
697 TestTableCopyCalls(execution_tier, 3, 0);
698 }
699
WASM_COMPILED_EXEC_TEST(TableCopyCallsTo5From9)700 WASM_COMPILED_EXEC_TEST(TableCopyCallsTo5From9) {
701 EXPERIMENTAL_FLAG_SCOPE(reftypes);
702 TestTableCopyCalls(execution_tier, 5, 9);
703 }
704
WASM_COMPILED_EXEC_TEST(TableCopyCallsTo6From6)705 WASM_COMPILED_EXEC_TEST(TableCopyCallsTo6From6) {
706 EXPERIMENTAL_FLAG_SCOPE(reftypes);
707 TestTableCopyCalls(execution_tier, 6, 6);
708 }
709
TestTableCopyOobWrites(TestExecutionTier execution_tier,int table_dst,int table_src)710 void TestTableCopyOobWrites(TestExecutionTier execution_tier, int table_dst,
711 int table_src) {
712 Isolate* isolate = CcTest::InitIsolateOnce();
713 HandleScope scope(isolate);
714 TestSignatures sigs;
715 WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
716 const uint32_t kTableSize = 5;
717 uint16_t function_indexes[kTableSize];
718 const uint32_t sig_index = r.builder().AddSignature(sigs.i_v());
719
720 for (uint32_t i = 0; i < kTableSize; ++i) {
721 WasmFunctionCompiler& fn = r.NewFunction(sigs.i_v(), "f");
722 BUILD(fn, WASM_I32V_1(i));
723 fn.SetSigIndex(sig_index);
724 function_indexes[i] = fn.function_index();
725 }
726
727 for (int i = 0; i < 10; ++i) {
728 r.builder().AddIndirectFunctionTable(function_indexes, kTableSize);
729 }
730
731 BUILD(r,
732 WASM_TABLE_COPY(table_dst, table_src, WASM_LOCAL_GET(0),
733 WASM_LOCAL_GET(1), WASM_LOCAL_GET(2)),
734 kExprI32Const, 0);
735
736 r.builder().FreezeSignatureMapAndInitializeWrapperCache();
737
738 auto table =
739 handle(WasmTableObject::cast(
740 r.builder().instance_object()->tables().get(table_dst)),
741 isolate);
742 // Fill the dst table with values from the src table, to make checks easier.
743 r.CheckCallViaJS(0, 0, 0, kTableSize);
744 auto f0 = WasmTableObject::Get(isolate, table, 0);
745 auto f1 = WasmTableObject::Get(isolate, table, 1);
746 auto f2 = WasmTableObject::Get(isolate, table, 2);
747 auto f3 = WasmTableObject::Get(isolate, table, 3);
748 auto f4 = WasmTableObject::Get(isolate, table, 4);
749
750 CheckTable(isolate, table, f0, f1, f2, f3, f4);
751
752 // Failing table.copy should not have any effect.
753 r.CheckCallViaJS(0xDEADBEEF, 3, 0, 3);
754 CheckTable(isolate, table, f0, f1, f2, f3, f4);
755
756 r.CheckCallViaJS(0xDEADBEEF, 0, 4, 2);
757 CheckTable(isolate, table, f0, f1, f2, f3, f4);
758
759 r.CheckCallViaJS(0xDEADBEEF, 3, 0, 99);
760 CheckTable(isolate, table, f0, f1, f2, f3, f4);
761
762 r.CheckCallViaJS(0xDEADBEEF, 0, 1, 99);
763 CheckTable(isolate, table, f0, f1, f2, f3, f4);
764 }
765
WASM_COMPILED_EXEC_TEST(TableCopyOobWritesFrom0To0)766 WASM_COMPILED_EXEC_TEST(TableCopyOobWritesFrom0To0) {
767 TestTableCopyOobWrites(execution_tier, 0, 0);
768 }
769
WASM_COMPILED_EXEC_TEST(TableCopyOobWritesFrom3To0)770 WASM_COMPILED_EXEC_TEST(TableCopyOobWritesFrom3To0) {
771 EXPERIMENTAL_FLAG_SCOPE(reftypes);
772 TestTableCopyOobWrites(execution_tier, 3, 0);
773 }
774
WASM_COMPILED_EXEC_TEST(TableCopyOobWritesFrom5To9)775 WASM_COMPILED_EXEC_TEST(TableCopyOobWritesFrom5To9) {
776 EXPERIMENTAL_FLAG_SCOPE(reftypes);
777 TestTableCopyOobWrites(execution_tier, 5, 9);
778 }
779
WASM_COMPILED_EXEC_TEST(TableCopyOobWritesFrom6To6)780 WASM_COMPILED_EXEC_TEST(TableCopyOobWritesFrom6To6) {
781 EXPERIMENTAL_FLAG_SCOPE(reftypes);
782 TestTableCopyOobWrites(execution_tier, 6, 6);
783 }
784
TestTableCopyOob1(TestExecutionTier execution_tier,int table_dst,int table_src)785 void TestTableCopyOob1(TestExecutionTier execution_tier, int table_dst,
786 int table_src) {
787 WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
788 const uint32_t kTableSize = 5;
789
790 for (int i = 0; i < 10; ++i) {
791 r.builder().AddIndirectFunctionTable(nullptr, kTableSize);
792 }
793
794 BUILD(r,
795 WASM_TABLE_COPY(table_dst, table_src, WASM_LOCAL_GET(0),
796 WASM_LOCAL_GET(1), WASM_LOCAL_GET(2)),
797 kExprI32Const, 0);
798
799 r.CheckCallViaJS(0, 0, 0, 1); // nop
800 r.CheckCallViaJS(0, 0, 0, kTableSize); // nop
801 r.CheckCallViaJS(0xDEADBEEF, 0, 0, kTableSize + 1);
802 r.CheckCallViaJS(0xDEADBEEF, 1, 0, kTableSize);
803 r.CheckCallViaJS(0xDEADBEEF, 0, 1, kTableSize);
804
805 {
806 const uint32_t big = 1000000;
807 r.CheckCallViaJS(0xDEADBEEF, big, 0, 0);
808 r.CheckCallViaJS(0xDEADBEEF, 0, big, 0);
809 }
810
811 for (uint32_t big = 4294967295; big > 1000; big >>= 1) {
812 r.CheckCallViaJS(0xDEADBEEF, big, 0, 1);
813 r.CheckCallViaJS(0xDEADBEEF, 0, big, 1);
814 r.CheckCallViaJS(0xDEADBEEF, 0, 0, big);
815 }
816
817 for (uint32_t big = -1000; big != 0; big <<= 1) {
818 r.CheckCallViaJS(0xDEADBEEF, big, 0, 1);
819 r.CheckCallViaJS(0xDEADBEEF, 0, big, 1);
820 r.CheckCallViaJS(0xDEADBEEF, 0, 0, big);
821 }
822 }
823
WASM_COMPILED_EXEC_TEST(TableCopyOob1From0To0)824 WASM_COMPILED_EXEC_TEST(TableCopyOob1From0To0) {
825 TestTableCopyOob1(execution_tier, 0, 0);
826 }
827
WASM_COMPILED_EXEC_TEST(TableCopyOob1From3To0)828 WASM_COMPILED_EXEC_TEST(TableCopyOob1From3To0) {
829 EXPERIMENTAL_FLAG_SCOPE(reftypes);
830 TestTableCopyOob1(execution_tier, 3, 0);
831 }
832
WASM_COMPILED_EXEC_TEST(TableCopyOob1From5To9)833 WASM_COMPILED_EXEC_TEST(TableCopyOob1From5To9) {
834 EXPERIMENTAL_FLAG_SCOPE(reftypes);
835 TestTableCopyOob1(execution_tier, 5, 9);
836 }
837
WASM_COMPILED_EXEC_TEST(TableCopyOob1From6To6)838 WASM_COMPILED_EXEC_TEST(TableCopyOob1From6To6) {
839 EXPERIMENTAL_FLAG_SCOPE(reftypes);
840 TestTableCopyOob1(execution_tier, 6, 6);
841 }
842
WASM_COMPILED_EXEC_TEST(ElemDropTwice)843 WASM_COMPILED_EXEC_TEST(ElemDropTwice) {
844 WasmRunner<uint32_t> r(execution_tier);
845 r.builder().AddIndirectFunctionTable(nullptr, 1);
846 r.builder().AddPassiveElementSegment({});
847 BUILD(r, WASM_ELEM_DROP(0), kExprI32Const, 0);
848
849 r.CheckCallViaJS(0);
850 r.CheckCallViaJS(0);
851 }
852
WASM_COMPILED_EXEC_TEST(ElemDropThenTableInit)853 WASM_COMPILED_EXEC_TEST(ElemDropThenTableInit) {
854 WasmRunner<uint32_t, uint32_t> r(execution_tier);
855 r.builder().AddIndirectFunctionTable(nullptr, 1);
856 r.builder().AddPassiveElementSegment({});
857 BUILD(
858 r, WASM_ELEM_DROP(0),
859 WASM_TABLE_INIT(0, 0, WASM_I32V_1(0), WASM_I32V_1(0), WASM_LOCAL_GET(0)),
860 kExprI32Const, 0);
861
862 r.CheckCallViaJS(0, 0);
863 r.CheckCallViaJS(0xDEADBEEF, 1);
864 }
865
866 } // namespace test_run_wasm_bulk_memory
867 } // namespace wasm
868 } // namespace internal
869 } // namespace v8
870