Bug 1598149 - Report bulk-memory failures to instantiate segments as runtime errors. r=lth

Bulk memory reduces active segments to sequences of *.init that are executed
before the start function is called. This implies that an error here is to be
reported as a RuntimeError, as an error in the start function would. The latest
spec tests for bulk-memory check this, so we're required to update as well.

Differential Revision: https://phabricator.services.mozilla.com/D54598

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Ryan Hunt 2019-11-26 18:24:39 +00:00
parent 68bba5ed9e
commit ad3b40a6b8
3 changed files with 34 additions and 29 deletions

View File

@ -1,8 +1,4 @@
const Module = WebAssembly.Module;
const Instance = WebAssembly.Instance;
const Memory = WebAssembly.Memory;
const Table = WebAssembly.Table;
const LinkError = WebAssembly.LinkError;
const { Module, Instance, Memory, Table, LinkError, RuntimeError } = WebAssembly;
const mem1Page = new Memory({initial:1});
const mem1PageMax1 = new Memory({initial:1, maximum: 1});
@ -19,6 +15,14 @@ const tab2Elem = new Table({initial:2, element:"funcref"});
const tab3Elem = new Table({initial:3, element:"funcref"});
const tab4Elem = new Table({initial:4, element:"funcref"});
function assertSegmentFitError(f) {
if (wasmBulkMemSupported()) {
assertErrorMessage(f, RuntimeError, /out of bounds/);
} else {
assertErrorMessage(f, LinkError, /segment does not fit/);
}
}
// Memory size consistency and internal limits.
assertErrorMessage(() => new Memory({initial:2, maximum:1}), RangeError, /bad Memory maximum size/);
@ -491,15 +495,15 @@ var m = new Module(wasmTextToBinary(`
`));
assertEq(new Instance(m, {glob:{a:0}}) instanceof Instance, true);
assertEq(new Instance(m, {glob:{a:(64*1024 - 2)}}) instanceof Instance, true);
assertErrorMessage(() => new Instance(m, {glob:{a:(64*1024 - 1)}}), LinkError, /data segment does not fit/);
assertErrorMessage(() => new Instance(m, {glob:{a:64*1024}}), LinkError, /data segment does not fit/);
assertSegmentFitError(() => new Instance(m, {glob:{a:(64*1024 - 1)}}));
assertSegmentFitError(() => new Instance(m, {glob:{a:64*1024}}));
var m = new Module(wasmTextToBinary(`
(module
(memory 1)
(data (i32.const 0x10001) "\\0a\\0b"))
`));
assertErrorMessage(() => new Instance(m), LinkError, /data segment does not fit/);
assertSegmentFitError(() => new Instance(m));
var m = new Module(wasmTextToBinary(`
(module
@ -544,9 +548,7 @@ var mem = new Memory({initial:npages});
var mem8 = new Uint8Array(mem.buffer);
var tbl = new Table({initial:2, element:"funcref"});
assertErrorMessage(() => new Instance(m, {a:{mem, tbl, memOff:1, tblOff:2}}),
LinkError,
/elem segment does not fit/);
assertSegmentFitError(() => new Instance(m, {a:{mem, tbl, memOff:1, tblOff:2}}));
if (wasmBulkMemSupported()) {
// The first active element segment is applied, but the second active
// element segment is completely OOB.
@ -562,9 +564,7 @@ assertEq(mem8[1], 0);
tbl.set(0, null);
tbl.set(1, null);
assertErrorMessage(() => new Instance(m, {a:{mem, tbl, memOff:npages*64*1024, tblOff:1}}),
LinkError,
/data segment does not fit/);
assertSegmentFitError(() => new Instance(m, {a:{mem, tbl, memOff:npages*64*1024, tblOff:1}}));
if (wasmBulkMemSupported()) {
// The first and second active element segments are applied fully. The
// first active data segment applies, but the second one is completely OOB.
@ -605,9 +605,7 @@ if (wasmBulkMemSupported()) {
(func $h))`));
let mem = new Memory({initial:1});
let tbl = new Table({initial:3, element:"funcref"});
assertErrorMessage(() => new Instance(m, {"":{mem, tbl}}),
LinkError,
/elem segment does not fit/);
assertSegmentFitError(() => new Instance(m, {"":{mem, tbl}}));
assertEq(tbl.get(0), null);
assertEq(tbl.get(1), null);
assertEq(tbl.get(2), null);
@ -626,9 +624,7 @@ if (wasmBulkMemSupported()) {
(data (i32.const 0) "\\04") ;; is not applied
)`));
let mem = new Memory({initial:1});
assertErrorMessage(() => new Instance(m, {"":{mem}}),
LinkError,
/data segment does not fit/);
assertSegmentFitError(() => new Instance(m, {"":{mem}}));
let v = new Uint8Array(mem.buffer);
assertEq(v[65534], 0);
assertEq(v[65535], 0);

View File

@ -7,6 +7,14 @@ const RuntimeError = WebAssembly.RuntimeError;
const badFuncRefError = /can only pass WebAssembly exported functions to funcref/;
function assertSegmentFitError(f) {
if (wasmBulkMemSupported()) {
assertErrorMessage(f, RuntimeError, /out of bounds/);
} else {
assertErrorMessage(f, LinkError, /segment does not fit/);
}
}
var callee = i => `(func $f${i} (result i32) (i32.const ${i}))`;
wasmFailValidateText(`(module (elem (i32.const 0) $f0) ${callee(0)})`, /elem segment requires a table/);
@ -14,12 +22,12 @@ wasmFailValidateText(`(module (table 10 funcref) (elem (i32.const 0) 0))`, /tabl
wasmFailValidateText(`(module (table 10 funcref) (func) (elem (i32.const 0) 0 1))`, /table element out of range/);
wasmFailValidateText(`(module (table 10 funcref) (func) (elem (f32.const 0) 0) ${callee(0)})`, /type mismatch/);
assertErrorMessage(() => wasmEvalText(`(module (table 10 funcref) (elem (i32.const 10) $f0) ${callee(0)})`), LinkError, /elem segment does not fit/);
assertErrorMessage(() => wasmEvalText(`(module (table 10 funcref) (elem (i32.const 8) $f0 $f0 $f0) ${callee(0)})`), LinkError, /elem segment does not fit/);
assertEq(wasmEvalText(`(module (table 0 funcref) (func) (elem (i32.const 0x10001)))`) instanceof Instance, true);
assertSegmentFitError(() => wasmEvalText(`(module (table 10 funcref) (elem (i32.const 10) $f0) ${callee(0)})`));
assertSegmentFitError(() => wasmEvalText(`(module (table 10 funcref) (elem (i32.const 8) $f0 $f0 $f0) ${callee(0)})`));
assertSegmentFitError(() => wasmEvalText(`(module (table 0 funcref) (func) (elem (i32.const 0x10001)))`));
assertErrorMessage(() => wasmEvalText(`(module (table 10 funcref) (import "globals" "a" (global i32)) (elem (global.get 0) $f0) ${callee(0)})`, {globals:{a:10}}), LinkError, /elem segment does not fit/);
assertErrorMessage(() => wasmEvalText(`(module (table 10 funcref) (import "globals" "a" (global i32)) (elem (global.get 0) $f0 $f0 $f0) ${callee(0)})`, {globals:{a:8}}), LinkError, /elem segment does not fit/);
assertSegmentFitError(() => wasmEvalText(`(module (table 10 funcref) (import "globals" "a" (global i32)) (elem (global.get 0) $f0) ${callee(0)})`, {globals:{a:10}}));
assertSegmentFitError(() => wasmEvalText(`(module (table 10 funcref) (import "globals" "a" (global i32)) (elem (global.get 0) $f0 $f0 $f0) ${callee(0)})`, {globals:{a:8}}));
assertEq(new Module(wasmTextToBinary(`(module (table 10 funcref) (elem (i32.const 1) $f0 $f0) (elem (i32.const 0) $f0) ${callee(0)})`)) instanceof Module, true);
assertEq(new Module(wasmTextToBinary(`(module (table 10 funcref) (elem (i32.const 1) $f0 $f0) (elem (i32.const 2) $f0) ${callee(0)})`)) instanceof Module, true);
@ -61,7 +69,7 @@ var m = new Module(wasmTextToBinary(`
`));
var tbl = new Table({initial:50, element:"funcref"});
assertEq(new Instance(m, {globals:{a:20, table:tbl}}) instanceof Instance, true);
assertErrorMessage(() => new Instance(m, {globals:{a:50, table:tbl}}), LinkError, /elem segment does not fit/);
assertSegmentFitError(() => new Instance(m, {globals:{a:50, table:tbl}}));
var caller = `(type $v2i (func (result i32))) (func $call (param $i i32) (result i32) (call_indirect $v2i (local.get $i))) (export "call" $call)`
var callee = i => `(func $f${i} (type $v2i) (i32.const ${i}))`;

View File

@ -531,7 +531,8 @@ bool Module::initSegments(JSContext* cx, HandleWasmInstanceObject instanceObj,
if (eagerBoundsCheck) {
// Perform all error checks up front so that this function does not perform
// partial initialization if an error is reported.
// partial initialization if an error is reported. In addition, we need to
// to report OOBs as a link error when bulk-memory is disabled.
for (const ElemSegment* seg : elemSegments_) {
if (!seg->active()) {
@ -586,7 +587,7 @@ bool Module::initSegments(JSContext* cx, HandleWasmInstanceObject instanceObj,
uint32_t tableLength = tables[seg->tableIndex]->length();
if (offset > tableLength || tableLength - offset < count) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
JSMSG_WASM_BAD_FIT, "elem", "table");
JSMSG_WASM_OUT_OF_BOUNDS);
return false;
}
}
@ -621,7 +622,7 @@ bool Module::initSegments(JSContext* cx, HandleWasmInstanceObject instanceObj,
if (!eagerBoundsCheck) {
if (offset > memoryLength || memoryLength - offset < count) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
JSMSG_WASM_BAD_FIT, "data", "memory");
JSMSG_WASM_OUT_OF_BOUNDS);
return false;
}
}