Line data Source code
1 : // Copyright 2015 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 <stdlib.h>
6 : #include <string.h>
7 :
8 : #include "src/api-inl.h"
9 : #include "src/objects-inl.h"
10 : #include "src/snapshot/code-serializer.h"
11 : #include "src/version.h"
12 : #include "src/wasm/module-decoder.h"
13 : #include "src/wasm/wasm-engine.h"
14 : #include "src/wasm/wasm-memory.h"
15 : #include "src/wasm/wasm-module-builder.h"
16 : #include "src/wasm/wasm-module.h"
17 : #include "src/wasm/wasm-objects-inl.h"
18 : #include "src/wasm/wasm-opcodes.h"
19 :
20 : #include "test/cctest/cctest.h"
21 : #include "test/common/wasm/flag-utils.h"
22 : #include "test/common/wasm/test-signatures.h"
23 : #include "test/common/wasm/wasm-macro-gen.h"
24 : #include "test/common/wasm/wasm-module-runner.h"
25 :
26 : namespace v8 {
27 : namespace internal {
28 : namespace wasm {
29 : namespace test_run_wasm_module {
30 :
31 : using testing::CompileAndInstantiateForTesting;
32 :
33 : namespace {
34 : void Cleanup(Isolate* isolate = CcTest::InitIsolateOnce()) {
35 : // By sending a low memory notifications, we will try hard to collect all
36 : // garbage and will therefore also invoke all weak callbacks of actually
37 : // unreachable persistent handles.
38 145 : reinterpret_cast<v8::Isolate*>(isolate)->LowMemoryNotification();
39 : }
40 :
41 205 : void TestModule(Zone* zone, WasmModuleBuilder* builder,
42 : int32_t expected_result) {
43 : ZoneBuffer buffer(zone);
44 205 : builder->WriteTo(buffer);
45 :
46 205 : Isolate* isolate = CcTest::InitIsolateOnce();
47 : HandleScope scope(isolate);
48 205 : testing::SetupIsolateForWasmModule(isolate);
49 : int32_t result =
50 205 : testing::CompileAndRunWasmModule(isolate, buffer.begin(), buffer.end());
51 205 : CHECK_EQ(expected_result, result);
52 205 : }
53 :
54 5 : void TestModuleException(Zone* zone, WasmModuleBuilder* builder) {
55 : ZoneBuffer buffer(zone);
56 5 : builder->WriteTo(buffer);
57 :
58 5 : Isolate* isolate = CcTest::InitIsolateOnce();
59 : HandleScope scope(isolate);
60 5 : testing::SetupIsolateForWasmModule(isolate);
61 10 : v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
62 5 : testing::CompileAndRunWasmModule(isolate, buffer.begin(), buffer.end());
63 5 : CHECK(try_catch.HasCaught());
64 5 : isolate->clear_pending_exception();
65 5 : }
66 :
67 235 : void ExportAsMain(WasmFunctionBuilder* f) {
68 235 : f->builder()->AddExport(CStrVector("main"), f);
69 235 : }
70 :
71 : #define EMIT_CODE_WITH_END(f, code) \
72 : do { \
73 : f->EmitCode(code, sizeof(code)); \
74 : f->Emit(kExprEnd); \
75 : } while (false)
76 :
77 : } // namespace
78 :
79 28342 : TEST(Run_WasmModule_Return114) {
80 : {
81 : static const int32_t kReturnValue = 114;
82 5 : TestSignatures sigs;
83 5 : v8::internal::AccountingAllocator allocator;
84 10 : Zone zone(&allocator, ZONE_NAME);
85 :
86 5 : WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
87 5 : WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
88 5 : ExportAsMain(f);
89 5 : byte code[] = {WASM_I32V_2(kReturnValue)};
90 5 : EMIT_CODE_WITH_END(f, code);
91 10 : TestModule(&zone, builder, kReturnValue);
92 : }
93 5 : Cleanup();
94 5 : }
95 :
96 28342 : TEST(Run_WasmModule_CallAdd) {
97 : {
98 5 : v8::internal::AccountingAllocator allocator;
99 10 : Zone zone(&allocator, ZONE_NAME);
100 5 : TestSignatures sigs;
101 :
102 5 : WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
103 :
104 10 : WasmFunctionBuilder* f1 = builder->AddFunction(sigs.i_ii());
105 : uint16_t param1 = 0;
106 : uint16_t param2 = 1;
107 : byte code1[] = {
108 5 : WASM_I32_ADD(WASM_GET_LOCAL(param1), WASM_GET_LOCAL(param2))};
109 5 : EMIT_CODE_WITH_END(f1, code1);
110 :
111 5 : WasmFunctionBuilder* f2 = builder->AddFunction(sigs.i_v());
112 :
113 5 : ExportAsMain(f2);
114 : byte code2[] = {
115 10 : WASM_CALL_FUNCTION(f1->func_index(), WASM_I32V_2(77), WASM_I32V_1(22))};
116 5 : EMIT_CODE_WITH_END(f2, code2);
117 10 : TestModule(&zone, builder, 99);
118 : }
119 5 : Cleanup();
120 5 : }
121 :
122 28342 : TEST(Run_WasmModule_ReadLoadedDataSegment) {
123 : {
124 : static const byte kDataSegmentDest0 = 12;
125 5 : v8::internal::AccountingAllocator allocator;
126 10 : Zone zone(&allocator, ZONE_NAME);
127 5 : TestSignatures sigs;
128 :
129 5 : WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
130 5 : WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
131 :
132 5 : ExportAsMain(f);
133 : byte code[] = {
134 5 : WASM_LOAD_MEM(MachineType::Int32(), WASM_I32V_1(kDataSegmentDest0))};
135 5 : EMIT_CODE_WITH_END(f, code);
136 5 : byte data[] = {0xAA, 0xBB, 0xCC, 0xDD};
137 5 : builder->AddDataSegment(data, sizeof(data), kDataSegmentDest0);
138 10 : TestModule(&zone, builder, 0xDDCCBBAA);
139 : }
140 5 : Cleanup();
141 5 : }
142 :
143 28342 : TEST(Run_WasmModule_CheckMemoryIsZero) {
144 : {
145 : static const int kCheckSize = 16 * 1024;
146 5 : v8::internal::AccountingAllocator allocator;
147 10 : Zone zone(&allocator, ZONE_NAME);
148 5 : TestSignatures sigs;
149 :
150 5 : WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
151 5 : WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
152 :
153 5 : uint16_t localIndex = f->AddLocal(kWasmI32);
154 5 : ExportAsMain(f);
155 5 : byte code[] = {WASM_BLOCK_I(
156 : WASM_WHILE(
157 : WASM_I32_LTS(WASM_GET_LOCAL(localIndex), WASM_I32V_3(kCheckSize)),
158 : WASM_IF_ELSE(
159 : WASM_LOAD_MEM(MachineType::Int32(), WASM_GET_LOCAL(localIndex)),
160 : WASM_BRV(3, WASM_I32V_1(-1)),
161 : WASM_INC_LOCAL_BY(localIndex, 4))),
162 10 : WASM_I32V_1(11))};
163 5 : EMIT_CODE_WITH_END(f, code);
164 10 : TestModule(&zone, builder, 11);
165 : }
166 5 : Cleanup();
167 5 : }
168 :
169 28342 : TEST(Run_WasmModule_CallMain_recursive) {
170 : {
171 5 : v8::internal::AccountingAllocator allocator;
172 10 : Zone zone(&allocator, ZONE_NAME);
173 5 : TestSignatures sigs;
174 :
175 5 : WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
176 5 : WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
177 :
178 5 : uint16_t localIndex = f->AddLocal(kWasmI32);
179 5 : ExportAsMain(f);
180 : byte code[] = {
181 5 : WASM_SET_LOCAL(localIndex,
182 : WASM_LOAD_MEM(MachineType::Int32(), WASM_ZERO)),
183 5 : WASM_IF_ELSE_I(WASM_I32_LTS(WASM_GET_LOCAL(localIndex), WASM_I32V_1(5)),
184 : WASM_SEQ(WASM_STORE_MEM(MachineType::Int32(), WASM_ZERO,
185 : WASM_INC_LOCAL(localIndex)),
186 : WASM_CALL_FUNCTION0(0)),
187 15 : WASM_I32V_1(55))};
188 5 : EMIT_CODE_WITH_END(f, code);
189 10 : TestModule(&zone, builder, 55);
190 : }
191 5 : Cleanup();
192 5 : }
193 :
194 28342 : TEST(Run_WasmModule_Global) {
195 : {
196 5 : v8::internal::AccountingAllocator allocator;
197 10 : Zone zone(&allocator, ZONE_NAME);
198 5 : TestSignatures sigs;
199 :
200 5 : WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
201 5 : uint32_t global1 = builder->AddGlobal(kWasmI32, false);
202 5 : uint32_t global2 = builder->AddGlobal(kWasmI32, false);
203 10 : WasmFunctionBuilder* f1 = builder->AddFunction(sigs.i_v());
204 : byte code1[] = {
205 5 : WASM_I32_ADD(WASM_GET_GLOBAL(global1), WASM_GET_GLOBAL(global2))};
206 5 : EMIT_CODE_WITH_END(f1, code1);
207 5 : WasmFunctionBuilder* f2 = builder->AddFunction(sigs.i_v());
208 5 : ExportAsMain(f2);
209 : byte code2[] = {WASM_SET_GLOBAL(global1, WASM_I32V_1(56)),
210 : WASM_SET_GLOBAL(global2, WASM_I32V_1(41)),
211 10 : WASM_RETURN1(WASM_CALL_FUNCTION0(f1->func_index()))};
212 5 : EMIT_CODE_WITH_END(f2, code2);
213 10 : TestModule(&zone, builder, 97);
214 : }
215 5 : Cleanup();
216 5 : }
217 :
218 28342 : TEST(MemorySize) {
219 : {
220 : // Initial memory size is 16, see wasm-module-builder.cc
221 : static const int kExpectedValue = 16;
222 5 : TestSignatures sigs;
223 5 : v8::internal::AccountingAllocator allocator;
224 10 : Zone zone(&allocator, ZONE_NAME);
225 :
226 5 : WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
227 5 : WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
228 5 : ExportAsMain(f);
229 5 : byte code[] = {WASM_MEMORY_SIZE};
230 5 : EMIT_CODE_WITH_END(f, code);
231 10 : TestModule(&zone, builder, kExpectedValue);
232 : }
233 5 : Cleanup();
234 5 : }
235 :
236 28342 : TEST(Run_WasmModule_MemSize_GrowMem) {
237 : {
238 : // Initial memory size = 16 + MemoryGrow(10)
239 : static const int kExpectedValue = 26;
240 5 : TestSignatures sigs;
241 5 : v8::internal::AccountingAllocator allocator;
242 10 : Zone zone(&allocator, ZONE_NAME);
243 :
244 5 : WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
245 5 : WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
246 5 : ExportAsMain(f);
247 : byte code[] = {WASM_GROW_MEMORY(WASM_I32V_1(10)), WASM_DROP,
248 5 : WASM_MEMORY_SIZE};
249 5 : EMIT_CODE_WITH_END(f, code);
250 10 : TestModule(&zone, builder, kExpectedValue);
251 : }
252 5 : Cleanup();
253 5 : }
254 :
255 28342 : TEST(MemoryGrowZero) {
256 : {
257 : // Initial memory size is 16, see wasm-module-builder.cc
258 : static const int kExpectedValue = 16;
259 5 : TestSignatures sigs;
260 5 : v8::internal::AccountingAllocator allocator;
261 10 : Zone zone(&allocator, ZONE_NAME);
262 :
263 5 : WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
264 5 : WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
265 5 : ExportAsMain(f);
266 5 : byte code[] = {WASM_GROW_MEMORY(WASM_I32V(0))};
267 5 : EMIT_CODE_WITH_END(f, code);
268 10 : TestModule(&zone, builder, kExpectedValue);
269 : }
270 5 : Cleanup();
271 5 : }
272 :
273 5 : class InterruptThread : public v8::base::Thread {
274 : public:
275 : explicit InterruptThread(Isolate* isolate, int32_t* memory)
276 : : Thread(Options("TestInterruptLoop")),
277 : isolate_(isolate),
278 5 : memory_(memory) {}
279 :
280 5 : static void OnInterrupt(v8::Isolate* isolate, void* data) {
281 : int32_t* m = reinterpret_cast<int32_t*>(data);
282 : // Set the interrupt location to 0 to break the loop in {TestInterruptLoop}.
283 : Address ptr = reinterpret_cast<Address>(&m[interrupt_location_]);
284 : WriteLittleEndianValue<int32_t>(ptr, interrupt_value_);
285 5 : }
286 :
287 5 : void Run() override {
288 : // Wait for the main thread to write the signal value.
289 5 : int32_t val = 0;
290 5 : do {
291 5 : val = memory_[0];
292 : val = ReadLittleEndianValue<int32_t>(reinterpret_cast<Address>(&val));
293 : } while (val != signal_value_);
294 5 : isolate_->RequestInterrupt(&OnInterrupt, const_cast<int32_t*>(memory_));
295 5 : }
296 :
297 : Isolate* isolate_;
298 : volatile int32_t* memory_;
299 : static const int32_t interrupt_location_ = 10;
300 : static const int32_t interrupt_value_ = 154;
301 : static const int32_t signal_value_ = 1221;
302 : };
303 :
304 28342 : TEST(TestInterruptLoop) {
305 : {
306 : // Do not dump the module of this test because it contains an infinite loop.
307 5 : if (FLAG_dump_wasm_module) return;
308 :
309 : // This test tests that WebAssembly loops can be interrupted, i.e. that if
310 : // an
311 : // InterruptCallback is registered by {Isolate::RequestInterrupt}, then the
312 : // InterruptCallback is eventually called even if a loop in WebAssembly code
313 : // is executed.
314 : // Test setup:
315 : // The main thread executes a WebAssembly function with a loop. In the loop
316 : // {signal_value_} is written to memory to signal a helper thread that the
317 : // main thread reached the loop in the WebAssembly program. When the helper
318 : // thread reads {signal_value_} from memory, it registers the
319 : // InterruptCallback. Upon exeution, the InterruptCallback write into the
320 : // WebAssemblyMemory to end the loop in the WebAssembly program.
321 5 : TestSignatures sigs;
322 5 : Isolate* isolate = CcTest::InitIsolateOnce();
323 5 : v8::internal::AccountingAllocator allocator;
324 10 : Zone zone(&allocator, ZONE_NAME);
325 :
326 5 : WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
327 5 : WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
328 5 : ExportAsMain(f);
329 : byte code[] = {
330 10 : WASM_LOOP(
331 : WASM_IFB(WASM_NOT(WASM_LOAD_MEM(
332 : MachineType::Int32(),
333 : WASM_I32V(InterruptThread::interrupt_location_ * 4))),
334 : WASM_STORE_MEM(MachineType::Int32(), WASM_ZERO,
335 : WASM_I32V(InterruptThread::signal_value_)),
336 : WASM_BR(1))),
337 15 : WASM_I32V(121)};
338 5 : EMIT_CODE_WITH_END(f, code);
339 : ZoneBuffer buffer(&zone);
340 5 : builder->WriteTo(buffer);
341 :
342 : HandleScope scope(isolate);
343 5 : testing::SetupIsolateForWasmModule(isolate);
344 5 : ErrorThrower thrower(isolate, "Test");
345 : const Handle<WasmInstanceObject> instance =
346 : CompileAndInstantiateForTesting(
347 10 : isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()))
348 10 : .ToHandleChecked();
349 :
350 10 : Handle<JSArrayBuffer> memory(instance->memory_object()->array_buffer(),
351 10 : isolate);
352 : int32_t* memory_array = reinterpret_cast<int32_t*>(memory->backing_store());
353 :
354 : InterruptThread thread(isolate, memory_array);
355 5 : thread.Start();
356 5 : testing::RunWasmModuleForTesting(isolate, instance, 0, nullptr);
357 : Address address = reinterpret_cast<Address>(
358 : &memory_array[InterruptThread::interrupt_location_]);
359 5 : CHECK_EQ(InterruptThread::interrupt_value_,
360 5 : ReadLittleEndianValue<int32_t>(address));
361 : }
362 5 : Cleanup();
363 : }
364 :
365 28342 : TEST(Run_WasmModule_MemoryGrowInIf) {
366 : {
367 5 : TestSignatures sigs;
368 5 : v8::internal::AccountingAllocator allocator;
369 10 : Zone zone(&allocator, ZONE_NAME);
370 5 : WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
371 5 : WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
372 5 : ExportAsMain(f);
373 : byte code[] = {WASM_IF_ELSE_I(WASM_I32V(0), WASM_GROW_MEMORY(WASM_I32V(1)),
374 5 : WASM_I32V(12))};
375 5 : EMIT_CODE_WITH_END(f, code);
376 10 : TestModule(&zone, builder, 12);
377 : }
378 5 : Cleanup();
379 5 : }
380 :
381 28342 : TEST(Run_WasmModule_GrowMemOobOffset) {
382 : {
383 : static const int kPageSize = 0x10000;
384 : // Initial memory size = 16 + MemoryGrow(10)
385 : static const int index = kPageSize * 17 + 4;
386 : int value = 0xACED;
387 5 : TestSignatures sigs;
388 5 : v8::internal::AccountingAllocator allocator;
389 10 : Zone zone(&allocator, ZONE_NAME);
390 :
391 5 : WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
392 5 : WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
393 5 : ExportAsMain(f);
394 : byte code[] = {WASM_GROW_MEMORY(WASM_I32V_1(1)),
395 5 : WASM_STORE_MEM(MachineType::Int32(), WASM_I32V(index),
396 10 : WASM_I32V(value))};
397 5 : EMIT_CODE_WITH_END(f, code);
398 10 : TestModuleException(&zone, builder);
399 : }
400 5 : Cleanup();
401 5 : }
402 :
403 28342 : TEST(Run_WasmModule_GrowMemOobFixedIndex) {
404 : {
405 : static const int kPageSize = 0x10000;
406 : // Initial memory size = 16 + MemoryGrow(10)
407 : static const int index = kPageSize * 26 + 4;
408 : int value = 0xACED;
409 5 : TestSignatures sigs;
410 5 : Isolate* isolate = CcTest::InitIsolateOnce();
411 5 : Zone zone(isolate->allocator(), ZONE_NAME);
412 :
413 5 : WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
414 5 : WasmFunctionBuilder* f = builder->AddFunction(sigs.i_i());
415 5 : ExportAsMain(f);
416 : byte code[] = {WASM_GROW_MEMORY(WASM_GET_LOCAL(0)), WASM_DROP,
417 5 : WASM_STORE_MEM(MachineType::Int32(), WASM_I32V(index),
418 : WASM_I32V(value)),
419 10 : WASM_LOAD_MEM(MachineType::Int32(), WASM_I32V(index))};
420 5 : EMIT_CODE_WITH_END(f, code);
421 :
422 : HandleScope scope(isolate);
423 : ZoneBuffer buffer(&zone);
424 5 : builder->WriteTo(buffer);
425 5 : testing::SetupIsolateForWasmModule(isolate);
426 :
427 5 : ErrorThrower thrower(isolate, "Test");
428 : Handle<WasmInstanceObject> instance =
429 : CompileAndInstantiateForTesting(
430 10 : isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()))
431 10 : .ToHandleChecked();
432 :
433 : // Initial memory size is 16 pages, should trap till index > MemSize on
434 : // consecutive GrowMem calls
435 25 : for (uint32_t i = 1; i < 5; i++) {
436 20 : Handle<Object> params[1] = {Handle<Object>(Smi::FromInt(i), isolate)};
437 20 : v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
438 20 : testing::RunWasmModuleForTesting(isolate, instance, 1, params);
439 20 : CHECK(try_catch.HasCaught());
440 20 : isolate->clear_pending_exception();
441 20 : }
442 :
443 : Handle<Object> params[1] = {Handle<Object>(Smi::FromInt(1), isolate)};
444 : int32_t result =
445 5 : testing::RunWasmModuleForTesting(isolate, instance, 1, params);
446 10 : CHECK_EQ(0xACED, result);
447 : }
448 5 : Cleanup();
449 5 : }
450 :
451 28342 : TEST(Run_WasmModule_GrowMemOobVariableIndex) {
452 : {
453 : static const int kPageSize = 0x10000;
454 : int value = 0xACED;
455 5 : TestSignatures sigs;
456 5 : Isolate* isolate = CcTest::InitIsolateOnce();
457 5 : v8::internal::AccountingAllocator allocator;
458 10 : Zone zone(&allocator, ZONE_NAME);
459 :
460 5 : WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
461 5 : WasmFunctionBuilder* f = builder->AddFunction(sigs.i_i());
462 5 : ExportAsMain(f);
463 : byte code[] = {WASM_GROW_MEMORY(WASM_I32V_1(1)), WASM_DROP,
464 5 : WASM_STORE_MEM(MachineType::Int32(), WASM_GET_LOCAL(0),
465 : WASM_I32V(value)),
466 10 : WASM_LOAD_MEM(MachineType::Int32(), WASM_GET_LOCAL(0))};
467 5 : EMIT_CODE_WITH_END(f, code);
468 :
469 : HandleScope scope(isolate);
470 : ZoneBuffer buffer(&zone);
471 5 : builder->WriteTo(buffer);
472 5 : testing::SetupIsolateForWasmModule(isolate);
473 :
474 5 : ErrorThrower thrower(isolate, "Test");
475 : Handle<WasmInstanceObject> instance =
476 : CompileAndInstantiateForTesting(
477 10 : isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()))
478 10 : .ToHandleChecked();
479 :
480 : // Initial memory size is 16 pages, should trap till index > MemSize on
481 : // consecutive GrowMem calls
482 25 : for (int i = 1; i < 5; i++) {
483 : Handle<Object> params[1] = {
484 20 : Handle<Object>(Smi::FromInt((16 + i) * kPageSize - 3), isolate)};
485 20 : v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
486 20 : testing::RunWasmModuleForTesting(isolate, instance, 1, params);
487 20 : CHECK(try_catch.HasCaught());
488 20 : isolate->clear_pending_exception();
489 20 : }
490 :
491 20 : for (int i = 1; i < 5; i++) {
492 : Handle<Object> params[1] = {
493 20 : Handle<Object>(Smi::FromInt((20 + i) * kPageSize - 4), isolate)};
494 : int32_t result =
495 20 : testing::RunWasmModuleForTesting(isolate, instance, 1, params);
496 20 : CHECK_EQ(0xACED, result);
497 : }
498 :
499 10 : v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
500 : Handle<Object> params[1] = {
501 : Handle<Object>(Smi::FromInt(25 * kPageSize), isolate)};
502 5 : testing::RunWasmModuleForTesting(isolate, instance, 1, params);
503 5 : CHECK(try_catch.HasCaught());
504 10 : isolate->clear_pending_exception();
505 : }
506 5 : Cleanup();
507 5 : }
508 :
509 28342 : TEST(Run_WasmModule_Global_init) {
510 : {
511 5 : v8::internal::AccountingAllocator allocator;
512 10 : Zone zone(&allocator, ZONE_NAME);
513 5 : TestSignatures sigs;
514 :
515 5 : WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
516 : uint32_t global1 =
517 5 : builder->AddGlobal(kWasmI32, false, false, WasmInitExpr(777777));
518 : uint32_t global2 =
519 5 : builder->AddGlobal(kWasmI32, false, false, WasmInitExpr(222222));
520 5 : WasmFunctionBuilder* f1 = builder->AddFunction(sigs.i_v());
521 : byte code[] = {
522 5 : WASM_I32_ADD(WASM_GET_GLOBAL(global1), WASM_GET_GLOBAL(global2))};
523 5 : EMIT_CODE_WITH_END(f1, code);
524 5 : ExportAsMain(f1);
525 10 : TestModule(&zone, builder, 999999);
526 : }
527 5 : Cleanup();
528 5 : }
529 :
530 : template <typename CType>
531 30 : static void RunWasmModuleGlobalInitTest(ValueType type, CType expected) {
532 : {
533 30 : v8::internal::AccountingAllocator allocator;
534 60 : Zone zone(&allocator, ZONE_NAME);
535 30 : TestSignatures sigs;
536 :
537 30 : ValueType types[] = {type};
538 : FunctionSig sig(1, 0, types);
539 :
540 180 : for (int padding = 0; padding < 5; padding++) {
541 : // Test with a simple initializer
542 150 : WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
543 :
544 450 : for (int i = 0; i < padding; i++) { // pad global before
545 600 : builder->AddGlobal(kWasmI32, false, false, WasmInitExpr(i + 20000));
546 : }
547 : uint32_t global =
548 150 : builder->AddGlobal(type, false, false, WasmInitExpr(expected));
549 450 : for (int i = 0; i < padding; i++) { // pad global after
550 600 : builder->AddGlobal(kWasmI32, false, false, WasmInitExpr(i + 30000));
551 : }
552 :
553 150 : WasmFunctionBuilder* f1 = builder->AddFunction(&sig);
554 150 : byte code[] = {WASM_GET_GLOBAL(global)};
555 150 : EMIT_CODE_WITH_END(f1, code);
556 150 : ExportAsMain(f1);
557 150 : TestModule(&zone, builder, expected);
558 30 : }
559 : }
560 30 : Cleanup();
561 30 : }
562 :
563 28342 : TEST(Run_WasmModule_Global_i32) {
564 5 : RunWasmModuleGlobalInitTest<int32_t>(kWasmI32, -983489);
565 5 : RunWasmModuleGlobalInitTest<int32_t>(kWasmI32, 11223344);
566 5 : }
567 :
568 28342 : TEST(Run_WasmModule_Global_f32) {
569 5 : RunWasmModuleGlobalInitTest<float>(kWasmF32, -983.9f);
570 5 : RunWasmModuleGlobalInitTest<float>(kWasmF32, 1122.99f);
571 5 : }
572 :
573 28342 : TEST(Run_WasmModule_Global_f64) {
574 5 : RunWasmModuleGlobalInitTest<double>(kWasmF64, -833.9);
575 5 : RunWasmModuleGlobalInitTest<double>(kWasmF64, 86374.25);
576 5 : }
577 :
578 28342 : TEST(InitDataAtTheUpperLimit) {
579 : {
580 5 : Isolate* isolate = CcTest::InitIsolateOnce();
581 : HandleScope scope(isolate);
582 5 : testing::SetupIsolateForWasmModule(isolate);
583 :
584 5 : ErrorThrower thrower(isolate, "Run_WasmModule_InitDataAtTheUpperLimit");
585 :
586 : const byte data[] = {
587 : WASM_MODULE_HEADER, // --
588 : kMemorySectionCode, // --
589 : U32V_1(4), // section size
590 : ENTRY_COUNT(1), // --
591 : kHasMaximumFlag, // --
592 : 1, // initial size
593 : 2, // maximum size
594 : kDataSectionCode, // --
595 : U32V_1(9), // section size
596 : ENTRY_COUNT(1), // --
597 : 0, // linear memory index
598 : WASM_I32V_3(0xFFFF), // destination offset
599 : kExprEnd,
600 : U32V_1(1), // source size
601 : 'c' // data bytes
602 5 : };
603 :
604 : CompileAndInstantiateForTesting(
605 5 : isolate, &thrower, ModuleWireBytes(data, data + arraysize(data)));
606 5 : if (thrower.error()) {
607 0 : thrower.Reify()->Print();
608 0 : FATAL("compile or instantiate error");
609 : }
610 : }
611 5 : Cleanup();
612 5 : }
613 :
614 28342 : TEST(EmptyMemoryNonEmptyDataSegment) {
615 : {
616 5 : Isolate* isolate = CcTest::InitIsolateOnce();
617 : HandleScope scope(isolate);
618 5 : testing::SetupIsolateForWasmModule(isolate);
619 :
620 5 : ErrorThrower thrower(isolate, "Run_WasmModule_InitDataAtTheUpperLimit");
621 :
622 : const byte data[] = {
623 : WASM_MODULE_HEADER, // --
624 : kMemorySectionCode, // --
625 : U32V_1(4), // section size
626 : ENTRY_COUNT(1), // --
627 : kHasMaximumFlag, // --
628 : 0, // initial size
629 : 0, // maximum size
630 : kDataSectionCode, // --
631 : U32V_1(7), // section size
632 : ENTRY_COUNT(1), // --
633 : 0, // linear memory index
634 : WASM_I32V_1(8), // destination offset
635 : kExprEnd,
636 : U32V_1(1), // source size
637 : 'c' // data bytes
638 5 : };
639 :
640 : CompileAndInstantiateForTesting(
641 5 : isolate, &thrower, ModuleWireBytes(data, data + arraysize(data)));
642 : // It should not be possible to instantiate this module.
643 5 : CHECK(thrower.error());
644 : }
645 5 : Cleanup();
646 5 : }
647 :
648 28342 : TEST(EmptyMemoryEmptyDataSegment) {
649 : {
650 5 : Isolate* isolate = CcTest::InitIsolateOnce();
651 : HandleScope scope(isolate);
652 5 : testing::SetupIsolateForWasmModule(isolate);
653 :
654 5 : ErrorThrower thrower(isolate, "Run_WasmModule_InitDataAtTheUpperLimit");
655 :
656 : const byte data[] = {
657 : WASM_MODULE_HEADER, // --
658 : kMemorySectionCode, // --
659 : U32V_1(4), // section size
660 : ENTRY_COUNT(1), // --
661 : kHasMaximumFlag, // --
662 : 0, // initial size
663 : 0, // maximum size
664 : kDataSectionCode, // --
665 : U32V_1(6), // section size
666 : ENTRY_COUNT(1), // --
667 : 0, // linear memory index
668 : WASM_I32V_1(0), // destination offset
669 : kExprEnd,
670 : U32V_1(0), // source size
671 5 : };
672 :
673 : CompileAndInstantiateForTesting(
674 5 : isolate, &thrower, ModuleWireBytes(data, data + arraysize(data)));
675 : // It should be possible to instantiate this module.
676 10 : CHECK(!thrower.error());
677 : }
678 5 : Cleanup();
679 5 : }
680 :
681 28342 : TEST(MemoryWithOOBEmptyDataSegment) {
682 : {
683 5 : Isolate* isolate = CcTest::InitIsolateOnce();
684 : HandleScope scope(isolate);
685 5 : testing::SetupIsolateForWasmModule(isolate);
686 :
687 5 : ErrorThrower thrower(isolate, "Run_WasmModule_InitDataAtTheUpperLimit");
688 :
689 : const byte data[] = {
690 : WASM_MODULE_HEADER, // --
691 : kMemorySectionCode, // --
692 : U32V_1(4), // section size
693 : ENTRY_COUNT(1), // --
694 : kHasMaximumFlag, // --
695 : 1, // initial size
696 : 1, // maximum size
697 : kDataSectionCode, // --
698 : U32V_1(9), // section size
699 : ENTRY_COUNT(1), // --
700 : 0, // linear memory index
701 : WASM_I32V_4(0x2468ACE), // destination offset
702 : kExprEnd,
703 : U32V_1(0), // source size
704 5 : };
705 :
706 : CompileAndInstantiateForTesting(
707 5 : isolate, &thrower, ModuleWireBytes(data, data + arraysize(data)));
708 : // It should not be possible to instantiate this module.
709 5 : CHECK(thrower.error());
710 : }
711 5 : Cleanup();
712 5 : }
713 :
714 : // Utility to free the allocated memory for a buffer that is manually
715 : // externalized in a test.
716 : struct ManuallyExternalizedBuffer {
717 : Isolate* isolate_;
718 : Handle<JSArrayBuffer> buffer_;
719 : void* allocation_base_;
720 : size_t allocation_length_;
721 : bool const should_free_;
722 :
723 10 : ManuallyExternalizedBuffer(JSArrayBuffer buffer, Isolate* isolate)
724 : : isolate_(isolate),
725 : buffer_(buffer, isolate),
726 10 : allocation_base_(buffer->allocation_base()),
727 10 : allocation_length_(buffer->allocation_length()),
728 : should_free_(!isolate_->wasm_engine()->memory_tracker()->IsWasmMemory(
729 50 : buffer->backing_store())) {
730 10 : if (!isolate_->wasm_engine()->memory_tracker()->IsWasmMemory(
731 20 : buffer->backing_store())) {
732 0 : v8::Utils::ToLocal(buffer_)->Externalize();
733 : }
734 10 : }
735 10 : ~ManuallyExternalizedBuffer() {
736 10 : if (should_free_) {
737 0 : buffer_->FreeBackingStoreFromMainThread();
738 : }
739 10 : }
740 : };
741 :
742 28342 : TEST(Run_WasmModule_Buffer_Externalized_GrowMem) {
743 : {
744 5 : Isolate* isolate = CcTest::InitIsolateOnce();
745 : HandleScope scope(isolate);
746 5 : TestSignatures sigs;
747 10 : v8::internal::AccountingAllocator allocator;
748 10 : Zone zone(&allocator, ZONE_NAME);
749 :
750 5 : WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
751 5 : WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
752 5 : ExportAsMain(f);
753 : byte code[] = {WASM_GROW_MEMORY(WASM_I32V_1(6)), WASM_DROP,
754 5 : WASM_MEMORY_SIZE};
755 5 : EMIT_CODE_WITH_END(f, code);
756 :
757 : ZoneBuffer buffer(&zone);
758 5 : builder->WriteTo(buffer);
759 5 : testing::SetupIsolateForWasmModule(isolate);
760 5 : ErrorThrower thrower(isolate, "Test");
761 : const Handle<WasmInstanceObject> instance =
762 : CompileAndInstantiateForTesting(
763 10 : isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()))
764 10 : .ToHandleChecked();
765 10 : Handle<WasmMemoryObject> memory_object(instance->memory_object(), isolate);
766 :
767 : // Fake the Embedder flow by externalizing the array buffer.
768 10 : ManuallyExternalizedBuffer buffer1(memory_object->array_buffer(), isolate);
769 :
770 : // Grow using the API.
771 5 : uint32_t result = WasmMemoryObject::Grow(isolate, memory_object, 4);
772 5 : CHECK_EQ(16, result);
773 5 : CHECK(buffer1.buffer_->was_detached()); // growing always detaches
774 5 : CHECK_EQ(0, buffer1.buffer_->byte_length());
775 :
776 15 : CHECK_NE(*buffer1.buffer_, memory_object->array_buffer());
777 :
778 : // Fake the Embedder flow by externalizing the array buffer.
779 10 : ManuallyExternalizedBuffer buffer2(memory_object->array_buffer(), isolate);
780 :
781 : // Grow using an internal WASM bytecode.
782 5 : result = testing::RunWasmModuleForTesting(isolate, instance, 0, nullptr);
783 5 : CHECK_EQ(26, result);
784 5 : CHECK(buffer2.buffer_->was_detached()); // growing always detaches
785 5 : CHECK_EQ(0, buffer2.buffer_->byte_length());
786 15 : CHECK_NE(*buffer2.buffer_, memory_object->array_buffer());
787 : }
788 5 : Cleanup();
789 5 : }
790 :
791 28342 : TEST(Run_WasmModule_Buffer_Externalized_GrowMemMemSize) {
792 : {
793 5 : Isolate* isolate = CcTest::InitIsolateOnce();
794 : HandleScope scope(isolate);
795 : Handle<JSArrayBuffer> buffer;
796 10 : CHECK(wasm::NewArrayBuffer(isolate, 16 * kWasmPageSize).ToHandle(&buffer));
797 : Handle<WasmMemoryObject> mem_obj =
798 5 : WasmMemoryObject::New(isolate, buffer, 100);
799 5 : auto const contents = v8::Utils::ToLocal(buffer)->Externalize();
800 5 : int32_t result = WasmMemoryObject::Grow(isolate, mem_obj, 0);
801 5 : CHECK_EQ(16, result);
802 : constexpr bool is_wasm_memory = true;
803 : const JSArrayBuffer::Allocation allocation{contents.AllocationBase(),
804 : contents.AllocationLength(),
805 5 : contents.Data(), is_wasm_memory};
806 5 : JSArrayBuffer::FreeBackingStore(isolate, allocation);
807 : }
808 5 : Cleanup();
809 5 : }
810 :
811 28342 : TEST(Run_WasmModule_Buffer_Externalized_Detach) {
812 : {
813 : // Regression test for
814 : // https://bugs.chromium.org/p/chromium/issues/detail?id=731046
815 5 : Isolate* isolate = CcTest::InitIsolateOnce();
816 : HandleScope scope(isolate);
817 : Handle<JSArrayBuffer> buffer;
818 10 : CHECK(wasm::NewArrayBuffer(isolate, 16 * kWasmPageSize).ToHandle(&buffer));
819 5 : auto const contents = v8::Utils::ToLocal(buffer)->Externalize();
820 5 : wasm::DetachMemoryBuffer(isolate, buffer, true);
821 : constexpr bool is_wasm_memory = true;
822 : const JSArrayBuffer::Allocation allocation{contents.AllocationBase(),
823 : contents.AllocationLength(),
824 5 : contents.Data(), is_wasm_memory};
825 5 : JSArrayBuffer::FreeBackingStore(isolate, allocation);
826 : }
827 5 : Cleanup();
828 5 : }
829 :
830 28342 : TEST(Run_WasmModule_Buffer_Externalized_Regression_UseAfterFree) {
831 : // Regresion test for https://crbug.com/813876
832 5 : Isolate* isolate = CcTest::InitIsolateOnce();
833 : HandleScope scope(isolate);
834 : Handle<JSArrayBuffer> buffer;
835 10 : CHECK(wasm::NewArrayBuffer(isolate, 16 * kWasmPageSize).ToHandle(&buffer));
836 5 : Handle<WasmMemoryObject> mem = WasmMemoryObject::New(isolate, buffer, 128);
837 5 : auto contents = v8::Utils::ToLocal(buffer)->Externalize();
838 5 : WasmMemoryObject::Grow(isolate, mem, 0);
839 : constexpr bool is_wasm_memory = true;
840 : JSArrayBuffer::FreeBackingStore(
841 : isolate, JSArrayBuffer::Allocation(contents.AllocationBase(),
842 : contents.AllocationLength(),
843 10 : contents.Data(), is_wasm_memory));
844 : // Make sure we can write to the buffer without crashing
845 : uint32_t* int_buffer =
846 10 : reinterpret_cast<uint32_t*>(mem->array_buffer()->backing_store());
847 5 : int_buffer[0] = 0;
848 5 : }
849 :
850 : #if V8_TARGET_ARCH_64_BIT
851 28342 : TEST(Run_WasmModule_Reclaim_Memory) {
852 : // Make sure we can allocate memories without running out of address space.
853 5 : Isolate* isolate = CcTest::InitIsolateOnce();
854 : Handle<JSArrayBuffer> buffer;
855 1285 : for (int i = 0; i < 256; ++i) {
856 : HandleScope scope(isolate);
857 2560 : CHECK(NewArrayBuffer(isolate, kWasmPageSize, SharedFlag::kNotShared)
858 : .ToHandle(&buffer));
859 : }
860 5 : }
861 : #endif
862 :
863 28342 : TEST(AtomicOpDisassembly) {
864 : {
865 : EXPERIMENTAL_FLAG_SCOPE(threads);
866 5 : TestSignatures sigs;
867 5 : Isolate* isolate = CcTest::InitIsolateOnce();
868 10 : v8::internal::AccountingAllocator allocator;
869 10 : Zone zone(&allocator, ZONE_NAME);
870 :
871 5 : WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
872 5 : builder->SetHasSharedMemory();
873 5 : builder->SetMaxMemorySize(16);
874 5 : WasmFunctionBuilder* f = builder->AddFunction(sigs.i_i());
875 5 : ExportAsMain(f);
876 : byte code[] = {
877 5 : WASM_ATOMICS_STORE_OP(kExprI32AtomicStore, WASM_ZERO, WASM_GET_LOCAL(0),
878 : MachineRepresentation::kWord32),
879 5 : WASM_ATOMICS_LOAD_OP(kExprI32AtomicLoad, WASM_ZERO,
880 15 : MachineRepresentation::kWord32)};
881 5 : EMIT_CODE_WITH_END(f, code);
882 :
883 : HandleScope scope(isolate);
884 : ZoneBuffer buffer(&zone);
885 5 : builder->WriteTo(buffer);
886 5 : testing::SetupIsolateForWasmModule(isolate);
887 :
888 5 : ErrorThrower thrower(isolate, "Test");
889 5 : auto enabled_features = WasmFeaturesFromIsolate(isolate);
890 : MaybeHandle<WasmModuleObject> module_object =
891 : isolate->wasm_engine()->SyncCompile(
892 : isolate, enabled_features, &thrower,
893 10 : ModuleWireBytes(buffer.begin(), buffer.end()));
894 :
895 5 : module_object.ToHandleChecked()->DisassembleFunction(0);
896 : }
897 5 : Cleanup();
898 5 : }
899 :
900 : #undef EMIT_CODE_WITH_END
901 :
902 : } // namespace test_run_wasm_module
903 : } // namespace wasm
904 : } // namespace internal
905 85011 : } // namespace v8
|