Skip to content

Commit d404837

Browse files
committed
Assert in JavascriptArray::EntryReverse when length > Uint32Max
Turns out it's pretty easy to make the length property of a TypedArray greater than Uint32Max. Passing a TypedArray object like that to Array#reverse hits an assert verifying the length is <= Uint32Max. This is a valid situation to get into, though, so instead of asserting we should fall into the object (non-Array, non-TypedArray) case and use the slow path which supports larger lengths. Fixes #6179
1 parent b83edb0 commit d404837

File tree

3 files changed

+69
-4
lines changed

3 files changed

+69
-4
lines changed

lib/Runtime/Library/JavascriptArray.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5533,7 +5533,6 @@ using namespace Js;
55335533
{
55345534
JS_REENTRANT_UNLOCK(jsReentLock, return JavascriptArray::ReverseHelper(pArr, nullptr, obj, length.GetSmallIndex(), scriptContext));
55355535
}
5536-
Assert(pArr == nullptr || length.IsUint32Max()); // if pArr is not null lets make sure length is safe to cast, which will only happen if length is a uint32max
55375536

55385537
JS_REENTRANT_UNLOCK(jsReentLock, return JavascriptArray::ReverseHelper(pArr, nullptr, obj, length.GetBigIndex(), scriptContext));
55395538
JIT_HELPER_END(Array_Reverse);
@@ -5624,6 +5623,7 @@ using namespace Js;
56245623

56255624
if (useNoSideEffectReverse)
56265625
{
5626+
Assert(length <= JavascriptArray::MaxArrayLength);
56275627
Recycler * recycler = scriptContext->GetRecycler();
56285628

56295629
if (length <= 1)
@@ -5774,9 +5774,8 @@ using namespace Js;
57745774

57755775
failFastOnError.Completed();
57765776
}
5777-
else if (typedArrayBase)
5777+
else if (typedArrayBase && length <= JavascriptArray::MaxArrayLength)
57785778
{
5779-
Assert(length <= JavascriptArray::MaxArrayLength);
57805779
if (typedArrayBase->GetLength() == length)
57815780
{
57825781
// If typedArrayBase->length == length then we know that the TypedArray will have all items < length

test/Bugs/bug_6179.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//-------------------------------------------------------------------------------------------------------
2+
// Copyright (C) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
4+
//-------------------------------------------------------------------------------------------------------
5+
6+
WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
7+
8+
var tests = [
9+
{
10+
name: "#6179 - Assert in JavascriptArray::EntryReverse when length > Uint32Max for TypedArray with Array prototype",
11+
body: function () {
12+
var ua = new Uint32Array(0x10);
13+
ua.__proto__ = new Array(0xffffffff);
14+
ua.length = 0xffffffff*2;
15+
16+
assert.throws(()=>ua.reverse(), TypeError, "Array#reverse tries to delete a property on the TypedArray but that throws");
17+
}
18+
},
19+
{
20+
name: "#6179 - Assert in JavascriptArray::EntryReverse when length > Uint32Max for TypedArray with own length property",
21+
body: function () {
22+
var ua = new Uint32Array(0x10);
23+
Object.defineProperty(ua, 'length', {value: 0xffffffff*2 });
24+
25+
assert.throws(()=>Array.prototype.reverse.call(ua), TypeError, "Array#reverse tries to delete a property on the TypedArray but that throws");
26+
}
27+
},
28+
{
29+
name: "#6179 - Assert in JavascriptArray::EntryReverse when length > Uint32Max for an object with length property",
30+
body: function () {
31+
let getCount = 0;
32+
let setCount = 0;
33+
var ua = {
34+
length: 0xffffffff*2,
35+
set [0xffffffff*2-1](v) {
36+
assert.areEqual(1, getCount, "1 === getCount");
37+
assert.areEqual(0, setCount, "0 === setCount");
38+
setCount++
39+
},
40+
get '0'() {
41+
assert.areEqual(0, getCount, "0 === getCount");
42+
assert.areEqual(0, setCount, "0 === setCount");
43+
getCount++
44+
},
45+
get '1'() {
46+
assert.areEqual(1, getCount, "1 === getCount");
47+
assert.areEqual(1, setCount, "1 === setCount");
48+
throw 123;
49+
}
50+
};
51+
52+
assert.throws(()=>Array.prototype.reverse.call(ua), 123, "Array#reverse will throw above when we try and get property '1'");
53+
54+
assert.areEqual(1, getCount, "1 === getCount");
55+
assert.areEqual(1, setCount, "1 === setCount");
56+
}
57+
},
58+
];
59+
60+
testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });

test/Bugs/rlexe.xml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -602,5 +602,11 @@ Re-enable after fixing ScopeInfo::SaveEnclosingScopeInfo to handle named functio
602602
<files>Bug19948792.js</files>
603603
<compile-flags>-maxinterpretcount:1 -bgjit- -oopjit- -loopinterpretcount:1 -maxsimplejitruncount:2</compile-flags>
604604
</default>
605-
</test>
605+
</test>
606+
<test>
607+
<default>
608+
<files>bug_6179.js</files>
609+
<compile-flags>-args summary -endargs</compile-flags>
610+
</default>
611+
</test>
606612
</regress-exe>

0 commit comments

Comments
 (0)