mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 795227 - ParallelArray should check length range like Array (r=dvander)
This commit is contained in:
parent
e85ed8da8f
commit
117a973833
@ -45,6 +45,26 @@ ReportBadArg(JSContext *cx, const char *s = "")
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
ReportBadLength(JSContext *cx)
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
ReportBadLengthOrArg(JSContext *cx, HandleValue v, const char *s = "")
|
||||
{
|
||||
return v.isNumber() ? ReportBadLength(cx) : ReportBadArg(cx, s);
|
||||
}
|
||||
|
||||
static bool
|
||||
ReportBadPartition(JSContext *cx)
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_BAD_PARTITION);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
ParallelArrayObject::IndexInfo::isInitialized() const
|
||||
{
|
||||
@ -89,6 +109,68 @@ CloseDelimiters(const IndexInfo &iv, StringBuffer &sb)
|
||||
return true;
|
||||
}
|
||||
|
||||
// A version of ToUint32 that reports if the input value is malformed: either
|
||||
// it is given to us as a negative integer or it overflows.
|
||||
static bool
|
||||
ToUint32(JSContext *cx, const Value &v, uint32_t *out, bool *malformed)
|
||||
{
|
||||
AssertArgumentsAreSane(cx, v);
|
||||
{
|
||||
js::SkipRoot skip(cx, &v);
|
||||
js::MaybeCheckStackRoots(cx);
|
||||
}
|
||||
|
||||
*malformed = false;
|
||||
|
||||
if (v.isInt32()) {
|
||||
int32_t i = v.toInt32();
|
||||
if (i < 0) {
|
||||
*malformed = true;
|
||||
return true;
|
||||
}
|
||||
*out = static_cast<uint32_t>(i);
|
||||
return true;
|
||||
}
|
||||
|
||||
double d;
|
||||
if (v.isDouble()) {
|
||||
d = v.toDouble();
|
||||
} else {
|
||||
if (!ToNumberSlow(cx, v, &d))
|
||||
return false;
|
||||
}
|
||||
|
||||
*out = ToUint32(d);
|
||||
|
||||
if (!MOZ_DOUBLE_IS_FINITE(d) || d != static_cast<double>(*out)) {
|
||||
*malformed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetLength(JSContext *cx, HandleObject obj, uint32_t *length)
|
||||
{
|
||||
// If obj's length cannot overflow, just use GetLengthProperty.
|
||||
if (obj->isArray() || obj->isArguments())
|
||||
return GetLengthProperty(cx, obj, length);
|
||||
|
||||
// Otherwise check that we don't overflow uint32.
|
||||
RootedValue value(cx);
|
||||
if (!JSObject::getProperty(cx, obj, obj, cx->names().length, &value))
|
||||
return false;
|
||||
|
||||
bool malformed;
|
||||
if (!ToUint32(cx, value, length, &malformed))
|
||||
return false;
|
||||
if (malformed)
|
||||
return ReportBadLengthOrArg(cx, value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if obj is a parallel array, and if so, cast to pa and initialize
|
||||
// the IndexInfo accordingly.
|
||||
//
|
||||
@ -104,7 +186,7 @@ MaybeGetParallelArrayObjectAndLength(JSContext *cx, HandleObject obj,
|
||||
if (!pa->isOneDimensional() && !iv->initialize(cx, pa, 1))
|
||||
return false;
|
||||
*length = pa->outermostDimension();
|
||||
} else if (!GetLengthProperty(cx, obj, length)) {
|
||||
} else if (!GetLength(cx, obj, length)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -238,12 +320,15 @@ NewDenseArrayWithType(JSContext *cx, uint32_t length)
|
||||
// Copy an array like object obj into an IndexVector, indices, using
|
||||
// ToUint32.
|
||||
static inline bool
|
||||
ArrayLikeToIndexVector(JSContext *cx, HandleObject obj, IndexVector &indices)
|
||||
ArrayLikeToIndexVector(JSContext *cx, HandleObject obj, IndexVector &indices,
|
||||
bool *malformed)
|
||||
{
|
||||
IndexInfo iv(cx);
|
||||
RootedParallelArrayObject pa(cx);
|
||||
uint32_t length;
|
||||
|
||||
*malformed = false;
|
||||
|
||||
if (!MaybeGetParallelArrayObjectAndLength(cx, obj, &pa, &iv, &length))
|
||||
return false;
|
||||
|
||||
@ -251,12 +336,16 @@ ArrayLikeToIndexVector(JSContext *cx, HandleObject obj, IndexVector &indices)
|
||||
return false;
|
||||
|
||||
RootedValue elem(cx);
|
||||
bool malformed_;
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
if (!GetElementFromArrayLikeObject(cx, obj, pa, iv, i, &elem) ||
|
||||
!ToUint32(cx, elem, &indices[i]))
|
||||
!ToUint32(cx, elem, &indices[i], &malformed_))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (malformed_)
|
||||
*malformed = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -514,13 +603,19 @@ ParallelArrayObject::SequentialMode::scatter(JSContext *cx, HandleParallelArrayO
|
||||
RootedValue targetElem(cx);
|
||||
for (uint32_t i = 0; i < Min(targetsLength, source->outermostDimension()); i++) {
|
||||
uint32_t targetIndex;
|
||||
bool malformed;
|
||||
|
||||
if (!GetElementFromArrayLikeObject(cx, targets, targetsPA, tiv, i, &telem) ||
|
||||
!ToUint32(cx, telem, &targetIndex))
|
||||
!ToUint32(cx, telem, &targetIndex, &malformed))
|
||||
{
|
||||
return ExecutionFailed;
|
||||
}
|
||||
|
||||
if (malformed) {
|
||||
ReportBadArg(cx, ".prototype.scatter");
|
||||
return ExecutionFailed;
|
||||
}
|
||||
|
||||
if (targetIndex >= length) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_SCATTER_BOUNDS);
|
||||
return ExecutionFailed;
|
||||
@ -1109,7 +1204,7 @@ ParallelArrayObject::construct(JSContext *cx, unsigned argc, Value *vp)
|
||||
// When using an array value we can only make one dimensional arrays.
|
||||
IndexVector dims(cx);
|
||||
uint32_t length;
|
||||
if (!dims.resize(1) || !GetLengthProperty(cx, source, &length))
|
||||
if (!dims.resize(1) || !GetLength(cx, source, &length))
|
||||
return false;
|
||||
dims[0] = length;
|
||||
|
||||
@ -1128,13 +1223,23 @@ ParallelArrayObject::construct(JSContext *cx, unsigned argc, Value *vp)
|
||||
// If the length is an array-like object of sizes, the i-th value in the
|
||||
// dimension array is the size of the i-th dimension.
|
||||
IndexInfo iv(cx);
|
||||
bool malformed;
|
||||
if (args[0].isObject()) {
|
||||
RootedObject dimObj(cx, &(args[0].toObject()));
|
||||
if (!ArrayLikeToIndexVector(cx, dimObj, iv.dimensions))
|
||||
if (!ArrayLikeToIndexVector(cx, dimObj, iv.dimensions, &malformed))
|
||||
return false;
|
||||
if (malformed)
|
||||
return ReportBadLength(cx);
|
||||
} else {
|
||||
if (!iv.dimensions.resize(1) || !ToUint32(cx, args[0], &iv.dimensions[0]))
|
||||
if (!iv.dimensions.resize(1))
|
||||
return false;
|
||||
|
||||
if (!ToUint32(cx, args[0], &iv.dimensions[0], &malformed))
|
||||
return false;
|
||||
if (malformed) {
|
||||
RootedValue arg0(cx, args[0]);
|
||||
return ReportBadLengthOrArg(cx, arg0);
|
||||
}
|
||||
}
|
||||
|
||||
// If the first argument wasn't a array-like or had no length, assume
|
||||
@ -1146,14 +1251,20 @@ ParallelArrayObject::construct(JSContext *cx, unsigned argc, Value *vp)
|
||||
if (!iv.initialize(iv.dimensions.length()))
|
||||
return false;
|
||||
|
||||
// We checked that each individual dimension does not overflow; now check
|
||||
// that the scalar length does not overflow.
|
||||
uint32_t length = iv.scalarLengthOfDimensions();
|
||||
double d = iv.dimensions[0];
|
||||
for (uint32_t i = 1; i < iv.dimensions.length(); i++)
|
||||
d *= iv.dimensions[i];
|
||||
if (d != static_cast<double>(length))
|
||||
return ReportBadLength(cx);
|
||||
|
||||
// Extract second argument, the elemental function.
|
||||
RootedObject elementalFun(cx, ValueToCallable(cx, &args[1]));
|
||||
if (!elementalFun)
|
||||
return false;
|
||||
|
||||
// How long the flattened array will be.
|
||||
uint32_t length = iv.scalarLengthOfDimensions();
|
||||
|
||||
// Create backing store.
|
||||
RootedObject buffer(cx, NewDenseArrayWithType(cx, length));
|
||||
if (!buffer)
|
||||
@ -1327,8 +1438,13 @@ ParallelArrayObject::scatter(JSContext *cx, CallArgs args)
|
||||
// of the source array.
|
||||
uint32_t resultLength;
|
||||
if (args.length() >= 4) {
|
||||
if (!ToUint32(cx, args[3], &resultLength))
|
||||
bool malformed;
|
||||
if (!ToUint32(cx, args[3], &resultLength, &malformed))
|
||||
return false;
|
||||
if (malformed) {
|
||||
RootedValue arg3(cx, args[3]);
|
||||
return ReportBadLengthOrArg(cx, arg3, ".prototype.scatter");
|
||||
}
|
||||
} else {
|
||||
resultLength = outer;
|
||||
}
|
||||
@ -1427,17 +1543,18 @@ ParallelArrayObject::partition(JSContext *cx, CallArgs args)
|
||||
return ReportMoreArgsNeeded(cx, "ParallelArray.prototype.partition", "0", "s");
|
||||
|
||||
uint32_t newDimension;
|
||||
if (!ToUint32(cx, args[0], &newDimension))
|
||||
bool malformed;
|
||||
if (!ToUint32(cx, args[0], &newDimension, &malformed))
|
||||
return false;
|
||||
if (malformed)
|
||||
return ReportBadPartition(cx);
|
||||
|
||||
RootedParallelArrayObject obj(cx, as(&args.thisv().toObject()));
|
||||
|
||||
// Throw if the outer dimension is not divisible by the new dimension.
|
||||
uint32_t outer = obj->outermostDimension();
|
||||
if (newDimension == 0 || outer % newDimension) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_BAD_PARTITION);
|
||||
return false;
|
||||
}
|
||||
if (newDimension == 0 || outer % newDimension)
|
||||
return ReportBadPartition(cx);
|
||||
|
||||
IndexVector dims(cx);
|
||||
if (!obj->getDimensions(cx, dims))
|
||||
@ -1469,13 +1586,21 @@ ParallelArrayObject::get(JSContext *cx, CallArgs args)
|
||||
IndexInfo iv(cx);
|
||||
if (!iv.initialize(cx, obj, 0))
|
||||
return false;
|
||||
if (!ArrayLikeToIndexVector(cx, indicesObj, iv.indices))
|
||||
|
||||
bool malformed;
|
||||
if (!ArrayLikeToIndexVector(cx, indicesObj, iv.indices, &malformed))
|
||||
return false;
|
||||
|
||||
// Throw if the shape of the index vector is wrong.
|
||||
if (iv.indices.length() == 0 || iv.indices.length() > iv.dimensions.length())
|
||||
return ReportBadArg(cx, ".prototype.get");
|
||||
|
||||
// Don't throw on overflow, just return undefined.
|
||||
if (malformed) {
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
return obj->getParallelArrayElement(cx, iv, args.rval());
|
||||
}
|
||||
|
||||
|
@ -5,9 +5,21 @@ function buildComprehension() {
|
||||
assertThrowsInstanceOf(function () {
|
||||
var p = new ParallelArray([2,2], undefined);
|
||||
}, TypeError);
|
||||
assertThrowsInstanceOf(function () {
|
||||
var p = new ParallelArray(2, /x/);
|
||||
}, TypeError);
|
||||
assertThrowsInstanceOf(function () {
|
||||
var p = new ParallelArray(/x/, /x/);
|
||||
}, TypeError);
|
||||
assertThrowsInstanceOf(function () {
|
||||
new ParallelArray([0xffffffff + 1], function() { return 0; });
|
||||
}, RangeError);
|
||||
assertThrowsInstanceOf(function () {
|
||||
new ParallelArray(0xffffffff + 1, function() { return 0; });
|
||||
}, RangeError);
|
||||
assertThrowsInstanceOf(function () {
|
||||
new ParallelArray([0xfffff, 0xffff], function() { return 0; });
|
||||
}, RangeError);
|
||||
}
|
||||
|
||||
buildComprehension();
|
||||
|
@ -0,0 +1,9 @@
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
function testThrows() {
|
||||
assertThrowsInstanceOf(function () {
|
||||
new ParallelArray({ length: 0xffffffff + 1 });
|
||||
}, RangeError);
|
||||
}
|
||||
|
||||
testThrows();
|
11
js/src/jit-test/tests/parallelarray/filter-throws.js
Normal file
11
js/src/jit-test/tests/parallelarray/filter-throws.js
Normal file
@ -0,0 +1,11 @@
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
function testFilterThrows() {
|
||||
var p = new ParallelArray([1,2,3,4,5]);
|
||||
|
||||
assertThrowsInstanceOf(function () {
|
||||
p.filter({ length: 0xffffffff + 1 });
|
||||
}, RangeError);
|
||||
}
|
||||
|
||||
testFilterThrows();
|
12
js/src/jit-test/tests/parallelarray/overflow-throws.js
Normal file
12
js/src/jit-test/tests/parallelarray/overflow-throws.js
Normal file
@ -0,0 +1,12 @@
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
function testOverflow() {
|
||||
assertThrowsInstanceOf(function () {
|
||||
new ParallelArray([0xffffffff + 1], function() { return 0; });
|
||||
}, RangeError);
|
||||
assertThrowsInstanceOf(function () {
|
||||
new ParallelArray({ length: 0xffffffff + 1 });
|
||||
}, RangeError);
|
||||
}
|
||||
|
||||
testOverflow();
|
@ -1,16 +1,30 @@
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
function testScatterThrows() {
|
||||
var p = new ParallelArray([1,2,3,4,5]);
|
||||
|
||||
// Throw on conflict with no resolution function
|
||||
assertThrowsInstanceOf(function () {
|
||||
var p = new ParallelArray([1,2,3,4,5]);
|
||||
var r = p.scatter([0,1,0,3,4]);
|
||||
}, Error);
|
||||
// Throw on out of bounds
|
||||
assertThrowsInstanceOf(function () {
|
||||
var p = new ParallelArray([1,2,3,4,5]);
|
||||
var r = p.scatter([0,1,0,3,11]);
|
||||
}, Error);
|
||||
|
||||
assertThrowsInstanceOf(function () {
|
||||
p.scatter([-1,1,0,3,4], 9, function (a,b) { return a+b; }, 10);
|
||||
}, TypeError);
|
||||
assertThrowsInstanceOf(function () {
|
||||
p.scatter([0,1,0,3,4], 9, function (a,b) { return a+b; }, -1);
|
||||
}, RangeError);
|
||||
assertThrowsInstanceOf(function () {
|
||||
p.scatter([0,1,0,3,4], 9, function (a,b) { return a+b; }, 0xffffffff + 1);
|
||||
}, RangeError);
|
||||
assertThrowsInstanceOf(function () {
|
||||
p.scatter({ length: 0xffffffff + 1 }, 9, function (a,b) { return a+b; }, 10);
|
||||
}, RangeError);
|
||||
|
||||
}
|
||||
|
||||
testScatterThrows();
|
||||
|
Loading…
Reference in New Issue
Block a user