Skip to content

Commit dce7443

Browse files
boingoingwyrichte
authored andcommitted
[CVE-2019-1196] Chakra Builtins Function Type Confusion
1 parent 242c59e commit dce7443

20 files changed

+735
-712
lines changed

lib/Backend/BackwardPass.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6555,6 +6555,7 @@ BackwardPass::TrackIntUsage(IR::Instr *const instr)
65556555
case Js::OpCode::Coerce_Regex:
65566556
case Js::OpCode::Coerce_StrOrRegex:
65576557
case Js::OpCode::Conv_PrimStr:
6558+
case Js::OpCode::Conv_Prop:
65586559
// These instructions don't generate -0, and their behavior is the same for any src that is -0 or +0
65596560
SetNegativeZeroDoesNotMatterIfLastUse(instr->GetSrc1());
65606561
SetNegativeZeroDoesNotMatterIfLastUse(instr->GetSrc2());

lib/Backend/JnHelperMethodList.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ HELPERCALL_MATH(Op_MaxInAnArray, Js::JavascriptMath::MaxInAnArray, AttrCanThrow)
134134
HELPERCALL_MATH(Op_MinInAnArray, Js::JavascriptMath::MinInAnArray, AttrCanThrow)
135135

136136
HELPERCALLCHK(Op_ConvString, Js::JavascriptConversion::ToString, AttrCanThrow)
137+
HELPERCALLCHK(Op_ConvPropertyKey, Js::JavascriptOperators::OP_ToPropertyKey, AttrCanThrow)
137138
HELPERCALLCHK(Op_CoerseString, Js::JavascriptConversion::CoerseString, AttrCanThrow)
138139
HELPERCALLCHK(Op_CoerseRegex, (Js::JavascriptRegExp* (*) (Js::Var aValue, Js::Var options, Js::ScriptContext *scriptContext))Js::JavascriptRegExp::CreateRegEx, AttrCanThrow)
139140

lib/Backend/Lower.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2826,6 +2826,10 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa
28262826
this->LowerConvPrimStr(instr);
28272827
break;
28282828

2829+
case Js::OpCode::Conv_Prop:
2830+
this->LowerConvPropertyKey(instr);
2831+
break;
2832+
28292833
case Js::OpCode::ClearAttributes:
28302834
this->LowerBinaryHelper(instr, IR::HelperOP_ClearAttributes);
28312835
break;
@@ -25474,6 +25478,12 @@ Lowerer::GenerateGetImmutableOrScriptUnreferencedString(IR::RegOpnd * strOpnd, I
2547425478
return dstOpnd;
2547525479
}
2547625480

25481+
void
25482+
Lowerer::LowerConvPropertyKey(IR::Instr* instr)
25483+
{
25484+
LowerConvStrCommon(IR::HelperOp_ConvPropertyKey, instr);
25485+
}
25486+
2547725487
void
2547825488
Lowerer::LowerConvStrCommon(IR::JnHelperMethod helper, IR::Instr * instr)
2547925489
{

lib/Backend/Lower.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,8 @@ class Lowerer
670670
void LowerConvPrimStr(IR::Instr * instr);
671671
void LowerConvStrCommon(IR::JnHelperMethod helper, IR::Instr * instr);
672672

673+
void LowerConvPropertyKey(IR::Instr* instr);
674+
673675
void GenerateRecyclerAlloc(IR::JnHelperMethod allocHelper, size_t allocSize, IR::RegOpnd* newObjDst, IR::Instr* insertionPointInstr, bool inOpHelper = false);
674676

675677
template <typename ArrayType>

lib/Runtime/ByteCode/ByteCodeCacheReleaseFileVersion.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55
// NOTE: If there is a merge conflict the correct fix is to make a new GUID.
66
// This file was generated with tools\update_bytecode_version.ps1
77

8-
// {26894CEE-B780-4CD4-B793-7B0972AEEDD9}
8+
// {3096A219-129D-4A4A-A61C-186D03BB25B7}
99
const GUID byteCodeCacheReleaseFileVersion =
10-
{ 0x26894CEE, 0xB780, 0x4CD4, { 0xB7, 0x93, 0x7B, 0x09, 0x72, 0xAE, 0xED, 0xD9 } };
10+
{ 0x3096a219, 0x129d, 0x4a4a, { 0xa6, 0x1c, 0x18, 0x6d, 0x3, 0xbb, 0x25, 0xb7 } };

lib/Runtime/ByteCode/ByteCodeEmitter.cpp

Lines changed: 26 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,8 @@ bool IsArguments(ParseNode *pnode)
223223
}
224224

225225
bool ApplyEnclosesArgs(ParseNode* fncDecl, ByteCodeGenerator* byteCodeGenerator);
226-
void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo, BOOL fReturnValue, bool isConstructorCall = false, ParseNode *bindPnode = nullptr, bool isTopLevel = false);
227-
void EmitBinaryOpnds(ParseNode *pnode1, ParseNode *pnode2, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo);
226+
void Emit(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo, BOOL fReturnValue, bool isConstructorCall = false, Js::RegSlot bindingNameLocation = Js::Constants::NoRegister, bool isTopLevel = false);
227+
void EmitBinaryOpnds(ParseNode* pnode1, ParseNode* pnode2, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo, Js::RegSlot computedPropertyLocation = Js::Constants::NoRegister);
228228
bool IsExpressionStatement(ParseNode* stmt, const Js::ScriptContext *const scriptContext);
229229
void EmitInvoke(Js::RegSlot location, Js::RegSlot callObjLocation, Js::PropertyId propertyId, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo);
230230
void EmitInvoke(Js::RegSlot location, Js::RegSlot callObjLocation, Js::PropertyId propertyId, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo, Js::RegSlot arg1Location);
@@ -953,7 +953,7 @@ void ByteCodeGenerator::EmitTopLevelStatement(ParseNode *stmt, FuncInfo *funcInf
953953
EndStatement(stmt);
954954
}
955955

956-
Emit(stmt, this, funcInfo, fReturnValue, false/*isConstructorCall*/, nullptr/*bindPnode*/, true/*isTopLevel*/);
956+
Emit(stmt, this, funcInfo, fReturnValue, false/*isConstructorCall*/, Js::Constants::NoRegister/*computedPropertyLocation*/, true/*isTopLevel*/);
957957
if (funcInfo->IsTmpReg(stmt->location))
958958
{
959959
if (!stmt->isUsed && !fReturnValue)
@@ -8224,22 +8224,6 @@ void EmitInvoke(
82248224
byteCodeGenerator->Writer()->CallI(Js::OpCode::CallI, location, location, 2, callSiteId);
82258225
}
82268226

8227-
void EmitComputedFunctionNameVar(ParseNode *nameNode, ParseNodeFnc *exprNode, ByteCodeGenerator *byteCodeGenerator)
8228-
{
8229-
AssertMsg(exprNode != nullptr, "callers of this function should pass in a valid expression Node");
8230-
Assert(exprNode->HasComputedName());
8231-
8232-
if (nameNode == nullptr)
8233-
{
8234-
return;
8235-
}
8236-
8237-
if (exprNode->pnodeName == nullptr)
8238-
{
8239-
byteCodeGenerator->Writer()->Reg2(Js::OpCode::SetComputedNameVar, exprNode->location, nameNode->location);
8240-
}
8241-
}
8242-
82438227
void EmitMemberNode(ParseNode *memberNode, Js::RegSlot objectLocation, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo, ParseNode* parentNode, bool useStore, bool* isObjectEmpty = nullptr)
82448228
{
82458229
ParseNode *nameNode = memberNode->AsParseNodeBin()->pnode1;
@@ -8254,6 +8238,8 @@ void EmitMemberNode(ParseNode *memberNode, Js::RegSlot objectLocation, ByteCodeG
82548238
exprNode->AsParseNodeFnc()->SetHomeObjLocation(objectLocation);
82558239
}
82568240

8241+
Js::RegSlot computedNamePropertyKey = Js::Constants::NoRegister;
8242+
82578243
// Moved SetComputedNameVar before LdFld of prototype because loading the prototype undefers the function TypeHandler
82588244
// which makes this bytecode too late to influence the function.name.
82598245
if (nameNode->nop == knopComputedName)
@@ -8262,10 +8248,13 @@ void EmitMemberNode(ParseNode *memberNode, Js::RegSlot objectLocation, ByteCodeG
82628248
// Transparently pass the name expr
82638249
// The Emit will replace this with a temp register if necessary to preserve the value.
82648250
nameNode->location = nameNode->AsParseNodeUni()->pnode1->location;
8265-
EmitBinaryOpnds(nameNode, exprNode, byteCodeGenerator, funcInfo);
8266-
if (isFncDecl && !exprNode->AsParseNodeFnc()->IsClassConstructor())
8251+
computedNamePropertyKey = funcInfo->AcquireTmpRegister();
8252+
8253+
EmitBinaryOpnds(nameNode, exprNode, byteCodeGenerator, funcInfo, computedNamePropertyKey);
8254+
8255+
if (isFncDecl && !exprNode->AsParseNodeFnc()->IsClassConstructor() && exprNode->AsParseNodeFnc()->pnodeName == nullptr)
82678256
{
8268-
EmitComputedFunctionNameVar(nameNode, exprNode->AsParseNodeFnc(), byteCodeGenerator);
8257+
byteCodeGenerator->Writer()->Reg2(Js::OpCode::SetComputedNameVar, exprNode->location, computedNamePropertyKey);
82698258
}
82708259
}
82718260

@@ -8289,10 +8278,11 @@ void EmitMemberNode(ParseNode *memberNode, Js::RegSlot objectLocation, ByteCodeG
82898278
(isClassMember ? Js::OpCode::InitClassMemberSetComputedName : Js::OpCode::InitSetElemI) :
82908279
(isClassMember ? Js::OpCode::InitClassMemberComputedName : Js::OpCode::InitComputedProperty);
82918280

8292-
byteCodeGenerator->Writer()->Element(setOp, exprNode->location, objectLocation, nameNode->location, true);
8281+
byteCodeGenerator->Writer()->Element(setOp, exprNode->location, objectLocation, computedNamePropertyKey, true);
82938282

82948283
funcInfo->ReleaseLoc(exprNode);
82958284
funcInfo->ReleaseLoc(nameNode);
8285+
funcInfo->ReleaseTmpRegister(computedNamePropertyKey);
82968286

82978287
return;
82988288
}
@@ -9578,7 +9568,7 @@ void EmitJumpCleanup(ParseNodeStmt *pnode, ParseNode *pnodeTarget, ByteCodeGener
95789568
}
95799569
}
95809570

9581-
void EmitBinaryOpnds(ParseNode *pnode1, ParseNode *pnode2, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo)
9571+
void EmitBinaryOpnds(ParseNode *pnode1, ParseNode *pnode2, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo, Js::RegSlot computedPropertyLocation)
95829572
{
95839573
// If opnd2 can overwrite opnd1, make sure the value of opnd1 is stashed away.
95849574
if (MayHaveSideEffectOnNode(pnode1, pnode2))
@@ -9588,15 +9578,12 @@ void EmitBinaryOpnds(ParseNode *pnode1, ParseNode *pnode2, ByteCodeGenerator *by
95889578

95899579
Emit(pnode1, byteCodeGenerator, funcInfo, false);
95909580

9591-
if (pnode1->nop == knopComputedName && pnode2->nop == knopClassDecl &&
9592-
(pnode2->AsParseNodeClass()->pnodeConstructor == nullptr || pnode2->AsParseNodeClass()->pnodeConstructor->nop != knopVarDecl))
9593-
{
9594-
Emit(pnode2, byteCodeGenerator, funcInfo, false, false, pnode1);
9595-
}
9596-
else
9581+
if (pnode1->nop == knopComputedName && computedPropertyLocation != Js::Constants::NoRegister)
95979582
{
9598-
Emit(pnode2, byteCodeGenerator, funcInfo, false);
9583+
byteCodeGenerator->Writer()->Reg2(Js::OpCode::Conv_Prop, computedPropertyLocation, pnode1->location);
95999584
}
9585+
9586+
Emit(pnode2, byteCodeGenerator, funcInfo, false, false, computedPropertyLocation);
96009587
}
96019588

96029589
void EmitBinaryReference(ParseNode *pnode1, ParseNode *pnode2, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo, BOOL fLoadLhs)
@@ -10142,7 +10129,7 @@ void TrackGlobalIntAssignments(ParseNodePtr pnode, ByteCodeGenerator * byteCodeG
1014210129
}
1014310130
}
1014410131

10145-
void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo, BOOL fReturnValue, bool isConstructorCall, ParseNode * bindPnode, bool isTopLevel)
10132+
void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo, BOOL fReturnValue, bool isConstructorCall, Js::RegSlot bindingNameLocation, bool isTopLevel)
1014610133
{
1014710134
if (pnode == nullptr)
1014810135
{
@@ -10966,7 +10953,13 @@ void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *func
1096610953

1096710954
// Constructor
1096810955
Emit(pnodeClass->pnodeConstructor, byteCodeGenerator, funcInfo, false);
10969-
EmitComputedFunctionNameVar(bindPnode, pnodeClass->pnodeConstructor, byteCodeGenerator);
10956+
10957+
if (bindingNameLocation != Js::Constants::NoRegister && !pnodeClass->pnodeConstructor->pnodeName)
10958+
{
10959+
Assert(pnodeClass->pnodeConstructor->HasComputedName());
10960+
byteCodeGenerator->Writer()->Reg2(Js::OpCode::SetComputedNameVar, pnodeClass->pnodeConstructor->location, bindingNameLocation);
10961+
}
10962+
1097010963
if (pnodeClass->pnodeExtends)
1097110964
{
1097210965
byteCodeGenerator->StartStatement(pnodeClass->pnodeExtends);

lib/Runtime/ByteCode/OpCodes.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,8 @@ MACRO_BACKEND_ONLY( CmUnGe_I4, Reg3, OpTempNumberSources|
320320
MACRO_WMS( Conv_Num, Reg2, OpSideEffect|OpTempNumberProducing|OpTempNumberTransfer|OpTempObjectSources|OpOpndHasImplicitCall|OpProducesNumber) // Convert to Number. [[ToNumber()]]
321321
// Operation ToString(str)
322322
MACRO_EXTEND_WMS( Conv_Str, Reg2, OpOpndHasImplicitCall|OpTempNumberSources|OpTempObjectSources|OpCanCSE|OpPostOpDbgBailOut)
323+
// Operation ToPropertyKey(var)
324+
MACRO_EXTEND_WMS( Conv_Prop, Reg2, OpOpndHasImplicitCall|OpTempNumberSources|OpTempObjectSources|OpCanCSE|OpPostOpDbgBailOut)
323325

324326
// Conv_Obj:
325327
// OpSideEffect - May throw exception on null/undefined.

lib/Runtime/Language/InterpreterHandler.inl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ EXDEF2 (BRPROP, BrOnHasProperty, OP_BrOnHasProper
9494
DEF3_WMS(CALL, ProfiledReturnTypeCallIExtendedFlags, PROFILEDOP(OP_ProfiledReturnTypeCallIExtendedFlags, OP_CallIExtendedFlags), ProfiledCallIExtendedFlags)
9595

9696
EXDEF2_WMS(A1toA1Mem, Conv_Str, JavascriptConversion::ToString)
97+
EXDEF2_WMS(A1toA1Mem, Conv_Prop, JavascriptOperators::OP_ToPropertyKey)
9798
DEF2_WMS(A1toA1Mem, Conv_Obj, JavascriptOperators::ToObject)
9899
EXDEF2_WMS(A1toA1Mem, NewUnscopablesWrapperObject,JavascriptOperators::ToUnscopablesWrapperObject)
99100
DEF2_WMS(A1toA1Mem, Conv_Num, JavascriptOperators::ToNumber)

lib/Runtime/Language/JavascriptConversion.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ using namespace Js;
267267
// ToPropertyKey() takes a value and converts it to a property key
268268
// Implementation of ES6 7.1.14
269269
//----------------------------------------------------------------------------
270-
void JavascriptConversion::ToPropertyKey(
270+
Var JavascriptConversion::ToPropertyKey(
271271
Var argument,
272272
_In_ ScriptContext* scriptContext,
273273
_Out_ const PropertyRecord** propertyRecord,
@@ -289,12 +289,15 @@ using namespace Js;
289289
{
290290
propertyString = PropertyString::UnsafeFromVar(propName);
291291
}
292+
key = propName;
292293
}
293294

294295
if (propString)
295296
{
296297
*propString = propertyString;
297298
}
299+
300+
return key;
298301
}
299302

300303
//----------------------------------------------------------------------------

lib/Runtime/Language/JavascriptConversion.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ namespace Js {
1717
static Var ToPrimitive(_In_ Var aValue, _In_ ScriptContext * scriptContext);
1818
static BOOL CanonicalNumericIndexString(JavascriptString *aValue, double *indexValue, ScriptContext * scriptContext);
1919

20-
static void ToPropertyKey(
20+
static Var ToPropertyKey(
2121
Var argument,
2222
_In_ ScriptContext* scriptContext,
2323
_Out_ const PropertyRecord** propertyRecord,

lib/Runtime/Language/JavascriptOperators.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5208,6 +5208,14 @@ using namespace Js;
52085208
JIT_HELPER_END(Op_DeleteElementI);
52095209
}
52105210

5211+
Var JavascriptOperators::OP_ToPropertyKey(Js::Var argument, ScriptContext* scriptContext)
5212+
{
5213+
JIT_HELPER_REENTRANT_HEADER(Op_ConvPropertyKey);
5214+
PropertyRecord const* unused = nullptr;
5215+
return JavascriptConversion::ToPropertyKey(argument, scriptContext, &unused, nullptr);
5216+
JIT_HELPER_END(Op_ConvPropertyKey);
5217+
}
5218+
52115219
Var JavascriptOperators::OP_GetLength(Var instance, ScriptContext* scriptContext)
52125220
{
52135221
return JavascriptOperators::OP_GetProperty(instance, PropertyIds::length, scriptContext);

lib/Runtime/Language/JavascriptOperators.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,8 @@ namespace Js
453453
static Var OP_CmGt_A(Js::Var a,Js::Var b,ScriptContext* scriptContext);
454454
static Var OP_CmGe_A(Js::Var a,Js::Var b,ScriptContext* scriptContext);
455455

456+
static Var OP_ToPropertyKey(Js::Var argument, ScriptContext* scriptContext);
457+
456458
static FunctionInfo * GetConstructorFunctionInfo(Var instance, ScriptContext * scriptContext);
457459
// Detach the type array buffer, if possible, and returns the state of the object which can be used to initialize another object
458460
static DetachedStateBase* DetachVarAndGetState(Var var, bool queueForDelayFree = true);

0 commit comments

Comments
 (0)