Line data Source code
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 "test/cctest/cctest.h"
6 : #include "test/cctest/wasm/wasm-run-utils.h"
7 : #include "test/common/wasm/test-signatures.h"
8 : #include "test/common/wasm/wasm-macro-gen.h"
9 :
10 : namespace v8 {
11 : namespace internal {
12 : namespace wasm {
13 : namespace test_run_wasm_bulk_memory {
14 :
15 : namespace {
16 204 : void CheckMemoryEquals(TestingModuleBuilder& builder, size_t index,
17 : const std::vector<byte>& expected) {
18 : const byte* mem_start = builder.raw_mem_start<byte>();
19 : const byte* mem_end = builder.raw_mem_end<byte>();
20 204 : size_t mem_size = mem_end - mem_start;
21 204 : CHECK_LE(index, mem_size);
22 204 : CHECK_LE(index + expected.size(), mem_size);
23 4044 : for (size_t i = 0; i < expected.size(); ++i) {
24 3840 : CHECK_EQ(expected[i], mem_start[index + i]);
25 : }
26 204 : }
27 :
28 156 : void CheckMemoryEqualsZero(TestingModuleBuilder& builder, size_t index,
29 : size_t length) {
30 : const byte* mem_start = builder.raw_mem_start<byte>();
31 : const byte* mem_end = builder.raw_mem_end<byte>();
32 156 : size_t mem_size = mem_end - mem_start;
33 156 : CHECK_LE(index, mem_size);
34 156 : CHECK_LE(index + length, mem_size);
35 20444172 : for (size_t i = 0; i < length; ++i) {
36 20444016 : CHECK_EQ(0, mem_start[index + i]);
37 : }
38 156 : }
39 :
40 144 : void CheckMemoryEqualsFollowedByZeroes(TestingModuleBuilder& builder,
41 : const std::vector<byte>& expected) {
42 144 : CheckMemoryEquals(builder, 0, expected);
43 288 : CheckMemoryEqualsZero(builder, expected.size(),
44 144 : builder.mem_size() - expected.size());
45 144 : }
46 : } // namespace
47 :
48 26663 : WASM_EXEC_TEST(MemoryInit) {
49 : EXPERIMENTAL_FLAG_SCOPE(bulk_memory);
50 24 : WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
51 12 : r.builder().AddMemory(kWasmPageSize);
52 12 : const byte data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
53 12 : r.builder().AddPassiveDataSegment(Vector<const byte>(data));
54 12 : BUILD(r,
55 : WASM_MEMORY_INIT(0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1),
56 : WASM_GET_LOCAL(2)),
57 : kExprI32Const, 0);
58 :
59 : // All zeroes.
60 12 : CheckMemoryEqualsZero(r.builder(), 0, kWasmPageSize);
61 :
62 : // Copy all bytes from data segment 0, to memory at [10, 20).
63 12 : CHECK_EQ(0, r.Call(10, 0, 10));
64 12 : CheckMemoryEqualsFollowedByZeroes(
65 : r.builder(),
66 12 : {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 12 : CHECK_EQ(0, r.Call(0, 5, 5));
70 12 : CheckMemoryEqualsFollowedByZeroes(
71 : r.builder(),
72 12 : {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 12 : CHECK_EQ(0, r.Call(10, 1, 0));
76 12 : CheckMemoryEqualsFollowedByZeroes(
77 : r.builder(),
78 12 : {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 12 : CHECK_EQ(0, r.Call(kWasmPageSize, 0, 0));
82 12 : CHECK_EQ(0, r.Call(0, sizeof(data), 0));
83 12 : }
84 :
85 26663 : WASM_EXEC_TEST(MemoryInitOutOfBoundsData) {
86 : EXPERIMENTAL_FLAG_SCOPE(bulk_memory);
87 24 : WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
88 12 : r.builder().AddMemory(kWasmPageSize);
89 12 : const byte data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
90 12 : r.builder().AddPassiveDataSegment(Vector<const byte>(data));
91 12 : BUILD(r,
92 : WASM_MEMORY_INIT(0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1),
93 : WASM_GET_LOCAL(2)),
94 : kExprI32Const, 0);
95 :
96 : const uint32_t last_5_bytes = kWasmPageSize - 5;
97 :
98 : // Write all values up to the out-of-bounds write.
99 12 : CHECK_EQ(0xDEADBEEF, r.Call(kWasmPageSize - 5, 0, 6));
100 24 : CheckMemoryEquals(r.builder(), last_5_bytes, {0, 1, 2, 3, 4});
101 :
102 : // Write all values up to the out-of-bounds read.
103 : r.builder().BlankMemory();
104 12 : CHECK_EQ(0xDEADBEEF, r.Call(0, 5, 6));
105 24 : CheckMemoryEqualsFollowedByZeroes(r.builder(), {5, 6, 7, 8, 9});
106 12 : }
107 :
108 26663 : WASM_EXEC_TEST(MemoryInitOutOfBounds) {
109 : EXPERIMENTAL_FLAG_SCOPE(bulk_memory);
110 24 : WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
111 12 : r.builder().AddMemory(kWasmPageSize);
112 12 : const byte data[kWasmPageSize] = {};
113 12 : r.builder().AddPassiveDataSegment(Vector<const byte>(data));
114 12 : BUILD(r,
115 : WASM_MEMORY_INIT(0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1),
116 : WASM_GET_LOCAL(2)),
117 : kExprI32Const, 0);
118 :
119 : // OK, copy the full data segment to memory.
120 12 : r.Call(0, 0, kWasmPageSize);
121 :
122 : // Source range must not be out of bounds.
123 12 : CHECK_EQ(0xDEADBEEF, r.Call(0, 1, kWasmPageSize));
124 12 : CHECK_EQ(0xDEADBEEF, r.Call(0, 1000, kWasmPageSize));
125 12 : CHECK_EQ(0xDEADBEEF, r.Call(0, kWasmPageSize, 1));
126 :
127 : // Destination range must not be out of bounds.
128 12 : CHECK_EQ(0xDEADBEEF, r.Call(1, 0, kWasmPageSize));
129 12 : CHECK_EQ(0xDEADBEEF, r.Call(1000, 0, kWasmPageSize));
130 12 : CHECK_EQ(0xDEADBEEF, r.Call(kWasmPageSize, 0, 1));
131 :
132 : // Copy 0 out-of-bounds fails.
133 12 : CHECK_EQ(0xDEADBEEF, r.Call(kWasmPageSize + 1, 0, 0));
134 12 : CHECK_EQ(0xDEADBEEF, r.Call(0, kWasmPageSize + 1, 0));
135 :
136 : // Make sure bounds aren't checked with 32-bit wrapping.
137 12 : CHECK_EQ(0xDEADBEEF, r.Call(1, 1, 0xFFFFFFFF));
138 12 : }
139 :
140 26663 : WASM_EXEC_TEST(MemoryCopy) {
141 : EXPERIMENTAL_FLAG_SCOPE(bulk_memory);
142 24 : WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
143 12 : byte* mem = r.builder().AddMemory(kWasmPageSize);
144 12 : BUILD(
145 : r,
146 : WASM_MEMORY_COPY(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), WASM_GET_LOCAL(2)),
147 : kExprI32Const, 0);
148 :
149 12 : const byte initial[] = {0, 11, 22, 33, 44, 55, 66, 77};
150 : memcpy(mem, initial, sizeof(initial));
151 :
152 : // Copy from [1, 8] to [10, 16].
153 12 : CHECK_EQ(0, r.Call(10, 1, 8));
154 12 : CheckMemoryEqualsFollowedByZeroes(
155 : r.builder(),
156 12 : {0, 11, 22, 33, 44, 55, 66, 77, 0, 0, 11, 22, 33, 44, 55, 66, 77});
157 :
158 : // Copy 0 bytes does nothing.
159 12 : CHECK_EQ(0, r.Call(10, 2, 0));
160 12 : CheckMemoryEqualsFollowedByZeroes(
161 : r.builder(),
162 12 : {0, 11, 22, 33, 44, 55, 66, 77, 0, 0, 11, 22, 33, 44, 55, 66, 77});
163 :
164 : // Copy 0 at end of memory region is OK.
165 12 : CHECK_EQ(0, r.Call(kWasmPageSize, 0, 0));
166 12 : CHECK_EQ(0, r.Call(0, kWasmPageSize, 0));
167 12 : }
168 :
169 26663 : WASM_EXEC_TEST(MemoryCopyOverlapping) {
170 : EXPERIMENTAL_FLAG_SCOPE(bulk_memory);
171 24 : WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
172 12 : byte* mem = r.builder().AddMemory(kWasmPageSize);
173 12 : BUILD(
174 : r,
175 : WASM_MEMORY_COPY(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), WASM_GET_LOCAL(2)),
176 : kExprI32Const, 0);
177 :
178 12 : const byte initial[] = {10, 20, 30};
179 : memcpy(mem, initial, sizeof(initial));
180 :
181 : // Copy from [0, 3] -> [2, 5]. The copy must not overwrite 30 before copying
182 : // it (i.e. cannot copy forward in this case).
183 12 : CHECK_EQ(0, r.Call(2, 0, 3));
184 24 : CheckMemoryEqualsFollowedByZeroes(r.builder(), {10, 20, 10, 20, 30});
185 :
186 : // Copy from [2, 5] -> [0, 3]. The copy must not write the first 10 (i.e.
187 : // cannot copy backward in this case).
188 12 : CHECK_EQ(0, r.Call(0, 2, 3));
189 24 : CheckMemoryEqualsFollowedByZeroes(r.builder(), {10, 20, 30, 20, 30});
190 12 : }
191 :
192 26663 : WASM_EXEC_TEST(MemoryCopyOutOfBoundsData) {
193 : EXPERIMENTAL_FLAG_SCOPE(bulk_memory);
194 24 : WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
195 12 : byte* mem = r.builder().AddMemory(kWasmPageSize);
196 12 : BUILD(
197 : r,
198 : WASM_MEMORY_COPY(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), WASM_GET_LOCAL(2)),
199 : kExprI32Const, 0);
200 :
201 12 : const byte data[] = {11, 22, 33, 44, 55, 66, 77, 88};
202 : memcpy(mem, data, sizeof(data));
203 :
204 : const uint32_t last_5_bytes = kWasmPageSize - 5;
205 :
206 : // Write all values up to the out-of-bounds access.
207 12 : CHECK_EQ(0xDEADBEEF, r.Call(last_5_bytes, 0, 6));
208 24 : CheckMemoryEquals(r.builder(), last_5_bytes, {11, 22, 33, 44, 55});
209 :
210 : // Copy overlapping with destination < source. Copy will happen forwards, up
211 : // to the out-of-bounds access.
212 : r.builder().BlankMemory();
213 12 : memcpy(mem + last_5_bytes, data, 5);
214 12 : CHECK_EQ(0xDEADBEEF, r.Call(0, last_5_bytes, kWasmPageSize));
215 24 : CheckMemoryEquals(r.builder(), 0, {11, 22, 33, 44, 55});
216 :
217 : // Copy overlapping with source < destination. Copy would happen backwards,
218 : // but the first byte to copy is out-of-bounds, so no data should be written.
219 : r.builder().BlankMemory();
220 : memcpy(mem, data, 5);
221 12 : CHECK_EQ(0xDEADBEEF, r.Call(last_5_bytes, 0, kWasmPageSize));
222 24 : CheckMemoryEquals(r.builder(), last_5_bytes, {0, 0, 0, 0, 0});
223 12 : }
224 :
225 26663 : WASM_EXEC_TEST(MemoryCopyOutOfBounds) {
226 : EXPERIMENTAL_FLAG_SCOPE(bulk_memory);
227 24 : WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
228 12 : r.builder().AddMemory(kWasmPageSize);
229 12 : BUILD(
230 : r,
231 : WASM_MEMORY_COPY(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), WASM_GET_LOCAL(2)),
232 : kExprI32Const, 0);
233 :
234 : // Copy full range is OK.
235 12 : CHECK_EQ(0, r.Call(0, 0, kWasmPageSize));
236 :
237 : // Source range must not be out of bounds.
238 12 : CHECK_EQ(0xDEADBEEF, r.Call(0, 1, kWasmPageSize));
239 12 : CHECK_EQ(0xDEADBEEF, r.Call(0, 1000, kWasmPageSize));
240 12 : CHECK_EQ(0xDEADBEEF, r.Call(0, kWasmPageSize, 1));
241 :
242 : // Destination range must not be out of bounds.
243 12 : CHECK_EQ(0xDEADBEEF, r.Call(1, 0, kWasmPageSize));
244 12 : CHECK_EQ(0xDEADBEEF, r.Call(1000, 0, kWasmPageSize));
245 12 : CHECK_EQ(0xDEADBEEF, r.Call(kWasmPageSize, 0, 1));
246 :
247 : // Copy 0 out-of-bounds fails.
248 12 : CHECK_EQ(0xDEADBEEF, r.Call(kWasmPageSize + 1, 0, 0));
249 12 : CHECK_EQ(0xDEADBEEF, r.Call(0, kWasmPageSize + 1, 0));
250 :
251 : // Make sure bounds aren't checked with 32-bit wrapping.
252 12 : CHECK_EQ(0xDEADBEEF, r.Call(1, 1, 0xFFFFFFFF));
253 12 : }
254 :
255 26663 : WASM_EXEC_TEST(MemoryFill) {
256 : EXPERIMENTAL_FLAG_SCOPE(bulk_memory);
257 24 : WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
258 12 : r.builder().AddMemory(kWasmPageSize);
259 12 : BUILD(
260 : r,
261 : WASM_MEMORY_FILL(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), WASM_GET_LOCAL(2)),
262 : kExprI32Const, 0);
263 12 : CHECK_EQ(0, r.Call(1, 33, 5));
264 24 : CheckMemoryEqualsFollowedByZeroes(r.builder(), {0, 33, 33, 33, 33, 33});
265 :
266 12 : CHECK_EQ(0, r.Call(4, 66, 4));
267 12 : CheckMemoryEqualsFollowedByZeroes(r.builder(),
268 12 : {0, 33, 33, 33, 66, 66, 66, 66});
269 :
270 : // Fill 0 bytes does nothing.
271 12 : CHECK_EQ(0, r.Call(4, 66, 0));
272 12 : CheckMemoryEqualsFollowedByZeroes(r.builder(),
273 12 : {0, 33, 33, 33, 66, 66, 66, 66});
274 :
275 : // Fill 0 at end of memory region is OK.
276 12 : CHECK_EQ(0, r.Call(kWasmPageSize, 66, 0));
277 12 : }
278 :
279 26663 : WASM_EXEC_TEST(MemoryFillValueWrapsToByte) {
280 : EXPERIMENTAL_FLAG_SCOPE(bulk_memory);
281 24 : WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
282 12 : r.builder().AddMemory(kWasmPageSize);
283 12 : BUILD(
284 : r,
285 : WASM_MEMORY_FILL(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), WASM_GET_LOCAL(2)),
286 : kExprI32Const, 0);
287 12 : CHECK_EQ(0, r.Call(0, 1000, 3));
288 : const byte expected = 1000 & 255;
289 12 : CheckMemoryEqualsFollowedByZeroes(r.builder(),
290 12 : {expected, expected, expected});
291 12 : }
292 :
293 26663 : WASM_EXEC_TEST(MemoryFillOutOfBoundsData) {
294 : EXPERIMENTAL_FLAG_SCOPE(bulk_memory);
295 24 : WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
296 12 : r.builder().AddMemory(kWasmPageSize);
297 12 : BUILD(
298 : r,
299 : WASM_MEMORY_FILL(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), WASM_GET_LOCAL(2)),
300 : kExprI32Const, 0);
301 : const byte v = 123;
302 12 : CHECK_EQ(0xDEADBEEF, r.Call(kWasmPageSize - 5, v, 999));
303 24 : CheckMemoryEquals(r.builder(), kWasmPageSize - 6, {0, v, v, v, v, v});
304 12 : }
305 :
306 26663 : WASM_EXEC_TEST(MemoryFillOutOfBounds) {
307 : EXPERIMENTAL_FLAG_SCOPE(bulk_memory);
308 24 : WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
309 12 : r.builder().AddMemory(kWasmPageSize);
310 12 : BUILD(
311 : r,
312 : WASM_MEMORY_FILL(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), WASM_GET_LOCAL(2)),
313 : kExprI32Const, 0);
314 :
315 : const byte v = 123;
316 :
317 : // Destination range must not be out of bounds.
318 12 : CHECK_EQ(0xDEADBEEF, r.Call(1, v, kWasmPageSize));
319 12 : CHECK_EQ(0xDEADBEEF, r.Call(1000, v, kWasmPageSize));
320 12 : CHECK_EQ(0xDEADBEEF, r.Call(kWasmPageSize, v, 1));
321 :
322 : // Fill 0 out-of-bounds fails.
323 12 : CHECK_EQ(0xDEADBEEF, r.Call(kWasmPageSize + 1, v, 0));
324 :
325 : // Make sure bounds aren't checked with 32-bit wrapping.
326 12 : CHECK_EQ(0xDEADBEEF, r.Call(1, v, 0xFFFFFFFF));
327 12 : }
328 :
329 26663 : WASM_EXEC_TEST(DataDropTwice) {
330 : EXPERIMENTAL_FLAG_SCOPE(bulk_memory);
331 24 : WasmRunner<uint32_t> r(execution_tier);
332 12 : r.builder().AddMemory(kWasmPageSize);
333 12 : const byte data[] = {0};
334 12 : r.builder().AddPassiveDataSegment(Vector<const byte>(data));
335 12 : BUILD(r, WASM_DATA_DROP(0), kExprI32Const, 0);
336 :
337 12 : CHECK_EQ(0, r.Call());
338 12 : CHECK_EQ(0xDEADBEEF, r.Call());
339 12 : }
340 :
341 26663 : WASM_EXEC_TEST(DataDropThenMemoryInit) {
342 : EXPERIMENTAL_FLAG_SCOPE(bulk_memory);
343 24 : WasmRunner<uint32_t> r(execution_tier);
344 12 : r.builder().AddMemory(kWasmPageSize);
345 12 : const byte data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
346 12 : r.builder().AddPassiveDataSegment(Vector<const byte>(data));
347 12 : BUILD(r, WASM_DATA_DROP(0),
348 : WASM_MEMORY_INIT(0, WASM_I32V_1(0), WASM_I32V_1(1), WASM_I32V_1(2)),
349 : kExprI32Const, 0);
350 :
351 12 : CHECK_EQ(0xDEADBEEF, r.Call());
352 12 : }
353 :
354 26663 : WASM_EXEC_TEST(TableCopyInbounds) {
355 : EXPERIMENTAL_FLAG_SCOPE(bulk_memory);
356 24 : WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
357 : const uint32_t kTableSize = 5;
358 12 : r.builder().AddIndirectFunctionTable(nullptr, kTableSize);
359 12 : BUILD(
360 : r,
361 : WASM_TABLE_COPY(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), WASM_GET_LOCAL(2)),
362 : kExprI32Const, 0);
363 :
364 156 : for (uint32_t i = 0; i <= kTableSize; ++i) {
365 72 : r.CheckCallViaJS(0, 0, 0, i); // nop
366 72 : r.CheckCallViaJS(0, 0, i, kTableSize - i);
367 72 : r.CheckCallViaJS(0, i, 0, kTableSize - i);
368 : }
369 12 : }
370 :
371 : namespace {
372 : template <typename... Args>
373 120 : void CheckTable(Isolate* isolate, Handle<WasmTableObject> table, Args... args) {
374 : uint32_t args_length = static_cast<uint32_t>(sizeof...(args));
375 120 : CHECK_EQ(table->current_length(), args_length);
376 120 : Handle<Object> handles[] = {args...};
377 1320 : for (uint32_t i = 0; i < args_length; ++i) {
378 1200 : CHECK(WasmTableObject::Get(isolate, table, i).is_identical_to(handles[i]));
379 : }
380 120 : }
381 :
382 : template <typename WasmRunner, typename... Args>
383 180 : void CheckTableCall(Isolate* isolate, Handle<WasmTableObject> table,
384 : WasmRunner& r, uint32_t function_index, Args... args) {
385 : uint32_t args_length = static_cast<uint32_t>(sizeof...(args));
386 180 : CHECK_EQ(table->current_length(), args_length);
387 180 : double expected[] = {args...};
388 1980 : for (uint32_t i = 0; i < args_length; ++i) {
389 900 : Handle<Object> buffer[] = {isolate->factory()->NewNumber(i)};
390 900 : r.CheckCallApplyViaJS(expected[i], function_index, buffer, 1);
391 : }
392 180 : }
393 : } // namespace
394 :
395 26663 : WASM_EXEC_TEST(TableInitElems) {
396 : EXPERIMENTAL_FLAG_SCOPE(bulk_memory);
397 : Isolate* isolate = CcTest::InitIsolateOnce();
398 : HandleScope scope(isolate);
399 12 : TestSignatures sigs;
400 24 : WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
401 : const uint32_t kTableSize = 5;
402 : std::vector<uint32_t> function_indexes;
403 12 : const uint32_t sig_index = r.builder().AddSignature(sigs.i_v());
404 :
405 132 : for (uint32_t i = 0; i < kTableSize; ++i) {
406 60 : WasmFunctionCompiler& fn = r.NewFunction(sigs.i_v(), "f");
407 60 : BUILD(fn, WASM_I32V_1(i));
408 : fn.SetSigIndex(sig_index);
409 120 : function_indexes.push_back(fn.function_index());
410 : }
411 :
412 : // Passive element segment has [f0, f1, f2, f3, f4, null].
413 12 : function_indexes.push_back(WasmElemSegment::kNullIndex);
414 :
415 12 : r.builder().AddIndirectFunctionTable(nullptr, kTableSize);
416 12 : r.builder().AddPassiveElementSegment(function_indexes);
417 :
418 12 : WasmFunctionCompiler& call = r.NewFunction(sigs.i_i(), "call");
419 12 : BUILD(call, WASM_CALL_INDIRECT0(sig_index, WASM_GET_LOCAL(0)));
420 : const uint32_t call_index = call.function_index();
421 :
422 12 : BUILD(r,
423 : WASM_TABLE_INIT(0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1),
424 : WASM_GET_LOCAL(2)),
425 : kExprI32Const, 0);
426 :
427 : auto table = handle(
428 : WasmTableObject::cast(r.builder().instance_object()->tables().get(0)),
429 12 : isolate);
430 : const double null = 0xDEADBEEF;
431 :
432 12 : CheckTableCall(isolate, table, r, call_index, null, null, null, null, null);
433 :
434 : // 0 count is ok in bounds, and at end of regions.
435 12 : r.CheckCallViaJS(0, 0, 0, 0);
436 12 : r.CheckCallViaJS(0, kTableSize, 0, 0);
437 12 : r.CheckCallViaJS(0, 0, kTableSize, 0);
438 :
439 : // Test actual writes.
440 12 : r.CheckCallViaJS(0, 0, 0, 1);
441 12 : CheckTableCall(isolate, table, r, call_index, 0, null, null, null, null);
442 12 : r.CheckCallViaJS(0, 0, 0, 2);
443 12 : CheckTableCall(isolate, table, r, call_index, 0, 1, null, null, null);
444 12 : r.CheckCallViaJS(0, 0, 0, 3);
445 12 : CheckTableCall(isolate, table, r, call_index, 0, 1, 2, null, null);
446 12 : r.CheckCallViaJS(0, 3, 0, 2);
447 12 : CheckTableCall(isolate, table, r, call_index, 0, 1, 2, 0, 1);
448 12 : r.CheckCallViaJS(0, 3, 1, 2);
449 12 : CheckTableCall(isolate, table, r, call_index, 0, 1, 2, 1, 2);
450 12 : r.CheckCallViaJS(0, 3, 2, 2);
451 12 : CheckTableCall(isolate, table, r, call_index, 0, 1, 2, 2, 3);
452 12 : r.CheckCallViaJS(0, 3, 3, 2);
453 12 : CheckTableCall(isolate, table, r, call_index, 0, 1, 2, 3, 4);
454 12 : }
455 :
456 26663 : WASM_EXEC_TEST(TableInitOob) {
457 : EXPERIMENTAL_FLAG_SCOPE(bulk_memory);
458 : Isolate* isolate = CcTest::InitIsolateOnce();
459 : HandleScope scope(isolate);
460 12 : TestSignatures sigs;
461 24 : WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
462 : const uint32_t kTableSize = 5;
463 : std::vector<uint32_t> function_indexes;
464 12 : const uint32_t sig_index = r.builder().AddSignature(sigs.i_v());
465 :
466 132 : for (uint32_t i = 0; i < kTableSize; ++i) {
467 60 : WasmFunctionCompiler& fn = r.NewFunction(sigs.i_v(), "f");
468 60 : BUILD(fn, WASM_I32V_1(i));
469 : fn.SetSigIndex(sig_index);
470 120 : function_indexes.push_back(fn.function_index());
471 : }
472 :
473 12 : r.builder().AddIndirectFunctionTable(nullptr, kTableSize);
474 12 : r.builder().AddPassiveElementSegment(function_indexes);
475 :
476 12 : WasmFunctionCompiler& call = r.NewFunction(sigs.i_i(), "call");
477 12 : BUILD(call, WASM_CALL_INDIRECT0(sig_index, WASM_GET_LOCAL(0)));
478 : const uint32_t call_index = call.function_index();
479 :
480 12 : BUILD(r,
481 : WASM_TABLE_INIT(0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1),
482 : WASM_GET_LOCAL(2)),
483 : kExprI32Const, 0);
484 :
485 : auto table = handle(
486 : WasmTableObject::cast(r.builder().instance_object()->tables().get(0)),
487 12 : isolate);
488 : const double null = 0xDEADBEEF;
489 :
490 12 : CheckTableCall(isolate, table, r, call_index, null, null, null, null, null);
491 :
492 : // Write all values up to the out-of-bounds write.
493 12 : r.CheckCallViaJS(0xDEADBEEF, 3, 0, 3);
494 12 : CheckTableCall(isolate, table, r, call_index, null, null, null, 0, 1);
495 :
496 : // Write all values up to the out-of-bounds read.
497 12 : r.CheckCallViaJS(0xDEADBEEF, 0, 3, 3);
498 12 : CheckTableCall(isolate, table, r, call_index, 3, 4, null, 0, 1);
499 :
500 : // 0-count is oob.
501 12 : r.CheckCallViaJS(0xDEADBEEF, kTableSize + 1, 0, 0);
502 12 : r.CheckCallViaJS(0xDEADBEEF, 0, kTableSize + 1, 0);
503 :
504 12 : r.CheckCallViaJS(0xDEADBEEF, 0, 0, 6);
505 12 : r.CheckCallViaJS(0xDEADBEEF, 0, 1, 5);
506 12 : r.CheckCallViaJS(0xDEADBEEF, 0, 2, 4);
507 12 : r.CheckCallViaJS(0xDEADBEEF, 0, 3, 3);
508 12 : r.CheckCallViaJS(0xDEADBEEF, 0, 4, 2);
509 12 : r.CheckCallViaJS(0xDEADBEEF, 0, 5, 1);
510 :
511 12 : r.CheckCallViaJS(0xDEADBEEF, 0, 0, 6);
512 12 : r.CheckCallViaJS(0xDEADBEEF, 1, 0, 5);
513 12 : r.CheckCallViaJS(0xDEADBEEF, 2, 0, 4);
514 12 : r.CheckCallViaJS(0xDEADBEEF, 3, 0, 3);
515 12 : r.CheckCallViaJS(0xDEADBEEF, 4, 0, 2);
516 12 : r.CheckCallViaJS(0xDEADBEEF, 5, 0, 1);
517 :
518 12 : r.CheckCallViaJS(0xDEADBEEF, 10, 0, 1);
519 12 : r.CheckCallViaJS(0xDEADBEEF, 0, 10, 1);
520 12 : }
521 :
522 26663 : WASM_EXEC_TEST(TableCopyElems) {
523 : EXPERIMENTAL_FLAG_SCOPE(bulk_memory);
524 : Isolate* isolate = CcTest::InitIsolateOnce();
525 : HandleScope scope(isolate);
526 12 : TestSignatures sigs;
527 24 : WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
528 : const uint32_t kTableSize = 5;
529 : uint16_t function_indexes[kTableSize];
530 12 : const uint32_t sig_index = r.builder().AddSignature(sigs.i_v());
531 :
532 132 : for (uint32_t i = 0; i < kTableSize; ++i) {
533 60 : WasmFunctionCompiler& fn = r.NewFunction(sigs.i_v(), "f");
534 60 : BUILD(fn, WASM_I32V_1(i));
535 : fn.SetSigIndex(sig_index);
536 60 : function_indexes[i] = fn.function_index();
537 : }
538 :
539 12 : r.builder().AddIndirectFunctionTable(function_indexes, kTableSize);
540 :
541 12 : BUILD(
542 : r,
543 : WASM_TABLE_COPY(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), WASM_GET_LOCAL(2)),
544 : kExprI32Const, 0);
545 :
546 : auto table = handle(
547 : WasmTableObject::cast(r.builder().instance_object()->tables().get(0)),
548 12 : isolate);
549 12 : auto f0 = WasmTableObject::Get(isolate, table, 0);
550 12 : auto f1 = WasmTableObject::Get(isolate, table, 1);
551 12 : auto f2 = WasmTableObject::Get(isolate, table, 2);
552 12 : auto f3 = WasmTableObject::Get(isolate, table, 3);
553 12 : auto f4 = WasmTableObject::Get(isolate, table, 4);
554 :
555 12 : CheckTable(isolate, table, f0, f1, f2, f3, f4);
556 12 : r.CheckCallViaJS(0, 0, 1, 1);
557 12 : CheckTable(isolate, table, f1, f1, f2, f3, f4);
558 12 : r.CheckCallViaJS(0, 0, 1, 2);
559 12 : CheckTable(isolate, table, f1, f2, f2, f3, f4);
560 12 : r.CheckCallViaJS(0, 3, 0, 2);
561 12 : CheckTable(isolate, table, f1, f2, f2, f1, f2);
562 12 : r.CheckCallViaJS(0, 1, 0, 2);
563 12 : CheckTable(isolate, table, f1, f1, f2, f1, f2);
564 12 : }
565 :
566 26663 : WASM_EXEC_TEST(TableCopyCalls) {
567 : EXPERIMENTAL_FLAG_SCOPE(bulk_memory);
568 : Isolate* isolate = CcTest::InitIsolateOnce();
569 : HandleScope scope(isolate);
570 12 : TestSignatures sigs;
571 24 : WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
572 : const uint32_t kTableSize = 5;
573 : uint16_t function_indexes[kTableSize];
574 12 : const uint32_t sig_index = r.builder().AddSignature(sigs.i_v());
575 :
576 132 : for (uint32_t i = 0; i < kTableSize; ++i) {
577 60 : WasmFunctionCompiler& fn = r.NewFunction(sigs.i_v(), "f");
578 60 : BUILD(fn, WASM_I32V_1(i));
579 : fn.SetSigIndex(sig_index);
580 60 : function_indexes[i] = fn.function_index();
581 : }
582 :
583 12 : r.builder().AddIndirectFunctionTable(function_indexes, kTableSize);
584 :
585 12 : WasmFunctionCompiler& call = r.NewFunction(sigs.i_i(), "call");
586 12 : BUILD(call, WASM_CALL_INDIRECT0(sig_index, WASM_GET_LOCAL(0)));
587 : const uint32_t call_index = call.function_index();
588 :
589 12 : BUILD(
590 : r,
591 : WASM_TABLE_COPY(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), WASM_GET_LOCAL(2)),
592 : kExprI32Const, 0);
593 :
594 : auto table = handle(
595 : WasmTableObject::cast(r.builder().instance_object()->tables().get(0)),
596 12 : isolate);
597 :
598 12 : CheckTableCall(isolate, table, r, call_index, 0, 1, 2, 3, 4);
599 12 : r.CheckCallViaJS(0, 0, 1, 1);
600 12 : CheckTableCall(isolate, table, r, call_index, 1, 1, 2, 3, 4);
601 12 : r.CheckCallViaJS(0, 0, 1, 2);
602 12 : CheckTableCall(isolate, table, r, call_index, 1, 2, 2, 3, 4);
603 12 : r.CheckCallViaJS(0, 3, 0, 2);
604 12 : CheckTableCall(isolate, table, r, call_index, 1, 2, 2, 1, 2);
605 12 : }
606 :
607 26663 : WASM_EXEC_TEST(TableCopyOobWrites) {
608 : EXPERIMENTAL_FLAG_SCOPE(bulk_memory);
609 : Isolate* isolate = CcTest::InitIsolateOnce();
610 : HandleScope scope(isolate);
611 12 : TestSignatures sigs;
612 24 : WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
613 : const uint32_t kTableSize = 5;
614 : uint16_t function_indexes[kTableSize];
615 12 : const uint32_t sig_index = r.builder().AddSignature(sigs.i_v());
616 :
617 132 : for (uint32_t i = 0; i < kTableSize; ++i) {
618 60 : WasmFunctionCompiler& fn = r.NewFunction(sigs.i_v(), "f");
619 60 : BUILD(fn, WASM_I32V_1(i));
620 : fn.SetSigIndex(sig_index);
621 60 : function_indexes[i] = fn.function_index();
622 : }
623 :
624 12 : r.builder().AddIndirectFunctionTable(function_indexes, kTableSize);
625 :
626 12 : BUILD(
627 : r,
628 : WASM_TABLE_COPY(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), WASM_GET_LOCAL(2)),
629 : kExprI32Const, 0);
630 :
631 : auto table = handle(
632 : WasmTableObject::cast(r.builder().instance_object()->tables().get(0)),
633 12 : isolate);
634 12 : auto f0 = WasmTableObject::Get(isolate, table, 0);
635 12 : auto f1 = WasmTableObject::Get(isolate, table, 1);
636 12 : auto f2 = WasmTableObject::Get(isolate, table, 2);
637 12 : auto f3 = WasmTableObject::Get(isolate, table, 3);
638 12 : auto f4 = WasmTableObject::Get(isolate, table, 4);
639 :
640 12 : CheckTable(isolate, table, f0, f1, f2, f3, f4);
641 :
642 : // Non-overlapping, src < dst.
643 12 : r.CheckCallViaJS(0xDEADBEEF, 3, 0, 3);
644 12 : CheckTable(isolate, table, f0, f1, f2, f0, f1);
645 :
646 : // Non-overlapping, dst < src.
647 12 : r.CheckCallViaJS(0xDEADBEEF, 0, 4, 2);
648 12 : CheckTable(isolate, table, f1, f1, f2, f0, f1);
649 :
650 : // Overlapping, src < dst. This is required to copy backward, but the first
651 : // access will be out-of-bounds, so nothing changes.
652 12 : r.CheckCallViaJS(0xDEADBEEF, 3, 0, 99);
653 12 : CheckTable(isolate, table, f1, f1, f2, f0, f1);
654 :
655 : // Overlapping, dst < src.
656 12 : r.CheckCallViaJS(0xDEADBEEF, 0, 1, 99);
657 12 : CheckTable(isolate, table, f1, f2, f0, f1, f1);
658 12 : }
659 :
660 26663 : WASM_EXEC_TEST(TableCopyOob1) {
661 : EXPERIMENTAL_FLAG_SCOPE(bulk_memory);
662 24 : WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
663 : const uint32_t kTableSize = 5;
664 :
665 12 : r.builder().AddIndirectFunctionTable(nullptr, kTableSize);
666 :
667 12 : BUILD(
668 : r,
669 : WASM_TABLE_COPY(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), WASM_GET_LOCAL(2)),
670 : kExprI32Const, 0);
671 :
672 12 : r.CheckCallViaJS(0, 0, 0, 1); // nop
673 12 : r.CheckCallViaJS(0, 0, 0, kTableSize); // nop
674 12 : r.CheckCallViaJS(0xDEADBEEF, 0, 0, kTableSize + 1);
675 12 : r.CheckCallViaJS(0xDEADBEEF, 1, 0, kTableSize);
676 12 : r.CheckCallViaJS(0xDEADBEEF, 0, 1, kTableSize);
677 :
678 : {
679 : const uint32_t big = 1000000;
680 12 : r.CheckCallViaJS(0xDEADBEEF, big, 0, 0);
681 12 : r.CheckCallViaJS(0xDEADBEEF, 0, big, 0);
682 : }
683 :
684 564 : for (uint32_t big = 4294967295; big > 1000; big >>= 1) {
685 276 : r.CheckCallViaJS(0xDEADBEEF, big, 0, 1);
686 276 : r.CheckCallViaJS(0xDEADBEEF, 0, big, 1);
687 276 : r.CheckCallViaJS(0xDEADBEEF, 0, 0, big);
688 : }
689 :
690 708 : for (uint32_t big = -1000; big != 0; big <<= 1) {
691 348 : r.CheckCallViaJS(0xDEADBEEF, big, 0, 1);
692 348 : r.CheckCallViaJS(0xDEADBEEF, 0, big, 1);
693 348 : r.CheckCallViaJS(0xDEADBEEF, 0, 0, big);
694 : }
695 12 : }
696 :
697 26663 : WASM_EXEC_TEST(ElemDropTwice) {
698 : EXPERIMENTAL_FLAG_SCOPE(bulk_memory);
699 24 : WasmRunner<uint32_t> r(execution_tier);
700 12 : r.builder().AddIndirectFunctionTable(nullptr, 1);
701 24 : r.builder().AddPassiveElementSegment({});
702 12 : BUILD(r, WASM_ELEM_DROP(0), kExprI32Const, 0);
703 :
704 12 : r.CheckCallViaJS(0);
705 12 : r.CheckCallViaJS(0xDEADBEEF);
706 12 : }
707 :
708 26663 : WASM_EXEC_TEST(ElemDropThenTableInit) {
709 : EXPERIMENTAL_FLAG_SCOPE(bulk_memory);
710 24 : WasmRunner<uint32_t> r(execution_tier);
711 12 : r.builder().AddIndirectFunctionTable(nullptr, 1);
712 24 : r.builder().AddPassiveElementSegment({});
713 12 : BUILD(r, WASM_ELEM_DROP(0),
714 : WASM_TABLE_INIT(0, WASM_I32V_1(0), WASM_I32V_1(0), WASM_I32V_1(0)),
715 : kExprI32Const, 0);
716 :
717 12 : r.CheckCallViaJS(0xDEADBEEF);
718 12 : }
719 :
720 : } // namespace test_run_wasm_bulk_memory
721 : } // namespace wasm
722 : } // namespace internal
723 79917 : } // namespace v8
|