Skip to content

Commit 457d13b

Browse files
committed
Array length limit check
1 parent cf9192f commit 457d13b

File tree

3 files changed

+64
-43
lines changed

3 files changed

+64
-43
lines changed

src/org/mozilla/javascript/NativeArray.java

Lines changed: 62 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -723,7 +723,7 @@ private static Object js_from(Context cx, Scriptable scope, Scriptable thisObj,
723723
}
724724
}
725725

726-
final long length = getLengthProperty(cx, items, false);
726+
final long length = getLengthProperty(cx, items);
727727
final Scriptable result = callConstructorOrCreateArray(cx, scope, thisObj, length, true);
728728
for (long k = 0; k < length; k++) {
729729
Object temp = getRawElem(items, k);
@@ -846,7 +846,7 @@ private void setLength(Object val) {
846846
* getLengthProperty returns 0 if obj does not have the length property
847847
* or its value is not convertible to a number.
848848
*/
849-
static long getLengthProperty(Context cx, Scriptable obj, boolean throwIfTooLarge) {
849+
static long getLengthProperty(Context cx, Scriptable obj) {
850850
// These will both give numeric lengths within Uint32 range.
851851
if (obj instanceof NativeString) {
852852
return ((NativeString)obj).getLength();
@@ -862,17 +862,15 @@ static long getLengthProperty(Context cx, Scriptable obj, boolean throwIfTooLarg
862862
}
863863

864864
double doubleLen = ScriptRuntime.toNumber(len);
865+
866+
// ToLength
865867
if (doubleLen > NativeNumber.MAX_SAFE_INTEGER) {
866-
if (throwIfTooLarge) {
867-
String msg = ScriptRuntime.getMessageById("msg.arraylength.bad");
868-
throw ScriptRuntime.rangeError(msg);
869-
}
870-
return (int) NativeNumber.MAX_SAFE_INTEGER;
868+
return (long) NativeNumber.MAX_SAFE_INTEGER;
871869
}
872870
if (doubleLen < 0) {
873871
return 0;
874872
}
875-
return ScriptRuntime.toUint32(len);
873+
return (long)doubleLen;
876874
}
877875

878876
private static Object setLengthProperty(Context cx, Scriptable target,
@@ -949,7 +947,7 @@ private static String toStringHelper(Context cx, Scriptable scope,
949947
/* It's probably redundant to handle long lengths in this
950948
* function; StringBuilders are limited to 2^31 in java.
951949
*/
952-
long length = getLengthProperty(cx, o, false);
950+
long length = getLengthProperty(cx, o);
953951

954952
StringBuilder result = new StringBuilder(256);
955953

@@ -1044,7 +1042,7 @@ private static String js_join(Context cx, Scriptable scope, Scriptable thisObj,
10441042
{
10451043
Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
10461044

1047-
long llength = getLengthProperty(cx, o, false);
1045+
long llength = getLengthProperty(cx, o);
10481046
int length = (int)llength;
10491047
if (llength != length) {
10501048
throw Context.reportRuntimeErrorById(
@@ -1120,7 +1118,7 @@ private static Scriptable js_reverse(Context cx, Scriptable scope, Scriptable th
11201118
return o;
11211119
}
11221120
}
1123-
long len = getLengthProperty(cx, o, false);
1121+
long len = getLengthProperty(cx, o);
11241122

11251123
long half = len / 2;
11261124
for(long i=0; i < half; i++) {
@@ -1170,7 +1168,7 @@ public int compare(final Object x, final Object y) {
11701168
comparator = DEFAULT_COMPARATOR;
11711169
}
11721170

1173-
long llength = getLengthProperty(cx, o, false);
1171+
long llength = getLengthProperty(cx, o);
11741172
final int length = (int) llength;
11751173
if (llength != length) {
11761174
throw Context.reportRuntimeErrorById(
@@ -1209,7 +1207,7 @@ private static Object js_push(Context cx, Scriptable scope, Scriptable thisObj,
12091207
return ScriptRuntime.wrapNumber(na.length);
12101208
}
12111209
}
1212-
long length = getLengthProperty(cx, o, false);
1210+
long length = getLengthProperty(cx, o);
12131211
for (int i = 0; i < args.length; i++) {
12141212
setElem(cx, o, length + i, args[i]);
12151213
}
@@ -1245,7 +1243,7 @@ private static Object js_pop(Context cx, Scriptable scope, Scriptable thisObj,
12451243
return result;
12461244
}
12471245
}
1248-
long length = getLengthProperty(cx, o, false);
1246+
long length = getLengthProperty(cx, o);
12491247
if (length > 0) {
12501248
length--;
12511249

@@ -1280,7 +1278,7 @@ private static Object js_shift(Context cx, Scriptable scope, Scriptable thisObj,
12801278
}
12811279
}
12821280
Object result;
1283-
long length = getLengthProperty(cx, o, false);
1281+
long length = getLengthProperty(cx, o);
12841282
if (length > 0) {
12851283
long i = 0;
12861284
length--;
@@ -1326,10 +1324,14 @@ private static Object js_unshift(Context cx, Scriptable scope, Scriptable thisOb
13261324
return ScriptRuntime.wrapNumber(na.length);
13271325
}
13281326
}
1329-
long length = getLengthProperty(cx, o, false);
1327+
long length = getLengthProperty(cx, o);
13301328
int argc = args.length;
13311329

1332-
if (args.length > 0) {
1330+
if (argc > 0) {
1331+
if (length + argc > NativeNumber.MAX_SAFE_INTEGER) {
1332+
throw ScriptRuntime.typeErrorById("msg.arraylength.too.big", length + argc);
1333+
}
1334+
13331335
/* Slide up the array to make room for args at the bottom */
13341336
if (length > 0) {
13351337
for (long last = length - 1; last >= 0; last--) {
@@ -1344,17 +1346,17 @@ private static Object js_unshift(Context cx, Scriptable scope, Scriptable thisOb
13441346
}
13451347
}
13461348
/* Follow Perl by returning the new array length. */
1347-
length += args.length;
1349+
length += argc;
13481350
return setLengthProperty(cx, o, length);
13491351
}
13501352

13511353
private static Object js_splice(Context cx, Scriptable scope,
13521354
Scriptable thisObj, Object[] args)
13531355
{
1354-
Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
1356+
Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
13551357

1356-
NativeArray na = null;
1357-
boolean denseMode = false;
1358+
NativeArray na = null;
1359+
boolean denseMode = false;
13581360
if (o instanceof NativeArray) {
13591361
na = (NativeArray) o;
13601362
denseMode = na.denseOnly;
@@ -1365,34 +1367,43 @@ private static Object js_splice(Context cx, Scriptable scope,
13651367
int argc = args.length;
13661368
if (argc == 0)
13671369
return cx.newArray(scope, 0);
1368-
long length = getLengthProperty(cx, o, false);
1370+
long length = getLengthProperty(cx, o);
13691371

13701372
/* Convert the first argument into a starting index. */
13711373
long begin = toSliceIndex(ScriptRuntime.toInteger(args[0]), length);
13721374
argc--;
13731375

13741376
/* Convert the second argument into count */
1375-
long count;
1377+
long actualDeleteCount;
13761378
if (args.length == 1) {
1377-
count = length - begin;
1379+
actualDeleteCount = length - begin;
13781380
} else {
13791381
double dcount = ScriptRuntime.toInteger(args[1]);
13801382
if (dcount < 0) {
1381-
count = 0;
1383+
actualDeleteCount = 0;
13821384
} else if (dcount > (length - begin)) {
1383-
count = length - begin;
1385+
actualDeleteCount = length - begin;
13841386
} else {
1385-
count = (long)dcount;
1387+
actualDeleteCount = (long)dcount;
13861388
}
13871389
argc--;
13881390
}
13891391

1390-
long end = begin + count;
1392+
long end = begin + actualDeleteCount;
1393+
long delta = argc - actualDeleteCount;
1394+
1395+
if (length + delta > NativeNumber.MAX_SAFE_INTEGER) {
1396+
throw ScriptRuntime.typeErrorById("msg.arraylength.too.big", length + delta);
1397+
}
1398+
if (actualDeleteCount > Integer.MAX_VALUE) {
1399+
String msg = ScriptRuntime.getMessageById("msg.arraylength.bad");
1400+
throw ScriptRuntime.rangeError(msg);
1401+
}
13911402

13921403
/* If there are elements to remove, put them into the return value. */
13931404
Object result;
1394-
if (count != 0) {
1395-
if (count == 1
1405+
if (actualDeleteCount != 0) {
1406+
if (actualDeleteCount == 1
13961407
&& (cx.getLanguageVersion() == Context.VERSION_1_2))
13971408
{
13981409
/*
@@ -1426,7 +1437,7 @@ private static Object js_splice(Context cx, Scriptable scope,
14261437
result = resultArray;
14271438
}
14281439
}
1429-
} else { // (count == 0)
1440+
} else { // (actualDeleteCount == 0)
14301441
if (cx.getLanguageVersion() == Context.VERSION_1_2) {
14311442
/* Emulate C JS1.2; if no elements are removed, return undefined. */
14321443
result = Undefined.instance;
@@ -1436,7 +1447,6 @@ private static Object js_splice(Context cx, Scriptable scope,
14361447
}
14371448

14381449
/* Find the direction (up or down) to copy and make way for argv. */
1439-
long delta = argc - count;
14401450
if (denseMode && length + delta < Integer.MAX_VALUE &&
14411451
na.ensureCapacity((int) (length + delta)))
14421452
{
@@ -1512,7 +1522,7 @@ private static boolean isConcatSpreadable(Context cx, Scriptable scope, Object v
15121522
// dense arrays.
15131523
private static long concatSpreadArg(Context cx,
15141524
Scriptable result, Scriptable arg, long offset) {
1515-
long srclen = getLengthProperty(cx, arg, false);
1525+
long srclen = getLengthProperty(cx, arg);
15161526
long newlen = srclen + offset;
15171527

15181528
// First, optimize for a pair of native, dense arrays
@@ -1577,8 +1587,7 @@ private static Scriptable js_slice(Context cx, Scriptable scope, Scriptable this
15771587
{
15781588
Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
15791589

1580-
Scriptable result = cx.newArray(scope, 0);
1581-
long len = getLengthProperty(cx, o, false);
1590+
long len = getLengthProperty(cx, o);
15821591

15831592
long begin, end;
15841593
if (args.length == 0) {
@@ -1593,6 +1602,12 @@ private static Scriptable js_slice(Context cx, Scriptable scope, Scriptable this
15931602
}
15941603
}
15951604

1605+
if (end - begin > Integer.MAX_VALUE) {
1606+
String msg = ScriptRuntime.getMessageById("msg.arraylength.bad");
1607+
throw ScriptRuntime.rangeError(msg);
1608+
}
1609+
1610+
Scriptable result = cx.newArray(scope, 0);
15961611
for (long slot = begin; slot < end; slot++) {
15971612
Object temp = getRawElem(o, slot);
15981613
if (temp != NOT_FOUND) {
@@ -1625,7 +1640,7 @@ private static Object js_indexOf(Context cx, Scriptable scope, Scriptable thisOb
16251640
Object compareTo = args.length > 0 ? args[0] : Undefined.instance;
16261641

16271642
Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
1628-
long length = getLengthProperty(cx, o, false);
1643+
long length = getLengthProperty(cx, o);
16291644
/*
16301645
* From http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:indexOf
16311646
* The index at which to begin the search. Defaults to 0, i.e. the
@@ -1681,7 +1696,7 @@ private static Object js_lastIndexOf(Context cx, Scriptable scope, Scriptable th
16811696
Object compareTo = args.length > 0 ? args[0] : Undefined.instance;
16821697

16831698
Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
1684-
long length = getLengthProperty(cx, o, false);
1699+
long length = getLengthProperty(cx, o);
16851700
/*
16861701
* From http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:lastIndexOf
16871702
* The index at which to start searching backwards. Defaults to the
@@ -1788,7 +1803,7 @@ private static Boolean js_includes(Context cx, Scriptable scope, Scriptable this
17881803
private static Object js_fill(Context cx, Scriptable scope, Scriptable thisObj, Object[] args)
17891804
{
17901805
Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
1791-
long len = getLengthProperty(cx, o, false);
1806+
long len = getLengthProperty(cx, o);
17921807

17931808
long relativeStart = 0;
17941809
if (args.length >= 2) {
@@ -1825,7 +1840,7 @@ private static Object js_fill(Context cx, Scriptable scope, Scriptable thisObj,
18251840
private static Object js_copyWithin(Context cx, Scriptable scope, Scriptable thisObj, Object[] args)
18261841
{
18271842
Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
1828-
long len = getLengthProperty(cx, o, false);
1843+
long len = getLengthProperty(cx, o);
18291844

18301845
Object targetArg = (args.length >= 1) ? args[0] : Undefined.instance;
18311846
long relativeTarget = (long) ScriptRuntime.toInteger(targetArg);
@@ -1914,7 +1929,13 @@ private static Object iterativeMethod(Context cx, IdFunctionObject idFunctionObj
19141929
requireObjectCoercible(cx, o, idFunctionObject);
19151930
}
19161931

1917-
long length = getLengthProperty(cx, o, id == Id_map);
1932+
long length = getLengthProperty(cx, o);
1933+
// TODO: In the case of Id_filter, CreateDataPropertyOrThrow throws a TypeError instead of ArraySpeciesCreate.
1934+
if ((id == Id_map || id == Id_filter) && length > Integer.MAX_VALUE) {
1935+
String msg = ScriptRuntime.getMessageById("msg.arraylength.bad");
1936+
throw ScriptRuntime.rangeError(msg);
1937+
}
1938+
19181939
Object callbackArg = args.length > 0 ? args[0] : Undefined.instance;
19191940
if (callbackArg == null || !(callbackArg instanceof Function)) {
19201941
throw ScriptRuntime.notFunctionError(callbackArg);
@@ -2009,7 +2030,7 @@ private static Object reduceMethod(Context cx, int id, Scriptable scope,
20092030
{
20102031
Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
20112032

2012-
long length = getLengthProperty(cx, o, false);
2033+
long length = getLengthProperty(cx, o);
20132034
Object callbackArg = args.length > 0 ? args[0] : Undefined.instance;
20142035
if (callbackArg == null || !(callbackArg instanceof Function)) {
20152036
throw ScriptRuntime.notFunctionError(callbackArg);

src/org/mozilla/javascript/NativeArrayIterator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public String getClassName() {
4343

4444
@Override
4545
protected boolean isDone(Context cx, Scriptable scope) {
46-
return index >= NativeArray.getLengthProperty(cx, arrayLike, false);
46+
return index >= NativeArray.getLengthProperty(cx, arrayLike);
4747
}
4848

4949
@Override

src/org/mozilla/javascript/ScriptRuntime.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4131,7 +4131,7 @@ public static boolean isArrayObject(Object obj)
41314131
public static Object[] getArrayElements(Scriptable object)
41324132
{
41334133
Context cx = Context.getContext();
4134-
long longLen = NativeArray.getLengthProperty(cx, object, false);
4134+
long longLen = NativeArray.getLengthProperty(cx, object);
41354135
if (longLen > Integer.MAX_VALUE) {
41364136
// arrays beyond MAX_INT is not in Java in any case
41374137
throw new IllegalArgumentException();

0 commit comments

Comments
 (0)