Skip to content

Commit bcd950e

Browse files
TypedArrayCreate for TypedArray built-in methods
CVE-2016-3199 (22.2.4.6)After getting user-defined constructor (through [@@species] or as 'this'), following built-ins: %TypedArray%.prototype.filter() %TypedArray%.prototype.map() %TypedArray%.prototype.slice() %TypedArray%.prototype.subarray() %TypedArray%.from() %TypedArray%.of() invoke TypedArrayCreate(), which throws TypeError when: - constructor does not have [[TypedArrayName]] internal slot, or - 'this' object has detached buffer, or - new object created through constructor has smaller length than stipulated by args[0] of built-in call *not applicable to %TypedArray%.prototype.subarray
1 parent d09a587 commit bcd950e

5 files changed

Lines changed: 115 additions & 68 deletions

File tree

lib/Runtime/Library/JavascriptArray.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5767,7 +5767,7 @@ namespace Js
57675767
newArr = CreateNewArrayHelper(static_cast<uint32>(newLenT), isIntArray, isFloatArray, pArr, scriptContext);
57685768
newObj = newArr;
57695769
}
5770-
else if (JavascriptOperators::IsConstructor(constructor) && JavascriptLibrary::IsTypedArrayConstructor(constructor, scriptContext))
5770+
else if (JavascriptOperators::IsConstructor(constructor))
57715771
{
57725772
if (pArr)
57735773
{
@@ -5778,7 +5778,7 @@ namespace Js
57785778

57795779
Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(newLenT, scriptContext) };
57805780
Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs));
5781-
newObj = RecyclableObject::FromVar(JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext));
5781+
newObj = RecyclableObject::FromVar(TypedArrayBase::TypedArrayCreate(constructor, &Js::Arguments(constructorCallInfo, constructorArgs), (uint32)newLenT, scriptContext));
57825782
}
57835783
else
57845784
{
@@ -8832,7 +8832,7 @@ namespace Js
88328832
{
88338833
Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(length, scriptContext) };
88348834
Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs));
8835-
newObj = RecyclableObject::FromVar(JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext));
8835+
newObj = RecyclableObject::FromVar(TypedArrayBase::TypedArrayCreate(constructor, &Js::Arguments(constructorCallInfo, constructorArgs), (uint32)length, scriptContext));
88368836
}
88378837
else if (isTypedArrayEntryPoint)
88388838
{
@@ -9788,7 +9788,9 @@ namespace Js
97889788

97899789
Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(len, scriptContext) };
97909790
Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs));
9791-
newObj = JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext);
9791+
newObj = isTypedArrayEntryPoint ?
9792+
TypedArrayBase::TypedArrayCreate(constructor, &Js::Arguments(constructorCallInfo, constructorArgs), len, scriptContext) :
9793+
JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext);
97929794

97939795
// If the new object we created is an array, remember that as it will save us time setting properties in the object below
97949796
if (JavascriptArray::Is(newObj))

lib/Runtime/Library/TypedArray.cpp

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1361,7 +1361,7 @@ namespace Js
13611361

13621362
Js::Var constructorArgs[] = { constructor, buffer, JavascriptNumber::ToVar(beginByteOffset, scriptContext), JavascriptNumber::ToVar(newLength, scriptContext) };
13631363
Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs));
1364-
newTypedArray = JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext);
1364+
newTypedArray = RecyclableObject::FromVar(TypedArrayBase::TypedArrayCreate(constructor, &Js::Arguments(constructorCallInfo, constructorArgs), newLength, scriptContext));
13651365
}
13661366
else
13671367
{
@@ -1447,7 +1447,7 @@ namespace Js
14471447

14481448
Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(len, scriptContext) };
14491449
Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs));
1450-
newObj = JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext);
1450+
newObj = TypedArrayBase::TypedArrayCreate(constructor, &Js::Arguments(constructorCallInfo, constructorArgs), len, scriptContext);
14511451

14521452
TypedArrayBase* newTypedArrayBase = nullptr;
14531453
JavascriptArray* newArr = nullptr;
@@ -1511,7 +1511,7 @@ namespace Js
15111511

15121512
Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(len, scriptContext) };
15131513
Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs));
1514-
newObj = JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext);
1514+
newObj = TypedArrayBase::TypedArrayCreate(constructor, &Js::Arguments(constructorCallInfo, constructorArgs), len, scriptContext);
15151515

15161516
TypedArrayBase* newTypedArrayBase = nullptr;
15171517
JavascriptArray* newArr = nullptr;
@@ -1770,7 +1770,7 @@ namespace Js
17701770

17711771
Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(captured, scriptContext) };
17721772
Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs));
1773-
newObj = RecyclableObject::FromVar(JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext));
1773+
newObj = RecyclableObject::FromVar(TypedArrayBase::TypedArrayCreate(constructor, &Js::Arguments(constructorCallInfo, constructorArgs), captured, scriptContext));
17741774

17751775
if (TypedArrayBase::Is(newObj))
17761776
{
@@ -2733,6 +2733,40 @@ namespace Js
27332733
return Js::JavascriptNumber::ToVarNoCheck(currentRes, scriptContext);
27342734
}
27352735

2736+
// static
2737+
Var TypedArrayBase::ValidateTypedArray(Var aValue, ScriptContext *scriptContext)
2738+
{
2739+
if (!TypedArrayBase::Is(aValue))
2740+
{
2741+
JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedTypedArray);
2742+
}
2743+
2744+
TypedArrayBase *typedArrayBase = TypedArrayBase::FromVar(aValue);
2745+
ArrayBuffer *arrayBuffer = typedArrayBase->GetArrayBuffer();
2746+
if (arrayBuffer->IsDetached())
2747+
{
2748+
JavascriptError::ThrowTypeError(scriptContext, JSERR_DetachedTypedArray);
2749+
}
2750+
2751+
return arrayBuffer;
2752+
}
2753+
2754+
// static
2755+
Var TypedArrayBase::TypedArrayCreate(Var constructor, Arguments *args, uint32 length, ScriptContext *scriptContext)
2756+
{
2757+
Var newObj = JavascriptOperators::NewScObject(constructor, *args, scriptContext);
2758+
2759+
TypedArrayBase::ValidateTypedArray(newObj, scriptContext);
2760+
2761+
// ECMA262 22.2.4.6 TypedArrayCreate line 3. "If argumentList is a List of a single Number" (args[0] == constructor)
2762+
if (args->Info.Count == 2 && TypedArrayBase::FromVar(newObj)->GetLength() < length)
2763+
{
2764+
JavascriptError::ThrowTypeError(scriptContext, JSERR_InvalidTypedArrayLength);
2765+
}
2766+
2767+
return newObj;
2768+
}
2769+
27362770
template<> BOOL Uint8ClampedArray::Is(Var aValue)
27372771
{
27382772
return JavascriptOperators::GetTypeId(aValue) == TypeIds_Uint8ClampedArray &&

lib/Runtime/Library/TypedArray.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ namespace Js
137137
// Returns false if this is not a TypedArray or it's not detached
138138
static BOOL IsDetachedTypedArray(Var aValue);
139139
static HRESULT GetBuffer(Var aValue, ArrayBuffer** outBuffer, uint32* outOffset, uint32* outLength);
140+
static Var ValidateTypedArray(Var aValue, ScriptContext *scriptContext);
141+
static Var TypedArrayCreate(Var constructor, Arguments *args, uint32 length, ScriptContext *scriptContext);
140142

141143
virtual BOOL DirectSetItem(__in uint32 index, __in Js::Var value) = 0;
142144
virtual BOOL DirectSetItemNoSet(__in uint32 index, __in Js::Var value) = 0;

0 commit comments

Comments
 (0)