//------------------------------------------------------------------------------------------------------- // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- // Limits taken from WasmLimits.h const MaxTypes = 1000000; const MaxFunctions = 1000000; const MaxImports = 100000; const MaxExports = 100000; const MaxGlobals = 1000000; const MaxDataSegments = 100000; const MaxElementSegments = 10000000; const MaxTableSize = 10000000; const MaxStringSize = 100000; const MaxFunctionLocals = 50000; const MaxFunctionParams = 1000; const MaxBrTableElems = 1000000; const MaxMemoryInitialPages = 32767; const MaxMemoryMaximumPages = 65536; const MaxModuleSize = 1024 * 1024 * 1024; const MaxFunctionSize = 7654321; /* global assert,testRunner */ // eslint rule WScript.LoadScriptFile("../UnitTestFramework/UnitTestFramework.js"); WScript.LoadScriptFile("../WasmSpec/testsuite/harness/wasm-constants.js"); WScript.LoadScriptFile("../WasmSpec/testsuite/harness/wasm-module-builder.js"); WScript.Flag("-off:wasmdeferred"); function compile(moduleStr) { const buf = WebAssembly.wabt.convertWast2Wasm(moduleStr); return new WebAssembly.Module(buf); } const notTooLongName = "a".repeat(MaxStringSize); const tooLongName = "a".repeat(MaxStringSize + 1); function makeLimitTests(opt) { return [{ name: "valid: " + opt.name, validTest: true, body() { const builder = new WasmModuleBuilder(); opt.makeModule(builder, opt.limit); assert.doesNotThrow(() => new WebAssembly.Module(builder.toBuffer()), `WebAssembly should allow up to ${opt.limit} ${opt.type}`); } }, { name: "invalid: " + opt.name, invalidTest: true, body() { const builder = new WasmModuleBuilder(); opt.makeModule(builder, opt.limit + 1); assert.throws(() => new WebAssembly.Module(builder.toBuffer()), WebAssembly.CompileError, `WebAssembly should not allow ${opt.limit + 1} ${opt.type}`, opt.errorMsg); } }]; } const tests = [ // Tests 0,1 ...makeLimitTests({ name: "test max types", limit: MaxTypes, type: "types", errorMsg: "Too many signatures", makeModule: (builder, limit) => {for (let i = 0; i < limit; ++i) {builder.addType(kSig_v_v);}} }), // Tests 2,3 ...makeLimitTests({ name: "test max functions", limit: MaxFunctions, type: "functions", errorMsg: "Too many functions", makeModule: (builder, limit) => { const typeIndex = builder.addType(kSig_v_v); for (let i = 0; i < limit; ++i) {builder.addFunction(null, typeIndex).addBody([]);} } }), // Tests 4,5 ...makeLimitTests({ name: "test max imports", limit: MaxImports, type: "imports", errorMsg: "Too many imports", makeModule: (builder, limit) => { const typeIndex = builder.addType(kSig_v_v); for (let i = 0; i < limit; ++i) {builder.addImport("", "", typeIndex);} } }), // Tests 6,7 ...makeLimitTests({ name: "test max exports", limit: MaxExports, type: "exports", errorMsg: "Too many exports", makeModule: (builder, limit) => { builder.addFunction(null, kSig_v_v).addBody([]); for (let i = 0; i < limit; ++i) {builder.addExport("" + i, 0);} } }), // Tests 8,9 ...makeLimitTests({ name: "test max globals", limit: MaxGlobals, type: "globals", errorMsg: "Too many globals", makeModule: (builder, limit) => {for (let i = 0; i < limit; ++i) {builder.addGlobal(kWasmI32, /* mutable */ false);}} }), // Tests 10,11 ...makeLimitTests({ name: "test max data segments", limit: MaxDataSegments, type: "data segments", errorMsg: "Too many data segments", makeModule: (builder, limit) => { builder.addMemory(0); for (let i = 0; i < limit; ++i) {builder.addDataSegment(0, "");} } }), // Tests 12,13 ...makeLimitTests({ name: "test max element segments", limit: MaxElementSegments, type: "element segments", errorMsg: "Too many element segments", makeModule: (builder, limit) => { builder.addFunction(null, kSig_v_v).addBody([]); builder.setTableLength(1); builder.element_segments.length = limit; builder.element_segments.fill({base:0, is_global:false, array:[0]}); } }), // Test 14 { name: "test max table size", body() { assert.doesNotThrow(() => new WebAssembly.Table({element: "anyfunc", initial: MaxTableSize})); assert.doesNotThrow(() => new WebAssembly.Table({element: "anyfunc", initial: MaxTableSize, maximum: MaxTableSize})); assert.throws(() => new WebAssembly.Table({element: "anyfunc", maximum: MaxTableSize})); assert.throws(() => new WebAssembly.Table({element: "anyfunc", initial: MaxTableSize + 1})); assert.throws(() => new WebAssembly.Table({element: "anyfunc", initial: MaxTableSize + 1, maximum: MaxTableSize + 1})); assert.throws(() => new WebAssembly.Table({element: "anyfunc", maximum: MaxTableSize + 1})); assert.doesNotThrow(() => compile(`(module (table ${MaxTableSize} anyfunc))`)); assert.doesNotThrow(() => compile(`(module (table ${MaxTableSize} ${MaxTableSize} anyfunc))`)); assert.doesNotThrow(() => compile(`(module (table 0 ${MaxTableSize} anyfunc))`)); assert.throws(() => compile(`(module (table ${MaxTableSize + 1} anyfunc))`), WebAssembly.CompileError, "table too big"); assert.throws(() => compile(`(module (table ${MaxTableSize + 1} ${MaxTableSize + 1} anyfunc))`), WebAssembly.CompileError, "table too big"); assert.throws(() => compile(`(module (table 0 ${MaxTableSize + 1} anyfunc))`), WebAssembly.CompileError, "table too big"); } }, // Test 15 { name: "test custom sections with long names", body() { function makeCustomSection(name) { const customSection = new Binary(); customSection.emit_section(0, section => { section.emit_string(name) section.emit_string("payload") }); const builder = new WasmModuleBuilder(); builder.addExplicitSection(customSection); return new WebAssembly.Module(builder.toBuffer()); } assert.doesNotThrow(() => makeCustomSection(notTooLongName)); assert.throws(() => makeCustomSection(tooLongName), WebAssembly.CompileError, "Name too long"); } }, // Test 16 { name: "test exports with long names", body() { function makeExportName(name) { const builder = new WasmModuleBuilder(); builder .addFunction(name, kSig_v_v) .exportFunc() .addBody([]); return new WebAssembly.Module(builder.toBuffer()); } assert.doesNotThrow(() => makeExportName(notTooLongName)); assert.throws(() => makeExportName(tooLongName), WebAssembly.CompileError, "Name too long"); } }, // Test 17 { name: "test imports with long names", body() { function makeImportName(name) { const builder = new WasmModuleBuilder(); builder.addImport(name, 'b', kSig_v_v); return new WebAssembly.Module(builder.toBuffer()); } assert.doesNotThrow(() => makeImportName(notTooLongName)); assert.throws(() => makeImportName(tooLongName), WebAssembly.CompileError, "Name too long"); } }, // Test 18 { name: "test Name section with long names", body() { function makeName(name) { const builder = new WasmModuleBuilder(); builder .addFunction(notTooLongName, kSig_v_v) .addBody([]); return new WebAssembly.Module(builder.toBuffer()); } assert.doesNotThrow(() => makeName(notTooLongName)); // todo:: enable test once we start using the Name section // assert.throws(() => makeName(tooLongName), WebAssembly.CompileError, "Name too long"); } }, // Tests 19,20 ...makeLimitTests({ name: "test max function locals", limit: MaxFunctionLocals, type: "locals", makeModule: (builder, limit) => { builder.addFunction(null, kSig_v_v).addLocals({i32_count: limit}).addBody([]); } }), // Tests 21,22 ...makeLimitTests({ name: "test max function params", limit: MaxFunctionParams, type: "params", errorMsg: "Too many arguments in signature", makeModule: (builder, limit) => { builder.addFunction(null, {params: (new Array(limit)).fill(kWasmI32), results: []}).addBody([]); } }), // Tests 23 { name: "test max memory pages", body() { // We can't actually allocate so much memory on a test machine and except things to go well //assert.doesNotThrow(() => new WebAssembly.Memory({initial: MaxMemoryInitialPages})); //assert.doesNotThrow(() => new WebAssembly.Memory({initial: MaxMemoryInitialPages, maximum: MaxMemoryMaximumPages})); //assert.doesNotThrow(() => new WebAssembly.Memory({maximum: MaxMemoryMaximumPages})); assert.throws(() => new WebAssembly.Memory({initial: MaxMemoryInitialPages + 1})); assert.throws(() => new WebAssembly.Memory({initial: MaxMemoryInitialPages + 1, maximum: MaxMemoryMaximumPages + 1})); assert.throws(() => new WebAssembly.Memory({maximum: MaxMemoryMaximumPages + 1})); const makeModule = (min, max) => { const builder = new WasmModuleBuilder(); builder.addMemory(min, max); return new WebAssembly.Module(builder.toBuffer()); }; assert.doesNotThrow(() => makeModule(MaxMemoryInitialPages)); assert.doesNotThrow(() => makeModule(MaxMemoryInitialPages, MaxMemoryMaximumPages)); assert.doesNotThrow(() => makeModule(0, MaxMemoryMaximumPages)); assert.throws(() => makeModule(MaxMemoryInitialPages + 1), WebAssembly.CompileError, "Minimum memory size too big"); assert.throws(() => makeModule(MaxMemoryInitialPages + 1, MaxMemoryMaximumPages + 1), WebAssembly.CompileError, "Minimum memory size too big"); assert.throws(() => makeModule(0, MaxMemoryMaximumPages + 1), WebAssembly.CompileError, "Maximum memory size too big"); } }, // Tests 24 { name: "test max module size", body() { assert.throws(() => new WebAssembly.Module(new ArrayBuffer(MaxModuleSize)), WebAssembly.CompileError, "Malformed WASM module header"); assert.throws(() => new WebAssembly.Module(new ArrayBuffer(MaxModuleSize + 1)), WebAssembly.CompileError, "Module too big"); } }, // Tests 25,26 ...makeLimitTests({ name: "test max function size", limit: MaxFunctionSize, type: "size", errorMsg: "Function body too big", makeModule: (builder, limit) => { // limit -1 (number of locals byte) -1 (endOpCode) builder.addFunction(null, kSig_v_v).addBody((new Array(limit - 2)).fill(kExprNop)); } }), // todo:: test MaxBrTableElems ]; WScript.LoadScriptFile("../UnitTestFramework/yargs.js"); const argv = yargsParse(WScript.Arguments, { boolean: ["valid", "invalid", "verbose"], number: ["start", "end"], default: { verbose: true, valid: true, invalid: true, start: 0, end: tests.length } }).argv; const todoTests = tests .slice(argv.start, argv.end) .filter(test => (!test.invalidTest || argv.invalid) && (!test.validTest || argv.valid)); testRunner.run(todoTests, {verbose: argv.verbose});