Bug 1284156 - Baldr: use ToNonWrappingUint32 for range checks (r=bbouvier)

MozReview-Commit-ID: 1oVQYPlF89J

--HG--
extra : rebase_source : 365a915d22ee8bd72a3d7ed81fa3a910b96d2387
This commit is contained in:
Luke Wagner 2016-09-14 11:54:17 -05:00
parent 6a30ba8b4e
commit 171ec3369e
4 changed files with 65 additions and 89 deletions

View File

@ -330,7 +330,26 @@ js::InitWasmClass(JSContext* cx, HandleObject global)
// Common functions
static bool
GetResizableLimits(JSContext* cx, HandleObject obj, const char* kind, ResizableLimits* limits)
ToNonWrappingUint32(JSContext* cx, HandleValue v, uint32_t max, const char* kind, const char* noun,
uint32_t* u32)
{
double dbl;
if (!ToInteger(cx, v, &dbl))
return false;
if (dbl < 0 || dbl > max) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_UINT32, kind, noun);
return false;
}
*u32 = uint32_t(dbl);
MOZ_ASSERT(double(*u32) == dbl);
return true;
}
static bool
GetResizableLimits(JSContext* cx, HandleObject obj, uint32_t max, const char* kind,
ResizableLimits* limits)
{
JSAtom* initialAtom = Atomize(cx, "initial", strlen("initial"));
if (!initialAtom)
@ -341,17 +360,9 @@ GetResizableLimits(JSContext* cx, HandleObject obj, const char* kind, ResizableL
if (!GetProperty(cx, obj, obj, initialId, &initialVal))
return false;
double initialDbl;
if (!ToInteger(cx, initialVal, &initialDbl))
if (!ToNonWrappingUint32(cx, initialVal, max, kind, "initial size", &limits->initial))
return false;
if (initialDbl < 0 || initialDbl > UINT32_MAX) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_SIZE, kind, "initial");
return false;
}
limits->initial = uint32_t(initialDbl);
JSAtom* maximumAtom = Atomize(cx, "maximum", strlen("maximum"));
if (!maximumAtom)
return false;
@ -363,16 +374,15 @@ GetResizableLimits(JSContext* cx, HandleObject obj, const char* kind, ResizableL
if (!GetProperty(cx, obj, obj, maximumId, &maxVal))
return false;
double maxDbl;
if (!ToInteger(cx, maxVal, &maxDbl))
limits->maximum.emplace();
if (!ToNonWrappingUint32(cx, maxVal, max, kind, "maximum size", limits->maximum.ptr()))
return false;
if (maxDbl < initialDbl || maxDbl > UINT32_MAX) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_SIZE, kind, "maximum");
if (limits->initial > *limits->maximum) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_UINT32,
kind, "maximum size");
return false;
}
limits->maximum = Some(uint32_t(maxDbl));
}
return true;
@ -827,24 +837,12 @@ WasmMemoryObject::construct(JSContext* cx, unsigned argc, Value* vp)
RootedObject obj(cx, &args[0].toObject());
ResizableLimits limits;
if (!GetResizableLimits(cx, obj, "Memory", &limits))
if (!GetResizableLimits(cx, obj, UINT32_MAX / PageSize, "Memory", &limits))
return false;
if (limits.initial > UINT32_MAX / PageSize) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_SIZE, "Memory", "initial");
return false;
}
limits.initial *= PageSize;
if (limits.maximum) {
if (limits.maximum.value() > UINT32_MAX / PageSize) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_SIZE, "Memory", "maximum");
return false;
}
limits.maximum = Some(limits.maximum.value() * PageSize);
}
if (limits.maximum)
limits.maximum = Some(*limits.maximum * PageSize);
RootedArrayBufferObject buffer(cx,
ArrayBufferObject::createForWasm(cx, limits.initial, limits.maximum));
@ -891,16 +889,11 @@ WasmMemoryObject::growImpl(JSContext* cx, const CallArgs& args)
{
RootedWasmMemoryObject memory(cx, &args.thisv().toObject().as<WasmMemoryObject>());
double deltaDbl;
if (!ToInteger(cx, args.get(0), &deltaDbl))
uint32_t delta;
if (!ToNonWrappingUint32(cx, args.get(0), UINT32_MAX, "Memory", "grow delta", &delta))
return false;
if (deltaDbl < 0 || deltaDbl > UINT32_MAX) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_GROW, "memory");
return false;
}
uint32_t ret = grow(memory, uint32_t(deltaDbl), cx);
uint32_t ret = grow(memory, delta, cx);
if (ret == uint32_t(-1)) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_GROW, "memory");
@ -1152,7 +1145,7 @@ WasmTableObject::construct(JSContext* cx, unsigned argc, Value* vp)
}
ResizableLimits limits;
if (!GetResizableLimits(cx, obj, "Table", &limits))
if (!GetResizableLimits(cx, obj, UINT32_MAX, "Table", &limits))
return false;
RootedWasmTableObject table(cx, WasmTableObject::create(cx, limits));
@ -1195,18 +1188,10 @@ WasmTableObject::getImpl(JSContext* cx, const CallArgs& args)
RootedWasmTableObject tableObj(cx, &args.thisv().toObject().as<WasmTableObject>());
const Table& table = tableObj->table();
double indexDbl;
if (!ToInteger(cx, args.get(0), &indexDbl))
uint32_t index;
if (!ToNonWrappingUint32(cx, args.get(0), table.length() - 1, "Table", "get index", &index))
return false;
if (indexDbl < 0 || indexDbl >= table.length()) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
return false;
}
uint32_t index = uint32_t(indexDbl);
MOZ_ASSERT(double(index) == indexDbl);
ExternalTableElem& elem = table.externalArray()[index];
if (!elem.code) {
args.rval().setNull();
@ -1242,18 +1227,10 @@ WasmTableObject::setImpl(JSContext* cx, const CallArgs& args)
if (!args.requireAtLeast(cx, "set", 2))
return false;
double indexDbl;
if (!ToInteger(cx, args[0], &indexDbl))
uint32_t index;
if (!ToNonWrappingUint32(cx, args.get(0), table.length() - 1, "Table", "set index", &index))
return false;
if (indexDbl < 0 || indexDbl >= table.length()) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
return false;
}
uint32_t index = uint32_t(indexDbl);
MOZ_ASSERT(double(index) == indexDbl);
RootedFunction value(cx);
if (!IsExportedFunction(args[1], &value) && !args[1].isNull()) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_TABLE_VALUE);
@ -1295,16 +1272,11 @@ WasmTableObject::growImpl(JSContext* cx, const CallArgs& args)
{
RootedWasmTableObject table(cx, &args.thisv().toObject().as<WasmTableObject>());
double deltaDbl;
if (!ToInteger(cx, args.get(0), &deltaDbl))
uint32_t delta;
if (!ToNonWrappingUint32(cx, args.get(0), UINT32_MAX, "Table", "grow delta", &delta))
return false;
if (deltaDbl < 0 || deltaDbl > UINT32_MAX) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_GROW, "table");
return false;
}
uint32_t ret = table->table().grow(uint32_t(deltaDbl), cx);
uint32_t ret = table->table().grow(delta, cx);
if (ret == uint32_t(-1)) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_GROW, "table");

View File

@ -22,7 +22,7 @@ const tab2Elem = new Table({initial:2, element:"anyfunc"});
const tab3Elem = new Table({initial:3, element:"anyfunc"});
const tab4Elem = new Table({initial:4, element:"anyfunc"});
assertErrorMessage(() => new Memory({initial:2, maximum:1}), TypeError, /bad Memory maximum size/);
assertErrorMessage(() => new Memory({initial:2, maximum:1}), RangeError, /bad Memory maximum size/);
const m1 = new Module(textToBinary('(module (import "foo" "bar") (import "baz" "quux"))'));
assertErrorMessage(() => new Instance(m1), TypeError, /no import object given/);
@ -264,7 +264,7 @@ assertEq(e.tbl1.get(1), null);
assertEq(e.tbl1.get(2), e.tbl1.get(2));
assertEq(e.tbl1.get(2)(), 2);
assertEq(e.tbl1.get(3), null);
assertErrorMessage(() => e.tbl1.get(4), RangeError, /out-of-range index/);
assertErrorMessage(() => e.tbl1.get(4), RangeError, /bad Table get index/);
assertEq(e.tbl1.get(1), null);
e.tbl1.set(1, e.f3);
assertEq(e.tbl1.get(1), e.f3);

View File

@ -117,11 +117,11 @@ assertEq(Memory.name, "Memory");
assertErrorMessage(() => Memory(), TypeError, /constructor without new is forbidden/);
assertErrorMessage(() => new Memory(1), TypeError, "first argument must be a memory descriptor");
assertErrorMessage(() => new Memory({initial:{valueOf() { throw new Error("here")}}}), Error, "here");
assertErrorMessage(() => new Memory({initial:-1}), TypeError, /bad Memory initial size/);
assertErrorMessage(() => new Memory({initial:Math.pow(2,32)}), TypeError, /bad Memory initial size/);
assertErrorMessage(() => new Memory({initial:1, maximum: Math.pow(2,32)/Math.pow(2,14) }), TypeError, /bad Memory maximum size/);
assertErrorMessage(() => new Memory({initial:2, maximum: 1 }), TypeError, /bad Memory maximum size/);
assertErrorMessage(() => new Memory({maximum: -1 }), TypeError, /bad Memory maximum size/);
assertErrorMessage(() => new Memory({initial:-1}), RangeError, /bad Memory initial size/);
assertErrorMessage(() => new Memory({initial:Math.pow(2,32)}), RangeError, /bad Memory initial size/);
assertErrorMessage(() => new Memory({initial:1, maximum: Math.pow(2,32)/Math.pow(2,14) }), RangeError, /bad Memory maximum size/);
assertErrorMessage(() => new Memory({initial:2, maximum:1 }), RangeError, /bad Memory maximum size/);
assertErrorMessage(() => new Memory({maximum: -1 }), RangeError, /bad Memory maximum size/);
assertEq(new Memory({initial:1}) instanceof Memory, true);
assertEq(new Memory({initial:1.5}).buffer.byteLength, WasmPage);
@ -169,8 +169,8 @@ const memGrow = memGrowDesc.value;
assertEq(memGrow.length, 1);
assertErrorMessage(() => memGrow.call(), TypeError, /called on incompatible undefined/);
assertErrorMessage(() => memGrow.call({}), TypeError, /called on incompatible Object/);
assertErrorMessage(() => memGrow.call(mem1, -1), Error, /failed to grow memory/);
assertErrorMessage(() => memGrow.call(mem1, Math.pow(2,32)), Error, /failed to grow memory/);
assertErrorMessage(() => memGrow.call(mem1, -1), RangeError, /bad Memory grow delta/);
assertErrorMessage(() => memGrow.call(mem1, Math.pow(2,32)), RangeError, /bad Memory grow delta/);
var mem = new Memory({initial:1, maximum:2});
var buf = mem.buffer;
assertEq(buf.byteLength, WasmPage);
@ -205,10 +205,14 @@ assertErrorMessage(() => new Table({initial:1, element:1}), TypeError, /must be
assertErrorMessage(() => new Table({initial:1, element:"any"}), TypeError, /must be "anyfunc"/);
assertErrorMessage(() => new Table({initial:1, element:{valueOf() { return "anyfunc" }}}), TypeError, /must be "anyfunc"/);
assertErrorMessage(() => new Table({initial:{valueOf() { throw new Error("here")}}, element:"anyfunc"}), Error, "here");
assertErrorMessage(() => new Table({initial:-1, element:"anyfunc"}), TypeError, /bad Table initial size/);
assertErrorMessage(() => new Table({initial:Math.pow(2,32), element:"anyfunc"}), TypeError, /bad Table initial size/);
assertErrorMessage(() => new Table({initial:-1, element:"anyfunc"}), RangeError, /bad Table initial size/);
assertErrorMessage(() => new Table({initial:Math.pow(2,32), element:"anyfunc"}), RangeError, /bad Table initial size/);
assertErrorMessage(() => new Table({initial:2, maximum:1, element:"anyfunc"}), RangeError, /bad Table maximum size/);
assertErrorMessage(() => new Table({initial:2, maximum:Math.pow(2,32), element:"anyfunc"}), RangeError, /bad Table maximum size/);
assertEq(new Table({initial:1, element:"anyfunc"}) instanceof Table, true);
assertEq(new Table({initial:1.5, element:"anyfunc"}) instanceof Table, true);
assertEq(new Table({initial:1, maximum:1.5, element:"anyfunc"}) instanceof Table, true);
assertEq(new Table({initial:1, maximum:Math.pow(2,32)-1, element:"anyfunc"}) instanceof Table, true);
// 'WebAssembly.Table.prototype' data property
const tableProtoDesc = Object.getOwnPropertyDescriptor(Table, 'prototype');
@ -258,10 +262,10 @@ assertErrorMessage(() => get.call({}), TypeError, /called on incompatible Object
assertEq(get.call(tbl1, 0), null);
assertEq(get.call(tbl1, 1), null);
assertEq(get.call(tbl1, 1.5), null);
assertErrorMessage(() => get.call(tbl1, 2), RangeError, /out-of-range index/);
assertErrorMessage(() => get.call(tbl1, 2.5), RangeError, /out-of-range index/);
assertErrorMessage(() => get.call(tbl1, -1), RangeError, /out-of-range index/);
assertErrorMessage(() => get.call(tbl1, Math.pow(2,33)), RangeError, /out-of-range index/);
assertErrorMessage(() => get.call(tbl1, 2), RangeError, /bad Table get index/);
assertErrorMessage(() => get.call(tbl1, 2.5), RangeError, /bad Table get index/);
assertErrorMessage(() => get.call(tbl1, -1), RangeError, /bad Table get index/);
assertErrorMessage(() => get.call(tbl1, Math.pow(2,33)), RangeError, /bad Table get index/);
assertErrorMessage(() => get.call(tbl1, {valueOf() { throw new Error("hi") }}), Error, "hi");
// 'WebAssembly.Table.prototype.set' data property
@ -276,9 +280,9 @@ assertEq(set.length, 2);
assertErrorMessage(() => set.call(), TypeError, /called on incompatible undefined/);
assertErrorMessage(() => set.call({}), TypeError, /called on incompatible Object/);
assertErrorMessage(() => set.call(tbl1, 0), TypeError, /requires more than 1 argument/);
assertErrorMessage(() => set.call(tbl1, 2, null), RangeError, /out-of-range index/);
assertErrorMessage(() => set.call(tbl1, -1, null), RangeError, /out-of-range index/);
assertErrorMessage(() => set.call(tbl1, Math.pow(2,33), null), RangeError, /out-of-range index/);
assertErrorMessage(() => set.call(tbl1, 2, null), RangeError, /bad Table set index/);
assertErrorMessage(() => set.call(tbl1, -1, null), RangeError, /bad Table set index/);
assertErrorMessage(() => set.call(tbl1, Math.pow(2,33), null), RangeError, /bad Table set index/);
assertErrorMessage(() => set.call(tbl1, 0, undefined), TypeError, /can only assign WebAssembly exported functions to Table/);
assertErrorMessage(() => set.call(tbl1, 0, {}), TypeError, /can only assign WebAssembly exported functions to Table/);
assertErrorMessage(() => set.call(tbl1, 0, function() {}), TypeError, /can only assign WebAssembly exported functions to Table/);
@ -298,8 +302,8 @@ const tblGrow = tblGrowDesc.value;
assertEq(tblGrow.length, 1);
assertErrorMessage(() => tblGrow.call(), TypeError, /called on incompatible undefined/);
assertErrorMessage(() => tblGrow.call({}), TypeError, /called on incompatible Object/);
assertErrorMessage(() => tblGrow.call(tbl1, -1), Error, /failed to grow table/);
assertErrorMessage(() => tblGrow.call(tbl1, Math.pow(2,32)), Error, /failed to grow table/);
assertErrorMessage(() => tblGrow.call(tbl1, -1), RangeError, /bad Table grow delta/);
assertErrorMessage(() => tblGrow.call(tbl1, Math.pow(2,32)), RangeError, /bad Table grow delta/);
var tbl = new Table({element:"anyfunc", initial:1, maximum:2});
assertEq(tbl.length, 1);
assertEq(tbl.grow(0), 1);

View File

@ -355,7 +355,7 @@ MSG_DEF(JSMSG_WASM_BAD_BUF_ARG, 0, JSEXN_TYPEERR, "first argument mus
MSG_DEF(JSMSG_WASM_BAD_MOD_ARG, 0, JSEXN_TYPEERR, "first argument must be a WebAssembly.Module")
MSG_DEF(JSMSG_WASM_BAD_DESC_ARG, 1, JSEXN_TYPEERR, "first argument must be a {0} descriptor")
MSG_DEF(JSMSG_WASM_BAD_IMP_SIZE, 1, JSEXN_TYPEERR, "imported {0} with incompatible size")
MSG_DEF(JSMSG_WASM_BAD_SIZE, 2, JSEXN_TYPEERR, "bad {0} {1} size")
MSG_DEF(JSMSG_WASM_BAD_UINT32, 2, JSEXN_RANGEERR, "bad {0} {1}")
MSG_DEF(JSMSG_WASM_BAD_IMP_MAX, 1, JSEXN_TYPEERR, "imported {0} with incompatible maximum size")
MSG_DEF(JSMSG_WASM_BAD_ELEMENT, 0, JSEXN_TYPEERR, "\"element\" property of table descriptor must be \"anyfunc\"")
MSG_DEF(JSMSG_WASM_BAD_IMPORT_ARG, 0, JSEXN_TYPEERR, "second argument, if present, must be an object")