v8/test/mjsunit/asm/asm-validation.js
Clemens Backes 9b810b9fb1 Reland "[asm] Reject import calls with too many parameters"
This is a reland of commit a664aef0ca.
The test is made ~25x faster by using integer parameters instead of
floating point.

Original change's description:
> [asm] Reject import calls with too many parameters
>
> The asm parser was missing a check for too many parameters for calls to
> imported functions. For regular functions this check implicitly existed
> because the limit was checked at the function declaration, and the call
> site needs to match the declared parameter count.
>
> R=mslekova@chromium.org
>
> Bug: chromium:1302596
> Change-Id: I0d35e70a66d682ee8fdecf5c8ea4d2b1419ce684
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3509393
> Reviewed-by: Maya Lekova <mslekova@chromium.org>
> Commit-Queue: Clemens Backes <clemensb@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#79415}

Bug: chromium:1302596
Change-Id: I138561742b38939a1c2c9a69a6fa508d4f3a028d
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3513613
Reviewed-by: Maya Lekova <mslekova@chromium.org>
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/main@{#79424}
2022-03-09 15:46:32 +00:00

562 lines
13 KiB
JavaScript

// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --validate-asm --allow-natives-syntax
// Note that this test file contains tests that explicitly check modules are
// valid asm.js and then break them with invalid instantiation arguments. If
// this script is run more than once (e.g. --stress-opt) then modules remain
// broken in the second run and assertions would fail. We prevent re-runs.
// Flags: --no-stress-opt
function assertValidAsm(func) {
assertTrue(%IsAsmWasmCode(func));
}
(function TestConst() {
function Module(s) {
"use asm";
var fround = s.Math.fround;
// Global constants. These are treated just like numeric literals.
const fConst = fround(-3.0);
const dConst = -3.0;
const iConst = -3;
// consts can be used to initialize other consts.
const fPrime = fConst;
// The following methods verify that return statements with global constants
// do not need type annotations.
function f() {
return fPrime;
}
function d() {
return dConst;
}
function i() {
return iConst;
}
// The following methods verify that locals initialized with global
// constants do not need type annotations.
function fVar() {
var v = fPrime;
return fround(v);
}
function iVar() {
var v = iConst;
return v|0;
}
function dVar() {
var v = dConst;
return +v;
}
return {
f: f, d: d, i: i,
fVar: fVar, dVar: dVar, iVar: iVar,
};
}
function DisallowAssignToConstGlobal() {
const constant = 0;
function invalid(i) {
i = i|0;
constant = i;
return constant;
}
return invalid;
}
var m = Module(this);
assertValidAsm(Module);
assertEquals(-3, m.i());
assertEquals(-3.0, m.d());
assertEquals(Math.fround(-3.0), m.f());
assertEquals(-3, m.iVar());
assertEquals(-3.0, m.dVar());
assertEquals(Math.fround(-3.0), m.fVar());
var m = DisallowAssignToConstGlobal();
assertFalse(%IsAsmWasmCode(DisallowAssignToConstGlobal));
})();
(function TestModuleArgs() {
function Module1(stdlib) {
"use asm";
function foo() { }
return { foo: foo };
}
function Module2(stdlib, ffi) {
"use asm";
function foo() { }
return { foo: foo };
}
function Module3(stdlib, ffi, heap) {
"use asm";
function foo() { }
return { foo: foo };
}
var modules = [Module1, Module2, Module3];
var heap = new ArrayBuffer(1024 * 1024);
for (var i = 0; i < modules.length; ++i) {
print('Module' + (i + 1));
var module = modules[i];
var m = module();
assertValidAsm(module);
var m = module({});
assertValidAsm(module);
var m = module({}, {});
assertValidAsm(module);
var m = module({}, {}, heap);
assertValidAsm(module);
var m = module({}, {}, heap, {});
assertValidAsm(module);
}
})();
(function TestBadModule() {
function Module(stdlib, ffi, heap) {
"use asm";
function foo() { var y = 3; var x = 1 + y; return 123; }
return { foo: foo };
}
var m = Module({});
assertFalse(%IsAsmWasmCode(Module));
assertEquals(123, m.foo());
})();
(function TestBadArgTypes() {
function Module(a, b, c) {
"use asm";
var NaN = a.NaN;
return {};
}
var m = Module(1, 2, 3);
assertFalse(%IsAsmWasmCode(Module));
assertEquals({}, m);
})();
(function TestBadArgTypesMismatch() {
function Module(a, b, c) {
"use asm";
var NaN = a.NaN;
return {};
}
var m = Module(1, 2);
assertFalse(%IsAsmWasmCode(Module));
assertEquals({}, m);
})();
(function TestModuleNoStdlib() {
function Module() {
"use asm";
function foo() { return 123; }
return { foo: foo };
}
var m = Module({});
assertValidAsm(Module);
assertEquals(123, m.foo());
})();
(function TestModuleWith5() {
function Module(a, b, c, d, e) {
"use asm";
function foo() { return 123; }
return { foo: foo };
}
var heap = new ArrayBuffer(1024 * 1024);
var m = Module({}, {}, heap);
assertFalse(%IsAsmWasmCode(Module));
assertEquals(123, m.foo());
})();
(function TestModuleNoStdlibCall() {
function Module(stdlib, ffi, heap) {
"use asm";
function foo() { return 123; }
return { foo: foo };
}
var m = Module();
assertValidAsm(Module);
assertEquals(123, m.foo());
})();
(function TestModuleNew() {
function Module(stdlib, ffi, heap) {
"use asm";
function foo() { return 123; }
return { foo: foo };
}
var m = new Module({}, {});
assertValidAsm(Module);
assertEquals(123, m.foo());
})();
(function TestMultipleFailures() {
function Module(stdlib) {
"use asm";
var NaN = stdlib.NaN;
function foo() { return 123; }
return { foo: foo };
}
var m1 = Module(1, 2, 3);
assertFalse(%IsAsmWasmCode(Module));
var m2 = Module(1, 2, 3);
assertFalse(%IsAsmWasmCode(Module));
assertEquals(123, m1.foo());
assertEquals(123, m2.foo());
})();
(function TestFailureThenSuccess() {
function MkModule() {
function Module(stdlib, ffi, heap) {
"use asm";
var NaN = stdlib.NaN;
function foo() { return 123; }
return { foo: foo };
}
return Module;
}
var Module1 = MkModule();
var Module2 = MkModule();
var heap = new ArrayBuffer(1024 * 1024);
var m1 = Module1(1, 2, 3);
assertFalse(%IsAsmWasmCode(Module1));
var m2 = Module2({}, {}, heap);
assertFalse(%IsAsmWasmCode(Module2));
assertEquals(123, m1.foo());
assertEquals(123, m2.foo());
})();
(function TestSuccessThenFailure() {
function MkModule() {
function Module(stdlib, ffi, heap) {
"use asm";
var NaN = stdlib.NaN;
function foo() { return 123; }
return { foo: foo };
}
return Module;
}
var Module1 = MkModule();
var Module2 = MkModule();
var heap = new ArrayBuffer(1024 * 1024);
var m1 = Module1({NaN: NaN}, {}, heap);
assertValidAsm(Module1);
var m2 = Module2(1, 2, 3);
assertFalse(%IsAsmWasmCode(Module2));
assertEquals(123, m1.foo());
assertEquals(123, m2.foo());
})();
(function TestSuccessThenFailureThenRetry() {
function MkModule() {
function Module(stdlib, ffi, heap) {
"use asm";
var NaN = stdlib.NaN;
function foo() { return 123; }
return { foo: foo };
}
return Module;
}
var Module1 = MkModule();
var Module2 = MkModule();
var heap = new ArrayBuffer(1024 * 1024);
var m1a = Module1({NaN: NaN}, {}, heap);
assertValidAsm(Module1);
var m2 = Module2(1, 2, 3);
assertFalse(%IsAsmWasmCode(Module2));
var m1b = Module1({NaN: NaN}, {}, heap);
assertFalse(%IsAsmWasmCode(Module1));
assertEquals(123, m1a.foo());
assertEquals(123, m1b.foo());
assertEquals(123, m2.foo());
})();
(function TestBoundFunction() {
function Module(stdlib, ffi, heap) {
"use asm";
function foo() { return 123; }
return { foo: foo };
}
var heap = new ArrayBuffer(1024 * 1024);
var ModuleBound = Module.bind(this, {}, {}, heap);
var m = ModuleBound();
assertValidAsm(Module);
assertEquals(123, m.foo());
})();
(function TestBadConstUnsignedReturn() {
function Module() {
"use asm";
const i = 0xffffffff;
function foo() { return i; }
return { foo: foo };
}
var m = Module();
assertFalse(%IsAsmWasmCode(Module));
assertEquals(0xffffffff, m.foo());
})();
(function TestBadBooleanParamAnnotation() {
function Module() {
"use asm";
function foo(x) {
x = x | true;
return x;
}
return { foo: foo };
}
var m = Module();
assertFalse(%IsAsmWasmCode(Module));
assertEquals(3, m.foo(3));
})();
(function TestBadExportTwice() {
function Module() {
"use asm";
function bar() { return 1; }
function baz() { return 2; }
return {foo: bar, foo: baz};
}
var m = Module();
assertTrue(%IsAsmWasmCode(Module));
assertEquals(2, m.foo());
})();
(function TestBadImport() {
function Module(stdlib) {
"use asm";
var set = 0;
var foo = stdlib[set];
return {};
}
var m = Module(this);
assertFalse(%IsAsmWasmCode(Module));
})();
(function TestBadFroundTrue() {
function Module(stdlib) {
"use asm";
var fround = stdlib.Math.fround;
function foo() {
var x = fround(true);
return +x;
}
return { foo: foo };
}
var m = Module(this);
assertFalse(%IsAsmWasmCode(Module));
assertEquals(1, m.foo());
})();
(function TestBadCase() {
function Module() {
"use asm";
function foo(x) {
x = x | 0;
switch (x|0) {
case true:
return 42;
default:
return 43;
}
return 0;
}
return { foo: foo };
}
var m = Module();
assertFalse(%IsAsmWasmCode(Module));
assertEquals(43, m.foo(3));
})();
(function TestVarHidesExport() {
function Module() {
"use asm";
var foo;
function foo() {}
return foo;
}
Module();
assertFalse(%IsAsmWasmCode(Module));
})();
(function TestUndefinedGlobalCall() {
function Module() {
"use asm";
function foo() {
return bar() | 0;
}
return foo;
}
Module();
assertFalse(%IsAsmWasmCode(Module));
})();
(function TestConditionalReturn() {
function Module() {
'use asm';
function foo(a, b) {
a = +a;
b = +b;
// Allowed, despite not matching the spec, as emscripten emits this in
// practice.
return a == b ? +a : +b;
}
return foo;
}
var m = Module();
assertEquals(4, m(4, 4));
assertEquals(5, m(4, 5));
assertEquals(4, m(5, 4));
assertValidAsm(Module);
})();
(function TestMismatchedConditionalReturn() {
function Module() {
'use asm';
function foo(a, b) {
a = +a;
return a == 0.0 ? 0 : +a;
}
return foo;
}
Module();
assertFalse(%IsAsmWasmCode(Module));
})();
(function TestBadIntConditionalReturn() {
function Module() {
'use asm';
function foo(a, b) {
a = a | 0;
b = b | 0;
// Disallowed because signature must be signed, but these will be int.
return 1 ? a : b;
}
return foo;
}
Module();
assertFalse(%IsAsmWasmCode(Module));
})();
(function TestBadSignedConditionalReturn() {
function Module() {
'use asm';
function foo(a, b) {
a = a | 0;
b = b | 0;
// Disallowed because conditional yields int, even when both sides
// are signed.
return 1 ? a | 0 : b | 0;
}
return foo;
}
Module();
assertFalse(%IsAsmWasmCode(Module));
})();
(function TestAsmIsRegular() {
function Module() {
'use asm';
var g = 123;
function foo() {
return g | 0;
}
return {x: foo};
}
var o = Module();
assertValidAsm(Module);
assertFalse(o instanceof WebAssembly.Instance);
assertTrue(o instanceof Object);
assertTrue(o.__proto__ === Object.prototype);
var p = Object.getOwnPropertyDescriptor(o, "x")
assertTrue(p.writable);
assertTrue(p.enumerable);
assertTrue(p.configurable);
assertTrue(typeof o.x === 'function');
o.x = 5;
assertTrue(typeof o.x === 'number');
assertTrue(o.__single_function__ === undefined);
assertTrue(o.__foreign_init__ === undefined);
})();
(function TestAsmExportOrderPreserved() {
function Module() {
"use asm";
function f() {}
function g() {}
return { a:f, b:g, x:f, c:g, d:f };
}
var m = Module();
assertValidAsm(Module);
var props = Object.getOwnPropertyNames(m);
assertEquals(["a","b","x","c","d"], props);
})();
(function TestDuplicateParameterName() {
function module1(x, x, heap) {
'use asm';
return {};
}
module1({}, {}, new ArrayBuffer(4096));
assertFalse(%IsAsmWasmCode(module1));
function module2(x, ffi, x) {
'use asm';
return {};
}
module2({}, {}, new ArrayBuffer(4096));
assertFalse(%IsAsmWasmCode(module2));
function module3(stdlib, x, x) {
'use asm';
return {};
}
module3({}, {}, new ArrayBuffer(4096));
assertFalse(%IsAsmWasmCode(module3));
// Regression test for https://crbug.com/1068355.
function regress1068355(ffi, ffi, heap) {
'use asm';
var result = new ffi.Uint8Array(heap);
function bar() {}
return {f: bar};
}
let heap = new ArrayBuffer(4096);
assertThrows(
() => regress1068355({Uint8Array: Uint8Array}, {}, heap), TypeError,
/Uint8Array is not a constructor/);
assertFalse(%IsAsmWasmCode(regress1068355));
})();
(function TestTooManyParametersToImport() {
function MakeModule(num_arguments) {
let template = `
'use asm';
var imported = foreign.imported;
function main() {
imported(ARGS);
}
return main;
`;
let args = new Array(num_arguments).fill('0').join(', ');
return new Function('stdlib', 'foreign', template.replace('ARGS', args));
}
// V8 has an internal limit of 1000 parameters (see wasm-limits.h).
let Module1000Params = MakeModule(1000);
let Module1001Params = MakeModule(1001);
Module1000Params({}, {imported: i => i});
Module1001Params({}, {imported: i => i});
assertTrue(%IsAsmWasmCode(Module1000Params));
assertFalse(%IsAsmWasmCode(Module1001Params));
})();