From 3237eca00091cc87d481a561e43c5adcb2168ec0 Mon Sep 17 00:00:00 2001 From: Michael Holman Date: Mon, 13 Nov 2017 16:49:01 -0800 Subject: [PATCH 01/16] [CVE-2017-11918] JIT: Escape analysis bug - Google, Inc. --- lib/Backend/GlobOpt.cpp | 4 ++-- lib/Backend/TempTracker.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Backend/GlobOpt.cpp b/lib/Backend/GlobOpt.cpp index 5d3384eaae9..7c8d93e9643 100644 --- a/lib/Backend/GlobOpt.cpp +++ b/lib/Backend/GlobOpt.cpp @@ -18165,8 +18165,8 @@ GlobOpt::TrackTempObjectSyms(IR::Instr * instr, IR::RegOpnd * opnd) (instr->GetSrc1()->IsRegOpnd() && globOptData.canStoreTempObjectSyms->Test(instr->GetSrc1()->AsRegOpnd()->m_sym->m_id)) && (!instr->GetSrc2() || (instr->GetSrc2()->IsRegOpnd() && globOptData.canStoreTempObjectSyms->Test(instr->GetSrc2()->AsRegOpnd()->m_sym->m_id)))); - Assert(!canStoreTemp || instr->dstIsTempObject); - Assert(!maybeTemp || instr->dstIsTempObject); + AssertOrFailFast(!canStoreTemp || instr->dstIsTempObject); + AssertOrFailFast(!maybeTemp || instr->dstIsTempObject); } // Need to get the var equiv sym as assignment of type specialized sym kill the var sym value anyway. diff --git a/lib/Backend/TempTracker.cpp b/lib/Backend/TempTracker.cpp index 246df51d5c5..09371ac18b9 100644 --- a/lib/Backend/TempTracker.cpp +++ b/lib/Backend/TempTracker.cpp @@ -1026,7 +1026,7 @@ ObjectTemp::IsTempUseOpCodeSym(IR::Instr * instr, Js::OpCode opcode, Sym * sym) return instr->GetSrc1()->AsIndirOpnd()->GetBaseOpnd()->m_sym == sym; case Js::OpCode::StElemI_A: case Js::OpCode::StElemI_A_Strict: - return instr->GetDst()->AsIndirOpnd()->GetBaseOpnd()->m_sym == sym; + return instr->GetDst()->AsIndirOpnd()->GetBaseOpnd()->m_sym == sym && instr->GetSrc1()->GetStackSym() != sym; case Js::OpCode::Memset: return instr->GetDst()->AsIndirOpnd()->GetBaseOpnd()->m_sym == sym || (instr->GetSrc1()->IsRegOpnd() && instr->GetSrc1()->AsRegOpnd()->m_sym == sym); case Js::OpCode::Memcopy: From a5d6be626305671166f21db359c1c06a3a372b8b Mon Sep 17 00:00:00 2001 From: Michael Holman Date: Mon, 13 Nov 2017 17:02:54 -0800 Subject: [PATCH 02/16] [CVE-2017-11911] OOB read in asm.js - Google, Inc. --- lib/Runtime/Language/AsmJsModule.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/Runtime/Language/AsmJsModule.cpp b/lib/Runtime/Language/AsmJsModule.cpp index fc5fb150de5..6a64fcf1e36 100644 --- a/lib/Runtime/Language/AsmJsModule.cpp +++ b/lib/Runtime/Language/AsmJsModule.cpp @@ -839,6 +839,7 @@ namespace Js AsmJsSIMDValue simdValue; simdValue.Zero(); // define all variables + BVSparse initializerBV(&mAllocator); while (pnode->nop == knopList) { ParseNode * varNode = ParserWrapper::GetBinaryLeft(pnode); @@ -932,6 +933,12 @@ namespace Js { return Fail(decl, _u("Failed to define var")); } + // If we are declaring a var that we previously used in an initializer, that value will be undefined + // so we need to throw an error. + if (initializerBV.Test(var->GetName()->GetPropertyId())) + { + return Fail(decl, _u("Cannot declare a var after using it in an initializer")); + } RegSlot loc = Constants::NoRegister; if (pnodeInit->nop == knopInt) { @@ -970,6 +977,7 @@ namespace Js if (declSym->GetSymbolType() == AsmJsSymbol::Variable) { AsmJsVar * definition = declSym->Cast(); + initializerBV.Set(definition->GetName()->GetPropertyId()); switch (definition->GetVarType().which()) { case AsmJsVarType::Double: From 40232a443c6316a58941572b0a4776b0677975ca Mon Sep 17 00:00:00 2001 From: Derek Morris Date: Tue, 14 Nov 2017 13:28:58 -0800 Subject: [PATCH 03/16] [CVE-2017-11910] Insufficient InlineCache check can lead to type confusion --- lib/Backend/ObjTypeSpecFldInfo.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/Backend/ObjTypeSpecFldInfo.cpp b/lib/Backend/ObjTypeSpecFldInfo.cpp index e234320ea50..f57ff5b3891 100644 --- a/lib/Backend/ObjTypeSpecFldInfo.cpp +++ b/lib/Backend/ObjTypeSpecFldInfo.cpp @@ -674,16 +674,26 @@ ObjTypeSpecFldInfo* ObjTypeSpecFldInfo::CreateFrom(uint id, Js::PolymorphicInlin // when we add a property. We also don't invalidate proto inline caches (and guards) unless the property being added exists on the proto chain. // Missing properties by definition do not exist on the proto chain, so in the end we could have an EquivalentObjTypeSpec cache hit on a // property that once was missing, but has since been added. (See OS Bugs 280582). - else if (inlineCache.IsProto() && !inlineCache.u.proto.isMissing) + else if (inlineCache.IsProto()) { - isProto = true; - typeId = TypeWithoutAuxSlotTag(inlineCache.u.proto.type)->GetTypeId(); - usesAuxSlot = TypeHasAuxSlotTag(inlineCache.u.proto.type); - slotIndex = inlineCache.u.proto.slotIndex; - prototypeObject = inlineCache.u.proto.prototypeObject; + if(!inlineCache.u.proto.isMissing) + { + isProto = true; + typeId = TypeWithoutAuxSlotTag(inlineCache.u.proto.type)->GetTypeId(); + usesAuxSlot = TypeHasAuxSlotTag(inlineCache.u.proto.type); + slotIndex = inlineCache.u.proto.slotIndex; + prototypeObject = inlineCache.u.proto.prototypeObject; + } + else + { + areEquivalent = false; + areStressEquivalent = false; + gatherDataForInlining = false; + } } else { + AssertOrFailFast(inlineCache.IsAccessor()); if (!PHASE_OFF(Js::FixAccessorPropsPhase, functionBody)) { isAccessor = true; From 1e7fa7b4ac6a128c0b9bbc019d9936b118421808 Mon Sep 17 00:00:00 2001 From: Derek Morris Date: Tue, 14 Nov 2017 13:52:30 -0800 Subject: [PATCH 04/16] [CVE-2017-11912] Regex construction can depend on unintialized memory and leak stack contents --- lib/Parser/RegexCompileTime.cpp | 27 +++++++++++++++++++++++++-- lib/Parser/RegexCompileTime.h | 4 +--- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/lib/Parser/RegexCompileTime.cpp b/lib/Parser/RegexCompileTime.cpp index 7d6ad5d34cc..2b27c0242b9 100644 --- a/lib/Parser/RegexCompileTime.cpp +++ b/lib/Parser/RegexCompileTime.cpp @@ -1173,7 +1173,17 @@ namespace UnifiedRegex { if ((compiler.program->flags & IgnoreCaseRegexFlag) != 0) { - Char equivs[CaseInsensitive::EquivClassSize]; + // To ensure initialization, we first default-initialize the + // whole array with a constant, and then individually set it + // to be just the first character (known to exist). This can + // hopefully be optimized to just initialize to cs[0] by the + // compiler. + Char equivs[CaseInsensitive::EquivClassSize] = { (Char)-1 }; + for (int i = 0; i < CaseInsensitive::EquivClassSize; i++) + { + equivs[i] = cs[0]; + + } bool isNonTrivial = compiler.standardChars->ToEquivs(compiler.program->GetCaseMappingSource(), cs[0], equivs); if (isNonTrivial) { @@ -1279,10 +1289,23 @@ namespace UnifiedRegex { if (isEquivClass) { - Char uniqueEquivs[CaseInsensitive::EquivClassSize]; + // To ensure initialization, we first default-initialize the + // whole array with a constant, and then individually set it + // to be just the first character (known to exist). This can + // hopefully be optimized to just initialize to cs[0] by the + // compiler. + Char uniqueEquivs[CaseInsensitive::EquivClassSize] = { (Char)-1 }; + for (int i = 0; i < CaseInsensitive::EquivClassSize; i++) + { + uniqueEquivs[i] = cs[0]; + } CharCount uniqueEquivCount = FindUniqueEquivs(cs, uniqueEquivs); switch (uniqueEquivCount) { + case 1: + EMIT(compiler, MatchCharInst, uniqueEquivs[0]); + break; + case 2: EMIT(compiler, MatchChar2Inst, uniqueEquivs[0], uniqueEquivs[1]); break; diff --git a/lib/Parser/RegexCompileTime.h b/lib/Parser/RegexCompileTime.h index bbd0d12fc99..2f293bd60c4 100644 --- a/lib/Parser/RegexCompileTime.h +++ b/lib/Parser/RegexCompileTime.h @@ -374,10 +374,8 @@ namespace UnifiedRegex , isEquivClass(false) { cs[0] = c; -#if DBG for (int i = 1; i < CaseInsensitive::EquivClassSize; i++) - cs[i] = (Char)-1; -#endif + cs[i] = c; } NODE_DECL From 0e4566a4c394cb69834719704a05aa17101ae3f5 Mon Sep 17 00:00:00 2001 From: Michael Ferris Date: Thu, 26 Oct 2017 15:51:55 -0700 Subject: [PATCH 05/16] [CVE-2017-11909] JIT: BackwardPass::RemoveEmptyLoopAfterMemOp doesn't insert branches / make break control flow - Google, Inc. --- lib/Backend/BackwardPass.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/Backend/BackwardPass.cpp b/lib/Backend/BackwardPass.cpp index 7b83fcbda92..4193f4a19d3 100644 --- a/lib/Backend/BackwardPass.cpp +++ b/lib/Backend/BackwardPass.cpp @@ -7853,6 +7853,14 @@ BackwardPass::RemoveEmptyLoopAfterMemOp(Loop *loop) outerBlock->RemovePred(head, this->func->m_fg); landingPad->RemoveSucc(head, this->func->m_fg); + Assert(landingPad->GetSuccList()->Count() == 0); + + IR::Instr* firstOuterInstr = outerBlock->GetFirstInstr(); + AssertOrFailFast(firstOuterInstr->IsLabelInstr() && !landingPad->GetLastInstr()->EndsBasicBlock()); + IR::LabelInstr* label = firstOuterInstr->AsLabelInstr(); + // Add br to Outer block to keep coherence between branches and flow graph + IR::BranchInstr *outerBr = IR::BranchInstr::New(Js::OpCode::Br, label, this->func); + landingPad->InsertAfter(outerBr); this->func->m_fg->AddEdge(landingPad, outerBlock); this->func->m_fg->RemoveBlock(head, nullptr); From f94fda6a486b873487fbdbee26d35f22bf1eb8df Mon Sep 17 00:00:00 2001 From: Tom Care Date: Thu, 16 Nov 2017 17:35:54 -0800 Subject: [PATCH 06/16] [CVE-2017-11930] Integer overflow - Individual A large numeric or spread array literal can lead to an overflow in ByteCodeGenerator that enables an OOB write. Fix is to use Int32Math to detect overflow and follow the overflow policy. Also preventatively added a bunch of UInt32Math operations around sizeof size calculations. --- lib/Runtime/ByteCode/ByteCodeEmitter.cpp | 62 +++++++++++++++--------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/lib/Runtime/ByteCode/ByteCodeEmitter.cpp b/lib/Runtime/ByteCode/ByteCodeEmitter.cpp index 353771b1395..c20647f4780 100644 --- a/lib/Runtime/ByteCode/ByteCodeEmitter.cpp +++ b/lib/Runtime/ByteCode/ByteCodeEmitter.cpp @@ -538,8 +538,7 @@ void ByteCodeGenerator::LoadUncachedHeapArguments(FuncInfo *funcInfo) { // Pass the frame object and ID array to the runtime, and put the resulting Arguments object // at the expected location. - - Js::PropertyIdArray *propIds = funcInfo->GetParsedFunctionBody()->AllocatePropertyIdArrayForFormals(count * sizeof(Js::PropertyId), count, 0); + Js::PropertyIdArray *propIds = funcInfo->GetParsedFunctionBody()->AllocatePropertyIdArrayForFormals(UInt32Math::Mul(count, sizeof(Js::PropertyId)), count, 0); GetFormalArgsArray(this, funcInfo, propIds); } @@ -1557,7 +1556,9 @@ void ByteCodeGenerator::EmitScopeObjectInit(FuncInfo *funcInfo) uint cachedFuncCount = 0; Js::PropertyId firstFuncSlot = Js::Constants::NoProperty; Js::PropertyId firstVarSlot = Js::Constants::NoProperty; - uint extraAlloc = (slotCount + Js::ActivationObjectEx::ExtraSlotCount()) * sizeof(Js::PropertyId); + + uint extraAlloc = UInt32Math::Add(slotCount, Js::ActivationObjectEx::ExtraSlotCount()); + extraAlloc = UInt32Math::Mul(extraAlloc, sizeof(Js::PropertyId)); // Create and fill the array of local property ID's. // They all have slots assigned to them already (if they need them): see StartEmitFunction. @@ -1997,7 +1998,7 @@ void ByteCodeGenerator::LoadAllConstants(FuncInfo *funcInfo) uint count = funcInfo->inArgsCount + (funcInfo->root->sxFnc.pnodeRest != nullptr ? 1 : 0) - 1; if (count != 0) { - Js::PropertyIdArray *propIds = RecyclerNewPlus(scriptContext->GetRecycler(), count * sizeof(Js::PropertyId), Js::PropertyIdArray, count, 0); + Js::PropertyIdArray *propIds = RecyclerNewPlus(scriptContext->GetRecycler(), UInt32Math::Mul(count, sizeof(Js::PropertyId)), Js::PropertyIdArray, count, 0); GetFormalArgsArray(this, funcInfo, propIds); byteCodeFunction->SetPropertyIdsOfFormals(propIds); @@ -7198,7 +7199,7 @@ Js::ArgSlot EmitArgList( if (spreadArgCount > 0) { - const size_t extraAlloc = spreadArgCount * sizeof(uint32); + const size_t extraAlloc = UInt32Math::Mul(spreadArgCount, sizeof(uint32)); Assert(spreadIndices != nullptr); *spreadIndices = AnewPlus(byteCodeGenerator->GetAllocator(), extraAlloc, Js::AuxArray, spreadArgCount); } @@ -7347,7 +7348,7 @@ Js::ArgSlot EmitNewObjectOfConstants( EmitArgListStart(Js::Constants::NoRegister, byteCodeGenerator, funcInfo, Js::Constants::NoProfileId); // Create the vars array - Js::VarArrayVarCount *vars = AnewPlus(byteCodeGenerator->GetAllocator(), (argCount - 1) * sizeof(Js::Var), Js::VarArrayVarCount, Js::TaggedInt::ToVarUnchecked(argCount - 1)); + Js::VarArrayVarCount *vars = AnewPlus(byteCodeGenerator->GetAllocator(), UInt32Math::Mul((argCount - 1), sizeof(Js::Var)), Js::VarArrayVarCount, Js::TaggedInt::ToVarUnchecked(argCount - 1)); // Emit all constants to the vars array EmitConstantArgsToVarArray(byteCodeGenerator, vars->elements, pnode->sxCall.pnodeArgs, argCount - 1); @@ -7372,10 +7373,11 @@ Js::ArgSlot EmitNewObjectOfConstants( Js::OpCode::NewScObject_A, funcInfo->AcquireLoc(pnode), vars, - sizeof(Js::VarArray) + (argCount - 1) * sizeof(Js::Var), + UInt32Math::MulAdd((argCount-1)), pnode->sxCall.pnodeTarget->location); - AdeletePlus(byteCodeGenerator->GetAllocator(), (argCount - 1) * sizeof(Js::VarArrayVarCount), vars); + + AdeletePlus(byteCodeGenerator->GetAllocator(), UInt32Math::Mul((argCount-1), sizeof(Js::VarArrayVarCount)), vars); return actualArgCount; } @@ -7804,8 +7806,8 @@ void EmitCallI( if (pnode->sxCall.spreadArgCount > 0) { Assert(spreadIndices != nullptr); - spreadExtraAlloc = spreadIndices->count * sizeof(uint32); - spreadIndicesSize = sizeof(*spreadIndices) + spreadExtraAlloc; + spreadExtraAlloc = UInt32Math::Mul(spreadIndices->count, sizeof(uint32)); + spreadIndicesSize = UInt32Math::Add(sizeof(*spreadIndices), spreadExtraAlloc); options = Js::CallIExtended_SpreadArgs; } @@ -7994,8 +7996,8 @@ void EmitNew(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, FuncInfo* f if (pnode->sxCall.spreadArgCount > 0) { Assert(spreadIndices != nullptr); - uint spreadExtraAlloc = spreadIndices->count * sizeof(uint32); - uint spreadIndicesSize = sizeof(*spreadIndices) + spreadExtraAlloc; + uint spreadExtraAlloc = UInt32Math::Mul(spreadIndices->count, sizeof(uint32)); + uint spreadIndicesSize = UInt32Math::Add(sizeof(*spreadIndices), spreadExtraAlloc); byteCodeGenerator->Writer()->CallIExtended(op, funcInfo->AcquireLoc(pnode), pnode->sxCall.pnodeTarget->location, (uint16)actualArgCount, Js::CallIExtended_SpreadArgs, spreadIndices, spreadIndicesSize, callSiteId); @@ -8413,7 +8415,8 @@ void EmitObjectInitializers(ParseNode *memberList, Js::RegSlot objectLocation, B } else { - Js::PropertyIdArray *propIds = AnewPlus(byteCodeGenerator->GetAllocator(), argCount * sizeof(Js::PropertyId), Js::PropertyIdArray, argCount, 0); + uint32 allocSize = UInt32Math::Mul(argCount, sizeof(Js::PropertyId)); + Js::PropertyIdArray *propIds = AnewPlus(byteCodeGenerator->GetAllocator(), allocSize, Js::PropertyIdArray, argCount, 0); if (propertyIds->ContainsKey(Js::PropertyIds::__proto__)) { @@ -8451,11 +8454,11 @@ void EmitObjectInitializers(ParseNode *memberList, Js::RegSlot objectLocation, B uint32 literalObjectId = funcInfo->GetParsedFunctionBody()->NewObjectLiteral(); // Generate the opcode with propIds and cacheId - byteCodeGenerator->Writer()->Auxiliary(Js::OpCode::NewScObjectLiteral, objectLocation, propIds, sizeof(Js::PropertyIdArray) + argCount * sizeof(Js::PropertyId), literalObjectId); + byteCodeGenerator->Writer()->Auxiliary(Js::OpCode::NewScObjectLiteral, objectLocation, propIds, UInt32Math::Add(sizeof(Js::PropertyIdArray), allocSize), literalObjectId); Adelete(byteCodeGenerator->GetAllocator(), propertyIds); - AdeletePlus(byteCodeGenerator->GetAllocator(), argCount * sizeof(Js::PropertyId), propIds); + AdeletePlus(byteCodeGenerator->GetAllocator(), allocSize, propIds); } memberList = pmemberList; @@ -8594,7 +8597,12 @@ void SetNewArrayElements(ParseNode *pnode, Js::RegSlot arrayLocation, ByteCodeGe bool arrayIntOpt = nativeArrays && pnode->sxArrLit.arrayOfInts; if (arrayIntOpt) { - int extraAlloc = argCount * sizeof(int32); + int extraAlloc = 0, auxSize = 0; + if (Int32Math::Mul(argCount, sizeof(int32), &extraAlloc) + || Int32Math::Add(sizeof(Js::AuxArray), extraAlloc, &auxSize)) + { + ::Math::DefaultOverflowPolicy(); + } Js::AuxArray *ints = AnewPlus(byteCodeGenerator->GetAllocator(), extraAlloc, Js::AuxArray, argCount); EmitConstantArgsToIntArray(byteCodeGenerator, ints->elements, args, argCount); Assert(!pnode->sxArrLit.hasMissingValues); @@ -8602,7 +8610,7 @@ void SetNewArrayElements(ParseNode *pnode, Js::RegSlot arrayLocation, ByteCodeGe Js::OpCode::NewScIntArray, pnode->location, ints, - sizeof(Js::AuxArray) + extraAlloc, + auxSize, argCount); AdeletePlus(byteCodeGenerator->GetAllocator(), extraAlloc, ints); return; @@ -8611,7 +8619,12 @@ void SetNewArrayElements(ParseNode *pnode, Js::RegSlot arrayLocation, ByteCodeGe bool arrayNumOpt = nativeArrays && pnode->sxArrLit.arrayOfNumbers; if (arrayNumOpt) { - int extraAlloc = argCount * sizeof(double); + int extraAlloc = 0, auxSize = 0; + if (Int32Math::Mul(argCount, sizeof(double), &extraAlloc) + || Int32Math::Add(sizeof(Js::AuxArray), extraAlloc, &auxSize)) + { + ::Math::DefaultOverflowPolicy(); + } Js::AuxArray *doubles = AnewPlus(byteCodeGenerator->GetAllocator(), extraAlloc, Js::AuxArray, argCount); EmitConstantArgsToFltArray(byteCodeGenerator, doubles->elements, args, argCount); Assert(!pnode->sxArrLit.hasMissingValues); @@ -8619,7 +8632,7 @@ void SetNewArrayElements(ParseNode *pnode, Js::RegSlot arrayLocation, ByteCodeGe Js::OpCode::NewScFltArray, pnode->location, doubles, - sizeof(Js::AuxArray) + extraAlloc, + auxSize, argCount); AdeletePlus(byteCodeGenerator->GetAllocator(), extraAlloc, doubles); return; @@ -8630,7 +8643,7 @@ void SetNewArrayElements(ParseNode *pnode, Js::RegSlot arrayLocation, ByteCodeGe Js::RegSlot spreadArrLoc = arrayLocation; Js::AuxArray *spreadIndices = nullptr; - const uint extraAlloc = spreadCount * sizeof(uint32); + const uint extraAlloc = UInt32Math::Mul(spreadCount, sizeof(uint32)); if (pnode->sxArrLit.spreadCount > 0) { arrayLocation = funcInfo->AcquireTmpRegister(); @@ -8678,14 +8691,15 @@ void SetNewArrayElements(ParseNode *pnode, Js::RegSlot arrayLocation, ByteCodeGe if (arrayLitOpt) { - Js::VarArray *vars = AnewPlus(byteCodeGenerator->GetAllocator(), argCount * sizeof(Js::Var), Js::VarArray, argCount); + uint32 allocSize = UInt32Math::Mul(argCount, sizeof(Js::Var)); + Js::VarArray *vars = AnewPlus(byteCodeGenerator->GetAllocator(), allocSize, Js::VarArray, argCount); EmitConstantArgsToVarArray(byteCodeGenerator, vars->elements, args, argCount); // Generate the opcode with vars - byteCodeGenerator->Writer()->Auxiliary(Js::OpCode::StArrSegItem_A, arrLoc, vars, sizeof(Js::VarArray) + argCount * sizeof(Js::Var), argCount); + byteCodeGenerator->Writer()->Auxiliary(Js::OpCode::StArrSegItem_A, arrLoc, vars, UInt32Math::Add(sizeof(Js::VarArray), allocSize), argCount); - AdeletePlus(byteCodeGenerator->GetAllocator(), argCount * sizeof(Js::Var), vars); + AdeletePlus(byteCodeGenerator->GetAllocator(), allocSize, vars); } else { @@ -8755,7 +8769,7 @@ void SetNewArrayElements(ParseNode *pnode, Js::RegSlot arrayLocation, ByteCodeGe if (pnode->sxArrLit.spreadCount > 0) { - byteCodeGenerator->Writer()->Reg2Aux(Js::OpCode::SpreadArrayLiteral, spreadArrLoc, arrayLocation, spreadIndices, sizeof(Js::AuxArray) + extraAlloc, extraAlloc); + byteCodeGenerator->Writer()->Reg2Aux(Js::OpCode::SpreadArrayLiteral, spreadArrLoc, arrayLocation, spreadIndices, UInt32Math::Add(sizeof(Js::AuxArray), extraAlloc), extraAlloc); AdeletePlus(byteCodeGenerator->GetAllocator(), extraAlloc, spreadIndices); funcInfo->ReleaseTmpRegister(arrayLocation); } From d97375c40cfd2b2376a5c4b6cd34098e1e99e1f1 Mon Sep 17 00:00:00 2001 From: Michael Holman Date: Fri, 17 Nov 2017 16:59:03 -0800 Subject: [PATCH 07/16] [CVE-2017-11905] JIT optimization fixes - Internal --- lib/Backend/IRBuilderAsmJs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Backend/IRBuilderAsmJs.cpp b/lib/Backend/IRBuilderAsmJs.cpp index 5ac08230eff..fe1973adef0 100644 --- a/lib/Backend/IRBuilderAsmJs.cpp +++ b/lib/Backend/IRBuilderAsmJs.cpp @@ -995,7 +995,7 @@ IRBuilderAsmJs::CreateLabel(IR::BranchInstr * branchInstr, uint & offset) } IR::LabelInstr * labelInstr; - if (instrPrev && instrPrev->IsLabelInstr() && instrPrev->GetByteCodeOffset() == offset) + if (instrPrev && instrPrev->IsLabelInstr()) { // Found an existing label at the right offset. Just reuse it. labelInstr = instrPrev->AsLabelInstr(); From 69e03c35543d2bf3befce6885d78cd751bf5db10 Mon Sep 17 00:00:00 2001 From: Rajat Dua Date: Tue, 28 Nov 2017 18:33:52 -0800 Subject: [PATCH 08/16] [CVE-2017-11916] Remote Code Execution Vul - Qihoo 360 --- lib/Backend/Lower.cpp | 100 ++++++++++++++++++++++++++---------------- lib/Backend/Lower.h | 1 + 2 files changed, 64 insertions(+), 37 deletions(-) diff --git a/lib/Backend/Lower.cpp b/lib/Backend/Lower.cpp index 309ae5e2d8b..1f8f734cbf1 100644 --- a/lib/Backend/Lower.cpp +++ b/lib/Backend/Lower.cpp @@ -18425,22 +18425,28 @@ Lowerer::GenerateFastReplace(IR::Opnd* strOpnd, IR::Opnd* src1, IR::Opnd* src2, this->GenerateStringTest(src2->AsRegOpnd(), insertInstr, labelHelper); } - //scriptContext, pRegEx, pThis, pReplace (to be pushed in reverse order) - - // pReplace, pThis, pRegEx - this->m_lowererMD.LoadHelperArgument(insertInstr, src2); - this->m_lowererMD.LoadHelperArgument(insertInstr, strOpnd); - this->m_lowererMD.LoadHelperArgument(insertInstr, src1); - - // script context - LoadScriptContext(insertInstr); - IR::Instr * helperCallInstr = IR::Instr::New(LowererMD::MDCallOpcode, insertInstr->m_func); - if(callDst) + if (callDst) { helperCallInstr->SetDst(callDst); } insertInstr->InsertBefore(helperCallInstr); + + if (insertInstr->HasBailOutInfo() && BailOutInfo::IsBailOutOnImplicitCalls(insertInstr->GetBailOutKind())) + { + helperCallInstr = AddBailoutToHelperCallInstr(helperCallInstr, insertInstr->GetBailOutInfo(), insertInstr->GetBailOutKind(), insertInstr); + } + + //scriptContext, pRegEx, pThis, pReplace (to be pushed in reverse order) + + // pReplace, pThis, pRegEx + this->m_lowererMD.LoadHelperArgument(helperCallInstr, src2); + this->m_lowererMD.LoadHelperArgument(helperCallInstr, strOpnd); + this->m_lowererMD.LoadHelperArgument(helperCallInstr, src1); + + // script context + LoadScriptContext(helperCallInstr); + if(callDst) { m_lowererMD.ChangeToHelperCall(helperCallInstr, IR::JnHelperMethod::HelperRegExp_ReplaceStringResultUsed); @@ -18520,6 +18526,17 @@ Lowerer::GenerateFastInlineStringSplitMatch(IR::Instr * instr) labelHelper, instr); + IR::Instr * helperCallInstr = IR::Instr::New(LowererMD::MDCallOpcode, instr->m_func); + if (callDst) + { + helperCallInstr->SetDst(callDst); + } + instr->InsertBefore(helperCallInstr); + if (instr->HasBailOutInfo() && BailOutInfo::IsBailOutOnImplicitCalls(instr->GetBailOutKind())) + { + helperCallInstr = AddBailoutToHelperCallInstr(helperCallInstr, instr->GetBailOutInfo(), instr->GetBailOutKind(), instr); + } + // [stackAllocationPointer, ]scriptcontext, regexp, input[, limit] (to be pushed in reverse order) if(src1->AsHelperCallOpnd()->m_fnHelper == IR::JnHelperMethod::HelperString_Split) @@ -18527,15 +18544,15 @@ Lowerer::GenerateFastInlineStringSplitMatch(IR::Instr * instr) //limit //As we are optimizing only for two operands, make limit UINT_MAX IR::Opnd* limit = IR::IntConstOpnd::New(UINT_MAX, TyUint32, instr->m_func); - this->m_lowererMD.LoadHelperArgument(instr, limit); + this->m_lowererMD.LoadHelperArgument(helperCallInstr, limit); } //input, regexp - this->m_lowererMD.LoadHelperArgument(instr, argsOpnd[0]); - this->m_lowererMD.LoadHelperArgument(instr, argsOpnd[1]); + this->m_lowererMD.LoadHelperArgument(helperCallInstr, argsOpnd[0]); + this->m_lowererMD.LoadHelperArgument(helperCallInstr, argsOpnd[1]); // script context - LoadScriptContext(instr); + LoadScriptContext(helperCallInstr); IR::JnHelperMethod helperMethod = IR::JnHelperMethod::HelperInvalid; IR::AutoReuseOpnd autoReuseStackAllocationOpnd; @@ -18560,8 +18577,8 @@ Lowerer::GenerateFastInlineStringSplitMatch(IR::Instr * instr) IR::RegOpnd *const stackAllocationOpnd = IR::RegOpnd::New(TyVar, m_func); autoReuseStackAllocationOpnd.Initialize(stackAllocationOpnd, m_func); stackAllocationOpnd->SetValueType(callDst->GetValueType()); - GenerateMarkTempAlloc(stackAllocationOpnd, Js::JavascriptArray::StackAllocationSize, instr); - m_lowererMD.LoadHelperArgument(instr, stackAllocationOpnd); + GenerateMarkTempAlloc(stackAllocationOpnd, Js::JavascriptArray::StackAllocationSize, helperCallInstr); + m_lowererMD.LoadHelperArgument(helperCallInstr, stackAllocationOpnd); } else { @@ -18587,13 +18604,6 @@ Lowerer::GenerateFastInlineStringSplitMatch(IR::Instr * instr) } } - IR::Instr * helperCallInstr = IR::Instr::New(LowererMD::MDCallOpcode, instr->m_func); - if(callDst) - { - helperCallInstr->SetDst(callDst); - } - instr->InsertBefore(helperCallInstr); - m_lowererMD.ChangeToHelperCall(helperCallInstr, helperMethod); IR::LabelInstr *doneLabel = IR::LabelInstr::New(Js::OpCode::Label, this->m_func); @@ -18768,20 +18778,30 @@ Lowerer::GenerateFastInlineRegExpExec(IR::Instr * instr) instr->InsertBefore(labelFastHelper); } + IR::Instr * helperCallInstr = IR::Instr::New(LowererMD::MDCallOpcode, instr->m_func); + if (callDst) + { + helperCallInstr->SetDst(callDst); + } + instr->InsertBefore(helperCallInstr); + if (instr->HasBailOutInfo() && BailOutInfo::IsBailOutOnImplicitCalls(instr->GetBailOutKind())) + { + helperCallInstr = AddBailoutToHelperCallInstr(helperCallInstr, instr->GetBailOutInfo(), instr->GetBailOutKind(), instr); + } // [stackAllocationPointer, ]scriptcontext, regexp, string (to be pushed in reverse order) //string, regexp - this->m_lowererMD.LoadHelperArgument(instr, opndString); - this->m_lowererMD.LoadHelperArgument(instr, opndRegex); + this->m_lowererMD.LoadHelperArgument(helperCallInstr, opndString); + this->m_lowererMD.LoadHelperArgument(helperCallInstr, opndRegex); // script context - LoadScriptContext(instr); + LoadScriptContext(helperCallInstr); IR::JnHelperMethod helperMethod; IR::AutoReuseOpnd autoReuseStackAllocationOpnd; - if(callDst) + if (callDst) { - if(instr->dstIsTempObject) + if (instr->dstIsTempObject) { helperMethod = IR::JnHelperMethod::HelperRegExp_ExecResultUsedAndMayBeTemp; @@ -18789,8 +18809,8 @@ Lowerer::GenerateFastInlineRegExpExec(IR::Instr * instr) IR::RegOpnd *const stackAllocationOpnd = IR::RegOpnd::New(TyVar, m_func); autoReuseStackAllocationOpnd.Initialize(stackAllocationOpnd, m_func); stackAllocationOpnd->SetValueType(callDst->GetValueType()); - GenerateMarkTempAlloc(stackAllocationOpnd, Js::JavascriptArray::StackAllocationSize, instr); - m_lowererMD.LoadHelperArgument(instr, stackAllocationOpnd); + GenerateMarkTempAlloc(stackAllocationOpnd, Js::JavascriptArray::StackAllocationSize, helperCallInstr); + m_lowererMD.LoadHelperArgument(helperCallInstr, stackAllocationOpnd); } else { @@ -18802,12 +18822,6 @@ Lowerer::GenerateFastInlineRegExpExec(IR::Instr * instr) helperMethod = IR::JnHelperMethod::HelperRegExp_ExecResultNotUsed; } - IR::Instr * helperCallInstr = IR::Instr::New(LowererMD::MDCallOpcode, instr->m_func); - if(callDst) - { - helperCallInstr->SetDst(callDst); - } - instr->InsertBefore(helperCallInstr); m_lowererMD.ChangeToHelperCall(helperCallInstr, helperMethod); instr->InsertAfter(doneLabel); @@ -25536,6 +25550,18 @@ Lowerer::InsertLoopTopLabel(IR::Instr * insertBeforeInstr) return loopTopLabel; } +IR::Instr * +Lowerer::AddBailoutToHelperCallInstr(IR::Instr * helperCallInstr, BailOutInfo * bailoutInfo, IR::BailOutKind bailoutKind, IR::Instr * primaryBailoutInstr) +{ + helperCallInstr = helperCallInstr->ConvertToBailOutInstr(bailoutInfo, bailoutKind); + if (bailoutInfo->bailOutInstr == primaryBailoutInstr) + { + IR::Instr * instrShare = primaryBailoutInstr->ShareBailOut(); + LowerBailTarget(instrShare); + } + return helperCallInstr; +} + #if DBG void Lowerer::LegalizeVerifyRange(IR::Instr * instrStart, IR::Instr * instrLast) diff --git a/lib/Backend/Lower.h b/lib/Backend/Lower.h index 09dfdf33224..ec09ac6fe0a 100644 --- a/lib/Backend/Lower.h +++ b/lib/Backend/Lower.h @@ -666,6 +666,7 @@ class Lowerer void GenerateHasObjectArrayCheck(IR::RegOpnd * objectOpnd, IR::RegOpnd * typeOpnd, IR::LabelInstr * hasObjectArray, IR::Instr * insertBeforeInstr); IR::LabelInstr* InsertLoopTopLabel(IR::Instr * insertBeforeInstr); + IR::Instr * AddBailoutToHelperCallInstr(IR::Instr * helperCallInstr, BailOutInfo * bailoutInfo, IR::BailOutKind bailoutKind, IR::Instr * primaryBailoutInstr); public: static IRType GetImplicitCallFlagsType() { From 760822c7bf4ffd5e773da14bc35d9c07d672f0c7 Mon Sep 17 00:00:00 2001 From: Rajat Dua Date: Tue, 28 Nov 2017 18:50:04 -0800 Subject: [PATCH 09/16] [CVE-2017-11893] JIT Op_MaxInAnArray and Op_MinInAnArray can explicitly call user defined JavaScript functions - Google, Inc. --- lib/Backend/Inline.cpp | 91 ++++++++++++++++++++++++++---------------- lib/Backend/Inline.h | 6 +-- 2 files changed, 59 insertions(+), 38 deletions(-) diff --git a/lib/Backend/Inline.cpp b/lib/Backend/Inline.cpp index c52f3eff0ba..b5b6431ec19 100644 --- a/lib/Backend/Inline.cpp +++ b/lib/Backend/Inline.cpp @@ -1983,19 +1983,7 @@ Inline::InlineBuiltInFunction(IR::Instr *callInstr, const FunctionJITTimeInfo * StackSym* originalCallTargetStackSym = callInstr->GetSrc1()->GetStackSym(); bool originalCallTargetOpndIsJITOpt = callInstr->GetSrc1()->GetIsJITOptimizedReg(); - // We are committed to inlining, optimize the call instruction for fixed fields now and don't attempt it later. - bool safeThis = false; - if (TryOptimizeCallInstrWithFixedMethod(callInstr, inlineeData, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/, safeThis /*unused here*/)) - { - Assert(callInstr->m_opcode == Js::OpCode::CallIFixed); - Assert(callInstr->GetFixedFunction()->GetFuncInfoAddr() == inlineeData->GetFunctionInfoAddr()); - } - else - { - // FunctionObject check for built-ins - IR::BailOutInstr * bailOutInstr = IR::BailOutInstr::New(Js::OpCode::BailOnNotBuiltIn, IR::BailOutOnInlineFunction, callInstr, callInstr->m_func); - InsertFunctionObjectCheck(callInstr, callInstr, bailOutInstr, inlineeData); - } + IR::ByteCodeUsesInstr* useCallTargetInstr = EmitFixedMethodOrFunctionObjectChecksForBuiltIns(callInstr, callInstr, inlineeData, false, true, false, true); // To push function object for cases when we have to make calls to helper method to assist in inlining if(inlineCallOpCode == Js::OpCode::CallDirect) @@ -2031,11 +2019,9 @@ Inline::InlineBuiltInFunction(IR::Instr *callInstr, const FunctionJITTimeInfo * } } - // Insert a byteCodeUsesInstr to make sure the function object's lifetime is extended beyond the last bailout point - // at which we may need to call the inlinee again in the interpreter. + if (useCallTargetInstr) { - IR::ByteCodeUsesInstr * useCallTargetInstr = IR::ByteCodeUsesInstr::New(callInstr); - useCallTargetInstr->SetRemovedOpndSymbol(originalCallTargetOpndIsJITOpt, originalCallTargetStackSym->m_id); + useCallTargetInstr->Unlink(); callInstr->InsertBefore(useCallTargetInstr); } @@ -2071,7 +2057,7 @@ Inline::InlineBuiltInFunction(IR::Instr *callInstr, const FunctionJITTimeInfo * // Insert a byteCodeUsesInstr to make sure the function object's lifetime is extended beyond the last bailout point // at which we may need to call the inlinee again in the interpreter. - IR::ByteCodeUsesInstr * useCallTargetInstr = IR::ByteCodeUsesInstr::New(callInstr->GetPrevRealInstrOrLabel()); + useCallTargetInstr = IR::ByteCodeUsesInstr::New(callInstr->GetPrevRealInstrOrLabel()); useCallTargetInstr->SetRemovedOpndSymbol(originalCallTargetOpndIsJITOpt, originalCallTargetStackSym->m_id); if(inlineCallOpCode == Js::OpCode::InlineArrayPop) @@ -2364,7 +2350,7 @@ IR::Instr* Inline::InlineApply(IR::Instr *callInstr, const FunctionJITTimeInfo * // TODO: OOP JIT enable assert (readprocessmemory?) //Assert((inlineeData->GetFunctionInfo()->GetAttributes() & Js::FunctionInfo::Attributes::BuiltInInlinableAsLdFldInlinee) != 0); - return InlineApplyWithArray(callInstr, applyData, Js::JavascriptLibrary::GetBuiltInForFuncInfo(inlineeData->GetFunctionInfoAddr(), this->topFunc->GetThreadContextInfo())); + return InlineApplyBuiltInTargetWithArray(callInstr, applyData, inlineeData); } else { @@ -2477,7 +2463,7 @@ IR::Instr * Inline::InlineApplyWithArgumentsObject(IR::Instr * callInstr, IR::In /* This method will only do CallDirect style inlining of built-in targets. No script function inlining. */ -IR::Instr * Inline::InlineApplyWithArray(IR::Instr * callInstr, const FunctionJITTimeInfo * funcInfo, Js::BuiltinFunction builtInId) +IR::Instr * Inline::InlineApplyBuiltInTargetWithArray(IR::Instr * callInstr, const FunctionJITTimeInfo * applyInfo, const FunctionJITTimeInfo * builtInInfo) { IR::Instr * implicitThisArgOut = nullptr; IR::Instr * explicitThisArgOut = nullptr; @@ -2485,7 +2471,25 @@ IR::Instr * Inline::InlineApplyWithArray(IR::Instr * callInstr, const FunctionJI uint argOutCount = 0; this->GetArgInstrsForCallAndApply(callInstr, &implicitThisArgOut, &explicitThisArgOut, &arrayArgOut, argOutCount); - TryFixedMethodAndPrepareInsertionPoint(callInstr, funcInfo, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/); + Js::OpCode originalCallOpcode = callInstr->m_opcode; + IR::Opnd * originalCallSrc1 = callInstr->GetSrc1()->Copy(this->topFunc); + IR::AutoReuseOpnd autoReuseOriginalCallSrc1(originalCallSrc1, this->topFunc); + + IR::Instr* applyLdInstr = nullptr; + IR::Instr* applyTargetLdInstr = nullptr; + if (!TryGetApplyAndTargetLdInstrs(callInstr, &applyLdInstr, &applyTargetLdInstr)) + { + return callInstr; + } + + // Fixed function/function object checks for target built-in + callInstr->ReplaceSrc1(applyTargetLdInstr->GetDst()); + EmitFixedMethodOrFunctionObjectChecksForBuiltIns(callInstr, callInstr, builtInInfo, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/); + + // Fixed function/function object checks for .apply + callInstr->m_opcode = originalCallOpcode; + callInstr->ReplaceSrc1(originalCallSrc1); + EmitFixedMethodOrFunctionObjectChecksForBuiltIns(callInstr, callInstr, applyInfo, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/); IR::Instr* builtInEndInstr = InsertInlineeBuiltInStartEndTags(callInstr, 3); // 3 args (implicit this + explicit this + array = 3) builtInEndInstr->m_opcode = Js::OpCode::InlineNonTrackingBuiltInEnd; // We will call EndTrackCall when we see CallDirect for reasons explained in GlobOpt::TrackCalls @@ -2513,6 +2517,7 @@ IR::Instr * Inline::InlineApplyWithArray(IR::Instr * callInstr, const FunctionJI argOut = IR::Instr::New(Js::OpCode::ArgOut_A_InlineSpecialized, linkOpnd, implicitThisArgOut->GetSrc1(), argOut->GetDst(), callInstr->m_func); callInstr->InsertBefore(argOut); + Js::BuiltinFunction builtInId = Js::JavascriptLibrary::GetBuiltInForFuncInfo(builtInInfo->GetFunctionInfoAddr(), this->topFunc->GetThreadContextInfo()); IR::HelperCallOpnd * helperCallOpnd = nullptr; switch (builtInId) { @@ -2543,7 +2548,7 @@ IR::Instr * Inline::InlineApplyWithoutArrayArgument(IR::Instr *callInstr, const uint argOutCount = 0; this->GetArgInstrsForCallAndApply(callInstr, &implicitThisArgOut, &explicitThisArgOut, &dummyInstr, argOutCount); - TryFixedMethodAndPrepareInsertionPoint(callInstr, applyInfo, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/); + EmitFixedMethodOrFunctionObjectChecksForBuiltIns(callInstr, callInstr, applyInfo, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/); InsertInlineeBuiltInStartEndTags(callInstr, 2); // 2 args (implicit this + explicit this) @@ -2616,6 +2621,22 @@ void Inline::GetArgInstrsForCallAndApply(IR::Instr* callInstr, IR::Instr** impli linkOpnd->AsRegOpnd()->m_sym->m_isInlinedArgSlot = true; } +bool Inline::TryGetApplyAndTargetLdInstrs(IR::Instr * callInstr, _Outptr_result_maybenull_ IR::Instr ** applyLdInstr, _Outptr_result_maybenull_ IR::Instr ** applyTargetLdInstr) +{ + IR::Opnd* applyOpnd = callInstr->GetSrc1(); + Assert(applyOpnd->IsRegOpnd()); + StackSym* applySym = applyOpnd->AsRegOpnd()->m_sym->AsStackSym(); + if (!applySym->IsSingleDef()) + { + *applyLdInstr = nullptr; + *applyTargetLdInstr = nullptr; + return false; + } + *applyLdInstr = applySym->GetInstrDef();; + *applyTargetLdInstr = (*applyLdInstr)->m_prev; + return true; +} + /* This method only inlines targets which are script functions, under the condition that the second argument (if any) passed to apply is arguments object. @@ -2637,16 +2658,13 @@ bool Inline::InlineApplyScriptTarget(IR::Instr *callInstr, const FunctionJITTime // Begin inlining apply target - IR::Opnd* applyOpnd = callInstr->GetSrc1(); - Assert(applyOpnd->IsRegOpnd()); - StackSym* applySym = applyOpnd->AsRegOpnd()->m_sym->AsStackSym(); - if (!applySym->IsSingleDef()) + IR::Instr* applyLdInstr = nullptr; + IR::Instr* applyTargetLdInstr = nullptr; + if (!TryGetApplyAndTargetLdInstrs(callInstr, &applyLdInstr, &applyTargetLdInstr)) { return false; } - IR::Instr* applyLdInstr = applySym->GetInstrDef(); - IR::Instr* applyTargetLdInstr = applyLdInstr->m_prev; - + if(applyTargetLdInstr->m_opcode != Js::OpCode::LdFldForCallApplyTarget || ((applyTargetLdInstr->AsProfiledInstr()->u.FldInfo().flags & Js::FldInfo_FromAccessor) != 0)) { @@ -2908,7 +2926,7 @@ Inline::InlineCall(IR::Instr *callInstr, const FunctionJITTimeInfo *funcInfo, co IR::SymOpnd* orgLinkOpnd = callInstr->GetSrc2()->AsSymOpnd(); - TryFixedMethodAndPrepareInsertionPoint(callInstr, funcInfo, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/); + EmitFixedMethodOrFunctionObjectChecksForBuiltIns(callInstr, callInstr, funcInfo, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/); InsertInlineeBuiltInStartEndTags(callInstr, actualCount); @@ -4225,26 +4243,29 @@ Inline::PrepareInsertionPoint(IR::Instr *callInstr, const FunctionJITTimeInfo *f return primaryBailOutInstr; } -void -Inline::TryFixedMethodAndPrepareInsertionPoint(IR::Instr *callInstr, const FunctionJITTimeInfo * inlineeInfo, bool isPolymorphic, bool isBuiltIn, bool isCtor, bool isInlined) +IR::ByteCodeUsesInstr* +Inline::EmitFixedMethodOrFunctionObjectChecksForBuiltIns(IR::Instr *callInstr, IR::Instr * funcObjCheckInsertInstr, const FunctionJITTimeInfo * inlineeInfo, bool isPolymorphic, bool isBuiltIn, bool isCtor, bool isInlined) { StackSym* originalCallTargetStackSym = callInstr->GetSrc1()->GetStackSym(); bool originalCallTargetIsJITOpt = callInstr->GetSrc1()->GetIsJITOptimizedReg(); + IR::ByteCodeUsesInstr * useCallTargetInstr = nullptr; bool safeThis = false; if (TryOptimizeCallInstrWithFixedMethod(callInstr, inlineeInfo, isPolymorphic, isBuiltIn, isCtor, isInlined, safeThis)) { Assert(callInstr->m_opcode == Js::OpCode::CallIFixed); - + Assert(callInstr->GetFixedFunction()->GetFuncInfoAddr() == inlineeInfo->GetFunctionInfoAddr()); // If we optimized the call instruction for a fixed function, we must extend the function object's lifetime until after the last bailout before the call. - IR::ByteCodeUsesInstr * useCallTargetInstr = IR::ByteCodeUsesInstr::New(callInstr); + useCallTargetInstr = IR::ByteCodeUsesInstr::New(callInstr); useCallTargetInstr->SetRemovedOpndSymbol(originalCallTargetIsJITOpt, originalCallTargetStackSym->m_id); callInstr->InsertBefore(useCallTargetInstr); } else { - PrepareInsertionPoint(callInstr, inlineeInfo, callInstr); + IR::BailOutInstr * bailOutInstr = IR::BailOutInstr::New(Js::OpCode::BailOnNotBuiltIn, IR::BailOutOnInlineFunction, callInstr, callInstr->m_func); + InsertFunctionObjectCheck(callInstr, funcObjCheckInsertInstr, bailOutInstr, inlineeInfo); } + return useCallTargetInstr; } uint Inline::CountActuals(IR::Instr *callInstr) diff --git a/lib/Backend/Inline.h b/lib/Backend/Inline.h index f4f00ceb348..e7e1ed58f91 100644 --- a/lib/Backend/Inline.h +++ b/lib/Backend/Inline.h @@ -47,13 +47,13 @@ class Inline IR::Instr * SimulateCallForGetterSetter(IR::Instr *accessorInstr, IR::Instr* insertInstr, IR::PropertySymOpnd* methodOpnd, bool isGetter); IR::Instr * InlineApply(IR::Instr *callInstr, const FunctionJITTimeInfo * applyData, const FunctionJITTimeInfo * inlinerData, const StackSym *symThis, bool* pIsInlined, uint callSiteId, uint recursiveInlineDepth, uint argsCount); - IR::Instr * InlineApplyWithArray(IR::Instr *callInstr, const FunctionJITTimeInfo * inlineeInfo, Js::BuiltinFunction builtInId); + IR::Instr * InlineApplyBuiltInTargetWithArray(IR::Instr *callInstr, const FunctionJITTimeInfo * applyInfo, const FunctionJITTimeInfo * builtInInfo); IR::Instr * InlineApplyWithArgumentsObject(IR::Instr * callInstr, IR::Instr * argsObjectArgInstr, const FunctionJITTimeInfo * inlineeInfo); IR::Instr * InlineApplyWithoutArrayArgument(IR::Instr *callInstr, const FunctionJITTimeInfo * applyInfo, const FunctionJITTimeInfo * applyTargetInfo); bool InlineApplyScriptTarget(IR::Instr *callInstr, const FunctionJITTimeInfo* inlinerData, const FunctionJITTimeInfo** pInlineeData, const FunctionJITTimeInfo * applyFuncInfo, const StackSym *symThis, IR::Instr ** returnInstr, uint recursiveInlineDepth, bool isArrayOpndArgumentsObject, uint argsCount); void GetArgInstrsForCallAndApply(IR::Instr* callInstr, IR::Instr** implicitThisArgOut, IR::Instr** explicitThisArgOut, IR::Instr** argumentsOrArrayArgOut, uint &argOutCount); - + bool TryGetApplyAndTargetLdInstrs(IR::Instr * callInstr, _Outptr_result_maybenull_ IR::Instr ** applyLdInstr, _Outptr_result_maybenull_ IR::Instr ** applyTargetLdInstr); IR::Instr * InlineCall(IR::Instr *callInstr, const FunctionJITTimeInfo * inlineeData, const FunctionJITTimeInfo * inlinerData, const StackSym *symThis, bool* pIsInlined, uint callSiteId, uint recursiveInlineDepth); bool InlineCallTarget(IR::Instr *callInstr, const FunctionJITTimeInfo* inlinerData, const FunctionJITTimeInfo** pInlineeData, const FunctionJITTimeInfo *callFuncInfo, const StackSym *symThis, IR::Instr ** returnInstr, uint recursiveInlineDepth); @@ -83,7 +83,7 @@ class Inline void FixupExtraActualParams(IR::Instr * instr, IR::Instr *argOuts[], IR::Instr *argOutsExtra[], uint index, uint actualCount, Js::ProfileId callSiteId); void RemoveExtraFixupArgouts(IR::Instr* instr, uint argoutRemoveCount, Js::ProfileId callSiteId); IR::Instr* PrepareInsertionPoint(IR::Instr *callInstr, const FunctionJITTimeInfo *funcInfo, IR::Instr *insertBeforeInstr, IR::BailOutKind bailOutKind = IR::BailOutOnInlineFunction); - void TryFixedMethodAndPrepareInsertionPoint(IR::Instr *callInstr, const FunctionJITTimeInfo * inlineeInfo, bool isPolymorphic, bool isBuiltIn, bool isCtor, bool isInlined); + IR::ByteCodeUsesInstr* EmitFixedMethodOrFunctionObjectChecksForBuiltIns(IR::Instr *callInstr, IR::Instr * funcObjCheckInsertInstr, const FunctionJITTimeInfo * inlineeInfo, bool isPolymorphic, bool isBuiltIn, bool isCtor, bool isInlined); Js::ArgSlot MapActuals(IR::Instr *callInstr, __out_ecount(maxParamCount) IR::Instr *argOuts[], Js::ArgSlot formalCount, Func *inlinee, Js::ProfileId callSiteId, bool *stackArgsArgOutExpanded, IR::Instr *argOutsExtra[] = nullptr, Js::ArgSlot maxParamCount = Js::InlineeCallInfo::MaxInlineeArgoutCount); uint32 CountActuals(IR::Instr *callIntr); void MapFormals(Func *inlinee, __in_ecount(formalCount) IR::Instr *argOuts[], uint formalCount, uint actualCount, IR::RegOpnd *retOpnd, IR::Opnd * funcObjOpnd, const StackSym *symCallerThis, bool stackArgsArgOutExpanded, bool fixedFunctionSafeThis = false, IR::Instr *argOutsExtra[] = nullptr); From 39eecff7daecce96088f7ed737f145ee4774faa6 Mon Sep 17 00:00:00 2001 From: Akrosh Gandhi Date: Wed, 29 Nov 2017 16:45:03 -0800 Subject: [PATCH 10/16] [CVE-2017-11908] Invalid stack restore when destructuring is used as a call param The destructuring element in the call argslist was not correctly determined as we were not restoring the previous state. Fixed that --- lib/Parser/Parse.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/Parser/Parse.h b/lib/Parser/Parse.h index ec01e9a0315..e21d3ad28b4 100644 --- a/lib/Parser/Parse.h +++ b/lib/Parser/Parse.h @@ -1125,6 +1125,7 @@ class Parser : m_parser(parser) { m_prevState = m_parser->GetIsInParsingArgList(); + m_prevDestructuringState = m_parser->GetHasDestructuringPattern(); m_parser->SetHasDestructuringPattern(false); m_parser->SetIsInParsingArgList(true); } @@ -1135,11 +1136,20 @@ class Parser { m_parser->SetHasDestructuringPattern(false); } + else + { + // Reset back to previous state only when the current call node does not have usage of destructuring expression. + if (!m_parser->GetHasDestructuringPattern()) + { + m_parser->SetHasDestructuringPattern(m_prevDestructuringState); + } + } } private: Parser *m_parser; bool m_prevState; + bool m_prevDestructuringState; }; public: From b488088279391deaaacfce4f0756caaefc284109 Mon Sep 17 00:00:00 2001 From: Aneesh Divakarakurup Date: Fri, 1 Dec 2017 15:22:11 -0800 Subject: [PATCH 11/16] [CVE-2017-11914] JavascriptGeneratorFunction::GetPropertyBuiltIns exposes scriptFunction - Google, Inc. While trying to get the length property pass the generator function as this not the inner script function. --- lib/Runtime/Library/JavascriptGeneratorFunction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Runtime/Library/JavascriptGeneratorFunction.cpp b/lib/Runtime/Library/JavascriptGeneratorFunction.cpp index 9f9168a1a5f..385906a5d8a 100644 --- a/lib/Runtime/Library/JavascriptGeneratorFunction.cpp +++ b/lib/Runtime/Library/JavascriptGeneratorFunction.cpp @@ -307,7 +307,7 @@ namespace Js // to get the length from our private ScriptFunction instead of ourself. int len = 0; Var varLength; - if (scriptFunction->GetProperty(scriptFunction, PropertyIds::length, &varLength, NULL, requestContext)) + if (scriptFunction->GetProperty(this, PropertyIds::length, &varLength, NULL, requestContext)) { len = JavascriptConversion::ToInt32(varLength, requestContext); } From fde164333c3f1f273b446f4c05dfe2c49ea39f3d Mon Sep 17 00:00:00 2001 From: Aneesh Divakarakurup Date: Fri, 1 Dec 2017 15:30:06 -0800 Subject: [PATCH 12/16] [CVE-2017-11919] An infoleak bug in the latest version of Edge - Qihoo 360 In ConstructName the finalName is copied over from propertyName which can contain null character in between. In that case part of finalName will remain uninitialized as we use RecyclerNewArrayLeaf to allocate finalName with the length of propertyName. --- lib/Runtime/Library/JavascriptObject.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Runtime/Library/JavascriptObject.cpp b/lib/Runtime/Library/JavascriptObject.cpp index d75a99715dd..7682e9aa1d1 100644 --- a/lib/Runtime/Library/JavascriptObject.cpp +++ b/lib/Runtime/Library/JavascriptObject.cpp @@ -1857,7 +1857,7 @@ namespace Js size_t totalChars; if (SizeTAdd(propertyLength, ConstructNameGetSetLength, &totalChars) == S_OK) { - finalName = RecyclerNewArrayLeaf(scriptContext->GetRecycler(), char16, totalChars); + finalName = RecyclerNewArrayLeafZ(scriptContext->GetRecycler(), char16, totalChars); Assert(finalName != nullptr); const char16* propertyName = propertyRecord->GetBuffer(); Assert(propertyName != nullptr); From 66b9abb148705d2a36062931cd5a8466a588e6e4 Mon Sep 17 00:00:00 2001 From: "Thomas Moore (CHAKRA)" Date: Fri, 1 Dec 2017 19:00:02 -0800 Subject: [PATCH 13/16] [CVE-2017-11889] UaF On the latest Patch - Qihoo 360 This change addresses a UAF that occurs when jitted code tries to index into a detached ArrayBuffer. The POC involves the convergence of JITing - a virtual type buffer (a certain performant type buffer that meets certain criteria) - A proxy object whose setter can cause the type buffer to be detached With this, it's possible to JIT code that indexes into the buffer without any checks. Thus, it's possible to index into the freed memory that backed the ArrayBuffer. The fix is to make sure that any call before a virtual ArrayBuffer access has a bailout to detect and protect against this assertion. --- lib/Backend/GlobOpt.cpp | 6 ++++-- lib/Backend/ValueInfo.h | 1 + lib/Runtime/Language/ValueType.cpp | 5 +++++ lib/Runtime/Language/ValueType.h | 1 + 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/Backend/GlobOpt.cpp b/lib/Backend/GlobOpt.cpp index 7c8d93e9643..f634c6b53b5 100644 --- a/lib/Backend/GlobOpt.cpp +++ b/lib/Backend/GlobOpt.cpp @@ -12946,7 +12946,9 @@ GlobOpt::ProcessValueKills(IR::Instr *const instr) Assert(kills.KillsTypedArrayHeadSegmentLengths()); // - Calls need to kill the value types of values in the following list. For instance, calls can transform a JS array - // into an ES5 array, so any definitely-array value types need to be killed. Update the value types. + // into an ES5 array, so any definitely-array value types need to be killed. Also, VirtualTypeArrays do not have + // bounds checks; this can be problematic if the array is detached, so check to ensure that it is a virtual array. + // Update the value types to likley to ensure a bailout that asserts Array type is generated. // - Calls also need to kill typed array head segment lengths. A typed array's array buffer may be transferred to a web // worker, in which case the typed array's length is set to zero. for(auto it = valuesToKillOnCalls->GetIterator(); it.IsValid(); it.MoveNext()) @@ -12956,7 +12958,7 @@ GlobOpt::ProcessValueKills(IR::Instr *const instr) Assert( valueInfo->IsArrayOrObjectWithArray() || valueInfo->IsOptimizedTypedArray() && valueInfo->AsArrayValueInfo()->HeadSegmentLengthSym()); - if(valueInfo->IsArrayOrObjectWithArray()) + if (valueInfo->IsArrayOrObjectWithArray() || valueInfo->IsOptimizedVirtualTypedArray()) { ChangeValueType(nullptr, value, valueInfo->Type().ToLikely(), false); continue; diff --git a/lib/Backend/ValueInfo.h b/lib/Backend/ValueInfo.h index 8c2c6158381..e8cff7ff8b2 100644 --- a/lib/Backend/ValueInfo.h +++ b/lib/Backend/ValueInfo.h @@ -153,6 +153,7 @@ class ValueInfo : protected ValueType using ValueType::IsLikelyTypedArray; using ValueType::IsOptimizedTypedArray; + using ValueType::IsOptimizedVirtualTypedArray; using ValueType::IsLikelyOptimizedTypedArray; using ValueType::IsLikelyOptimizedVirtualTypedArray; diff --git a/lib/Runtime/Language/ValueType.cpp b/lib/Runtime/Language/ValueType.cpp index 22ef773b63d..aa90004056f 100644 --- a/lib/Runtime/Language/ValueType.cpp +++ b/lib/Runtime/Language/ValueType.cpp @@ -650,6 +650,11 @@ bool ValueType::IsOptimizedTypedArray() const return IsObject() && ((GetObjectType() >= ObjectType::Int8Array && GetObjectType() <= ObjectType::Float64MixedArray)); } +bool ValueType::IsOptimizedVirtualTypedArray() const +{ + return IsObject() && (GetObjectType() >= ObjectType::Int8VirtualArray && GetObjectType() <= ObjectType::Float64VirtualArray); +} + bool ValueType::IsLikelyOptimizedTypedArray() const { return IsLikelyObject() && ((GetObjectType() >= ObjectType::Int8Array && GetObjectType() <= ObjectType::Float64MixedArray)); diff --git a/lib/Runtime/Language/ValueType.h b/lib/Runtime/Language/ValueType.h index f789653575c..431867a9b12 100644 --- a/lib/Runtime/Language/ValueType.h +++ b/lib/Runtime/Language/ValueType.h @@ -228,6 +228,7 @@ class ValueType bool IsTypedIntOrFloatArray() const; bool IsOptimizedTypedArray() const; + bool IsOptimizedVirtualTypedArray() const; bool IsLikelyOptimizedTypedArray() const; bool IsLikelyOptimizedVirtualTypedArray() const; From 5d4aca73b25cc9bd627e3064b53cfb97a3b3ed8c Mon Sep 17 00:00:00 2001 From: Richard Cobbe Date: Tue, 7 Nov 2017 16:36:45 -0800 Subject: [PATCH 14/16] [CVE-2017-11894] An OOB Access in Chakra Engine - Individual Avoid integer overflow in implementation of `String.prototype.replace`: - Failfast in Chakra regex parser if a regex contains more than 2^15 capturing groups, thus avoiding overflow when passing the matching string's to `replace`'s second argument. - Add comments to `RegexHelper::ReplaceFormatString` explaining why the capture index cannot overflow; add asserts to guard against it. --- lib/Parser/RegexParser.cpp | 4 ++++ lib/Parser/RegexParser.h | 10 ++++++++++ lib/Runtime/Library/RegexHelper.cpp | 18 +++++++++++++++--- test/Regex/replace.baseline | 4 ++++ test/Regex/replace.js | 7 +++++++ 5 files changed, 40 insertions(+), 3 deletions(-) diff --git a/lib/Parser/RegexParser.cpp b/lib/Parser/RegexParser.cpp index 132bd7ccd2c..e59dcd58c66 100644 --- a/lib/Parser/RegexParser.cpp +++ b/lib/Parser/RegexParser.cpp @@ -2961,6 +2961,8 @@ namespace UnifiedRegex this->scriptContext->ProfileEnd(Js::RegexCompilePhase); #endif + AssertOrFailFast(0 < pattern->NumGroups() && pattern->NumGroups() <= MAX_NUM_GROUPS); + return pattern; } @@ -2993,6 +2995,8 @@ namespace UnifiedRegex program->numGroups = nextGroupId; + AssertOrFailFast(0 < program->numGroups && program->numGroups <= MAX_NUM_GROUPS); + // Remaining to set during compilation: litbuf, litbufLen, numLoops, insts, instsLen, entryPointLabel } diff --git a/lib/Parser/RegexParser.h b/lib/Parser/RegexParser.h index 7426f1a48fc..322ddf8e66c 100644 --- a/lib/Parser/RegexParser.h +++ b/lib/Parser/RegexParser.h @@ -77,6 +77,16 @@ namespace UnifiedRegex const EncodedChar* next; bool inBody; + // Maximum number of capturing groups allowed, including the entire regexp, which is always + // considered a capturing group. Using INT16_MAX allows us to pass one value for each + // group, plus a few additional values, to a JavaScript function without overflowing the + // number of arguments. This is important, for example, in the implementation of + // String.prototype.replace, where the second argument is a function. + // + // This should really be an unsigned, but we compare it against numGroups, so make it + // an int for now to avoid a bunch of compiler warnings until we can go back and clean this up. + static const int MAX_NUM_GROUPS = INT16_MAX; + int numGroups; // determined in first parse int nextGroupId; // Buffer accumulating all literals. diff --git a/lib/Runtime/Library/RegexHelper.cpp b/lib/Runtime/Library/RegexHelper.cpp index d8fcc12e817..669d474086a 100644 --- a/lib/Runtime/Library/RegexHelper.cpp +++ b/lib/Runtime/Library/RegexHelper.cpp @@ -724,7 +724,14 @@ namespace Js char16 currentChar = replaceStr[substitutionOffset + 1]; if (currentChar >= _u('0') && currentChar <= _u('9')) { + // We've found a substitution ref, like $32. In accordance with the standard (sec-getsubstitution), + // we recognize at most two decimal digits after the dollar sign. + + // This should be unsigned, but this would cause lots of compiler warnings unless we also make + // numGroups unsigned, because of a comparison below. int captureIndex = (int)(currentChar - _u('0')); + Assert(0 <= captureIndex && captureIndex <= 9); // numeric value of single decimal digit + offset = substitutionOffset + 2; if (offset < replaceLength) @@ -732,7 +739,9 @@ namespace Js currentChar = replaceStr[substitutionOffset + 2]; if (currentChar >= _u('0') && currentChar <= _u('9')) { + // Should also be unsigned; see captureIndex above. int tempCaptureIndex = (10 * captureIndex) + (int)(currentChar - _u('0')); + Assert(0 <= tempCaptureIndex && tempCaptureIndex < 100); // numeric value of 2-digit positive decimal number if (tempCaptureIndex < numGroups) { captureIndex = tempCaptureIndex; @@ -741,6 +750,7 @@ namespace Js } } + Assert(0 <= captureIndex && captureIndex < 100); // as above, value of 2-digit positive decimal number if (captureIndex < numGroups && (captureIndex != 0)) { Var group = getGroup(captureIndex, nonMatchValue); @@ -1204,10 +1214,13 @@ namespace Js JavascriptString* newString = nullptr; const char16* inputStr = input->GetString(); CharCount inputLength = input->GetLength(); - const int numGroups = pattern->NumGroups(); + const int rawNumGroups = pattern->NumGroups(); Var nonMatchValue = NonMatchValue(scriptContext, false); UnifiedRegex::GroupInfo lastMatch; // initially undefined + AssertOrFailFast(0 < rawNumGroups && rawNumGroups <= INT16_MAX); + const uint16 numGroups = uint16(rawNumGroups); + #if ENABLE_REGEX_CONFIG_OPTIONS RegexHelperTrace(scriptContext, UnifiedRegex::RegexStats::Replace, regularExpression, input, scriptContext->GetLibrary()->CreateStringFromCppLiteral(_u(""))); #endif @@ -1265,7 +1278,6 @@ namespace Js lastSuccessfulMatch = lastActualMatch; for (int groupId = 0; groupId < numGroups; groupId++) replaceArgs[groupId + 1] = GetGroup(scriptContext, pattern, input, nonMatchValue, groupId); -#pragma prefast(suppress:6386, "The write index numGroups + 1 is in the bound") replaceArgs[numGroups + 1] = JavascriptNumber::ToVar(lastActualMatch.offset, scriptContext); // The called function must see the global state updated by the current match @@ -1278,7 +1290,7 @@ namespace Js ThreadContext* threadContext = scriptContext->GetThreadContext(); Var replaceVar = threadContext->ExecuteImplicitCall(replacefn, ImplicitCall_Accessor, [=]()->Js::Var { - return replacefn->CallFunction(Arguments(CallInfo((ushort)(numGroups + 3)), replaceArgs)); + return replacefn->CallFunction(Arguments(CallInfo(UInt16Math::Add(numGroups, 3)), replaceArgs)); }); JavascriptString* replace = JavascriptConversion::ToString(replaceVar, scriptContext); concatenated.Append(input, offset, lastActualMatch.offset - offset); diff --git a/test/Regex/replace.baseline b/test/Regex/replace.baseline index ac5062164f2..446637400e8 100644 --- a/test/Regex/replace.baseline +++ b/test/Regex/replace.baseline @@ -38,6 +38,10 @@ replace(/b/ /*lastIndex=0*/ , "abc", "$12345678"); "a$12345678c" r.lastIndex=0 RegExp.${_,1,...,9}=["abc","","","","","","","","",""] +replace(/([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])([ab])/ /*lastIndex=0*/ , "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "$100"); +"a0" +r.lastIndex=0 +RegExp.${_,1,...,9}=["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","a","a","a","a","a","a","a","a","a"] replace(/b/ /*lastIndex=0*/ , "abc", []); "ac" diff --git a/test/Regex/replace.js b/test/Regex/replace.js index 19292dfad29..77539ec5163 100644 --- a/test/Regex/replace.js +++ b/test/Regex/replace.js @@ -229,6 +229,13 @@ replace(/b/, "abc", "$`$&$'"); replace(/b/, "abc", "1234567$"); replace(/b/, "abc", "$12345678"); +// Ensure that we only accept 2 digits after a $ in the replacement +// string, as per standards (sec-getsubstitution); this avoids an +// integer overflow in implementation of 'replace'. Correct behavior +// is 10th char of input ('a') followed by '0'; incorrect behavior is +// 100th char of input ('b'). +replace(new RegExp('([ab])'.repeat(101)), ("a".repeat(50) + "b".repeat(51)), "$100"); + echo(""); replace(/b/, "abc", function () { return ""; }); From a7f6761d6090a9ec9758f097bedf817be946d4bf Mon Sep 17 00:00:00 2001 From: Michael Holman Date: Fri, 8 Dec 2017 14:59:54 -0800 Subject: [PATCH 15/16] Update ChakraCore version --- Build/NuGet/.pack-version | 2 +- lib/Common/ChakraCoreVersion.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Build/NuGet/.pack-version b/Build/NuGet/.pack-version index 10c088013f8..6a126f402d5 100644 --- a/Build/NuGet/.pack-version +++ b/Build/NuGet/.pack-version @@ -1 +1 @@ -1.7.4 +1.7.5 diff --git a/lib/Common/ChakraCoreVersion.h b/lib/Common/ChakraCoreVersion.h index c53910414ce..48a81b2cec4 100644 --- a/lib/Common/ChakraCoreVersion.h +++ b/lib/Common/ChakraCoreVersion.h @@ -17,7 +17,7 @@ // ChakraCore version number definitions (used in ChakraCore binary metadata) #define CHAKRA_CORE_MAJOR_VERSION 1 #define CHAKRA_CORE_MINOR_VERSION 7 -#define CHAKRA_CORE_PATCH_VERSION 4 +#define CHAKRA_CORE_PATCH_VERSION 5 #define CHAKRA_CORE_VERSION_RELEASE_QFE 0 // Redundant with PATCH_VERSION. Keep this value set to 0. // ------------- From 6e6301cfbab093e122a3c292873f793a684e44fb Mon Sep 17 00:00:00 2001 From: Michael Holman Date: Tue, 12 Dec 2017 10:25:14 -0800 Subject: [PATCH 16/16] fix prefast warnings --- lib/Backend/Inline.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Backend/Inline.cpp b/lib/Backend/Inline.cpp index b5b6431ec19..03489fc760d 100644 --- a/lib/Backend/Inline.cpp +++ b/lib/Backend/Inline.cpp @@ -2481,7 +2481,7 @@ IR::Instr * Inline::InlineApplyBuiltInTargetWithArray(IR::Instr * callInstr, con { return callInstr; } - + AnalysisAssert(applyTargetLdInstr != nullptr); // Fixed function/function object checks for target built-in callInstr->ReplaceSrc1(applyTargetLdInstr->GetDst()); EmitFixedMethodOrFunctionObjectChecksForBuiltIns(callInstr, callInstr, builtInInfo, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/); @@ -2664,6 +2664,7 @@ bool Inline::InlineApplyScriptTarget(IR::Instr *callInstr, const FunctionJITTime { return false; } + AnalysisAssert(applyTargetLdInstr != nullptr); if(applyTargetLdInstr->m_opcode != Js::OpCode::LdFldForCallApplyTarget || ((applyTargetLdInstr->AsProfiledInstr()->u.FldInfo().flags & Js::FldInfo_FromAccessor) != 0))