Bug 653175 - An array index with a null character could wrongly be considered to be valid. r=jorendorff

This commit is contained in:
Tom Schuster
2011-08-06 00:37:54 +02:00
parent 2a6479b9e6
commit 6967f6f11e
5 changed files with 92 additions and 59 deletions

View File

@@ -120,10 +120,6 @@
using namespace js;
using namespace js::gc;
/* 2^32 - 1 as a number and a string */
#define MAXINDEX 4294967295u
#define MAXSTR "4294967295"
static inline bool
ENSURE_SLOW_ARRAY(JSContext *cx, JSObject *obj)
{
@@ -131,54 +127,6 @@ ENSURE_SLOW_ARRAY(JSContext *cx, JSObject *obj)
obj->makeDenseArraySlow(cx);
}
/*
* Determine if the id represents an array index or an XML property index.
*
* An id is an array index according to ECMA by (15.4):
*
* "Array objects give special treatment to a certain class of property names.
* A property name P (in the form of a string value) is an array index if and
* only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal
* to 2^32-1."
*
* In our implementation, it would be sufficient to check for JSVAL_IS_INT(id)
* except that by using signed 31-bit integers we miss the top half of the
* valid range. This function checks the string representation itself; note
* that calling a standard conversion routine might allow strings such as
* "08" or "4.0" as array indices, which they are not.
*
* 'id' is passed as a jsboxedword since the given id need not necessarily hold
* an atomized string.
*/
bool
js_StringIsIndex(JSLinearString *str, jsuint *indexp)
{
const jschar *cp = str->chars();
if (JS7_ISDEC(*cp) && str->length() < sizeof(MAXSTR)) {
jsuint index = JS7_UNDEC(*cp++);
jsuint oldIndex = 0;
jsuint c = 0;
if (index != 0) {
while (JS7_ISDEC(*cp)) {
oldIndex = index;
c = JS7_UNDEC(*cp);
index = 10*index + c;
cp++;
}
}
/* Ensure that all characters were consumed and we didn't overflow. */
if (*cp == 0 &&
(oldIndex < (MAXINDEX / 10) ||
(oldIndex == (MAXINDEX / 10) && c < (MAXINDEX % 10))))
{
*indexp = index;
return true;
}
}
return false;
}
JSBool
js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
{
@@ -208,6 +156,66 @@ js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
return ValueToECMAUint32(cx, tvr.value(), (uint32_t *)lengthp);
}
namespace js {
/*
* Determine if the id represents an array index or an XML property index.
*
* An id is an array index according to ECMA by (15.4):
*
* "Array objects give special treatment to a certain class of property names.
* A property name P (in the form of a string value) is an array index if and
* only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal
* to 2^32-1."
*
* This means the largest allowed index is actually 2^32-2 (4294967294).
*
* In our implementation, it would be sufficient to check for JSVAL_IS_INT(id)
* except that by using signed 31-bit integers we miss the top half of the
* valid range. This function checks the string representation itself; note
* that calling a standard conversion routine might allow strings such as
* "08" or "4.0" as array indices, which they are not.
*
*/
bool
StringIsArrayIndex(JSLinearString *str, jsuint *indexp)
{
const jschar *s = str->chars();
uint32 length = str->length();
const jschar *end = s + length;
if (length == 0 || length > (sizeof("4294967294") - 1) || !JS7_ISDEC(*s))
return false;
uint32 c = 0, previous = 0;
uint32 index = JS7_UNDEC(*s++);
/* Don't allow leading zeros. */
if (index == 0 && s != end)
return false;
for (; s < end; s++) {
if (!JS7_ISDEC(*s))
return false;
previous = index;
c = JS7_UNDEC(*s);
index = 10 * index + c;
}
/* Make sure we didn't overflow. */
if (previous < (MAX_ARRAY_INDEX / 10) || (previous == (MAX_ARRAY_INDEX / 10) &&
c <= (MAX_ARRAY_INDEX % 10))) {
JS_ASSERT(index <= MAX_ARRAY_INDEX);
*indexp = index;
return true;
}
return false;
}
}
static JSBool
BigIndexToId(JSContext *cx, JSObject *obj, jsuint index, JSBool createAtom,
jsid *idp)
@@ -1373,7 +1381,7 @@ array_toLocaleString(JSContext *cx, uintN argc, Value *vp)
static JSBool
InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, Value *vector)
{
JS_ASSERT(count < MAXINDEX);
JS_ASSERT(count <= MAX_ARRAY_INDEX);
/*
* Optimize for dense arrays so long as adding the given set of elements
@@ -1403,7 +1411,7 @@ InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, Valu
} while (false);
Value* end = vector + count;
while (vector != end && start < MAXINDEX) {
while (vector != end && start <= MAX_ARRAY_INDEX) {
if (!JS_CHECK_OPERATION_LIMIT(cx) ||
!SetArrayElement(cx, obj, start++, *vector++)) {
return JS_FALSE;
@@ -1417,10 +1425,10 @@ InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, Valu
if (obj->isDenseArray() && !ENSURE_SLOW_ARRAY(cx, obj))
return JS_FALSE;
JS_ASSERT(start == MAXINDEX);
JS_ASSERT(start == MAX_ARRAY_INDEX + 1);
AutoValueRooter tvr(cx);
AutoIdRooter idr(cx);
Value idval = DoubleValue(MAXINDEX);
Value idval = DoubleValue(MAX_ARRAY_INDEX + 1);
do {
*tvr.addr() = *vector++;
if (!js_ValueToStringId(cx, idval, idr.addr()) ||