From a9ab1aae31078e80593b9227db11d316c2239ef3 Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Fri, 12 Apr 2019 16:15:50 -0700 Subject: [PATCH 01/13] [CVE-2019-0922] Type confusion in Edge - Individual --- lib/Backend/GlobOptArrays.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/Backend/GlobOptArrays.cpp b/lib/Backend/GlobOptArrays.cpp index 135fcf5c108..9ac28c59c12 100644 --- a/lib/Backend/GlobOptArrays.cpp +++ b/lib/Backend/GlobOptArrays.cpp @@ -1736,7 +1736,14 @@ void GlobOpt::ArraySrcOpt::Optimize() { if (newBaseValueType != baseValueType) { - UpdateValue(nullptr, nullptr, nullptr); + if (globOpt->IsSafeToTransferInPrePass(baseOpnd, baseValue)) + { + UpdateValue(nullptr, nullptr, nullptr); + } + else if (globOpt->IsOperationThatLikelyKillsJsArraysWithNoMissingValues(instr) && baseValueInfo->HasNoMissingValues()) + { + globOpt->ChangeValueType(nullptr, baseValue, baseValueInfo->Type().SetHasNoMissingValues(false), true); + } } // For javascript arrays and objects with javascript arrays: From 6615113a09c0618ecc10e5680ffb978bf665641f Mon Sep 17 00:00:00 2001 From: Paul Leathers Date: Tue, 16 Apr 2019 15:24:32 -0700 Subject: [PATCH 02/13] [CVE-2019-0924] --- lib/Backend/BackwardPass.cpp | 3 +-- lib/Backend/GlobOptBailOut.cpp | 8 ++++++ lib/Backend/Inline.cpp | 2 ++ lib/Backend/Lower.cpp | 46 +++++++++++++++++++++++----------- lib/Runtime/ByteCode/OpCodes.h | 4 +-- 5 files changed, 45 insertions(+), 18 deletions(-) diff --git a/lib/Backend/BackwardPass.cpp b/lib/Backend/BackwardPass.cpp index d94e79fab7f..be4e42d4be4 100644 --- a/lib/Backend/BackwardPass.cpp +++ b/lib/Backend/BackwardPass.cpp @@ -4677,10 +4677,9 @@ BackwardPass::ProcessNewScObject(IR::Instr* instr) return; } - if (instr->HasBailOutInfo()) + if (instr->HasBailOutInfo() && (instr->GetBailOutKind() & ~IR::BailOutKindBits) == IR::BailOutFailedCtorGuardCheck) { Assert(instr->IsProfiledInstr()); - Assert(instr->GetBailOutKind() == IR::BailOutFailedCtorGuardCheck); Assert(instr->GetDst()->IsRegOpnd()); BasicBlock * block = this->currentBlock; diff --git a/lib/Backend/GlobOptBailOut.cpp b/lib/Backend/GlobOptBailOut.cpp index c27a7af7863..f168545b3b2 100644 --- a/lib/Backend/GlobOptBailOut.cpp +++ b/lib/Backend/GlobOptBailOut.cpp @@ -1337,6 +1337,14 @@ GlobOpt::MayNeedBailOnImplicitCall(IR::Instr const * instr, Value const * src1Va ); } + case Js::OpCode::NewScObjectNoCtor: + if (instr->HasBailOutInfo() && (instr->GetBailOutKind() & ~IR::BailOutKindBits) == IR::BailOutFailedCtorGuardCheck) + { + // No helper call with this bailout. + return false; + } + break; + default: break; } diff --git a/lib/Backend/Inline.cpp b/lib/Backend/Inline.cpp index b185ad83aea..5703333c78c 100644 --- a/lib/Backend/Inline.cpp +++ b/lib/Backend/Inline.cpp @@ -4204,6 +4204,8 @@ Inline::SplitConstructorCallCommon( { createObjInstr->SetByteCodeOffset(newObjInstr); createObjInstr->GetSrc1()->SetIsJITOptimizedReg(true); + // We're splitting a single byte code, so the interpreter has to resume from the beginning if we bail out. + createObjInstr->forcePreOpBailOutIfNeeded = true; newObjInstr->InsertBefore(createObjInstr); createObjDst->SetValueType(ValueType::GetObject(ObjectType::UninitializedObject)); diff --git a/lib/Backend/Lower.cpp b/lib/Backend/Lower.cpp index a5d19c4021e..52b5be80238 100644 --- a/lib/Backend/Lower.cpp +++ b/lib/Backend/Lower.cpp @@ -4601,18 +4601,40 @@ Lowerer::LowerNewScObject(IR::Instr *newObjInstr, bool callCtor, bool hasArgs, b { Assert(!newObjDst->CanStoreTemp()); // createObjDst = NewScObject...(ctorOpnd) - newScHelper = !callCtor ? - (isBaseClassConstructorNewScObject ? - (hasArgs ? IR::HelperNewScObjectNoCtorFull : IR::HelperNewScObjectNoArgNoCtorFull) : - (hasArgs ? IR::HelperNewScObjectNoCtor : IR::HelperNewScObjectNoArgNoCtor)) : - (hasArgs || usedFixedCtorCache ? IR::HelperNewScObjectNoCtor : IR::HelperNewScObjectNoArg); LoadScriptContext(newObjInstr); - m_lowererMD.LoadHelperArgument(newObjInstr, newObjInstr->GetSrc1()); - newScObjCall = IR::Instr::New(Js::OpCode::Call, createObjDst, IR::HelperCallOpnd::New(newScHelper, func), func); - newObjInstr->InsertBefore(newScObjCall); - m_lowererMD.LowerCall(newScObjCall, 0); + if (callCtor) + { + newScHelper = (hasArgs || usedFixedCtorCache ? IR::HelperNewScObjectNoCtor : IR::HelperNewScObjectNoArg); + + m_lowererMD.LoadHelperArgument(newObjInstr, newObjInstr->GetSrc1()); + + newScObjCall = IR::Instr::New(Js::OpCode::Call, createObjDst, IR::HelperCallOpnd::New(newScHelper, func), func); + newObjInstr->InsertBefore(newScObjCall); + m_lowererMD.LowerCall(newScObjCall, 0); + } + else + { + newScHelper = + (isBaseClassConstructorNewScObject ? + (hasArgs ? IR::HelperNewScObjectNoCtorFull : IR::HelperNewScObjectNoArgNoCtorFull) : + (hasArgs ? IR::HelperNewScObjectNoCtor : IR::HelperNewScObjectNoArgNoCtor)); + + // Branch around the helper call to execute the inlined ctor. + Assert(callCtorLabel != nullptr); + newObjInstr->InsertAfter(callCtorLabel); + + // Change the NewScObject* to a helper call on the spot. This generates implicit call bailout for us if we need one. + m_lowererMD.LoadHelperArgument(newObjInstr, newObjInstr->UnlinkSrc1()); + m_lowererMD.ChangeToHelperCall(newObjInstr, newScHelper); + + // Then we're done. + Assert(createObjDst == newObjDst); + + // Return the first instruction above the region we've just lowered. + return RemoveLoweredRegionStartMarker(startMarkerInstr); + } } } @@ -4857,9 +4879,6 @@ bool Lowerer::TryLowerNewScObjectWithFixedCtorCache(IR::Instr* newObjInstr, IR:: skipNewScObj = false; returnNewScObj = false; - AssertMsg(!PHASE_OFF(Js::ObjTypeSpecNewObjPhase, this->m_func) || !newObjInstr->HasBailOutInfo(), - "Why do we have bailout on NewScObject when ObjTypeSpecNewObj is off?"); - if (PHASE_OFF(Js::FixedNewObjPhase, newObjInstr->m_func) && PHASE_OFF(Js::ObjTypeSpecNewObjPhase, this->m_func)) { return false; @@ -4867,11 +4886,10 @@ bool Lowerer::TryLowerNewScObjectWithFixedCtorCache(IR::Instr* newObjInstr, IR:: JITTimeConstructorCache * ctorCache; - if (newObjInstr->HasBailOutInfo()) + if (newObjInstr->HasBailOutInfo() && (newObjInstr->GetBailOutKind() & ~IR::BailOutKindBits) == IR::BailOutFailedCtorGuardCheck) { Assert(newObjInstr->IsNewScObjectInstr()); Assert(newObjInstr->IsProfiledInstr()); - Assert(newObjInstr->GetBailOutKind() == IR::BailOutFailedCtorGuardCheck); emitBailOut = true; diff --git a/lib/Runtime/ByteCode/OpCodes.h b/lib/Runtime/ByteCode/OpCodes.h index cb9d1ed9552..d75019d0d04 100755 --- a/lib/Runtime/ByteCode/OpCodes.h +++ b/lib/Runtime/ByteCode/OpCodes.h @@ -577,8 +577,8 @@ MACRO_EXTEND_WMS_AND_PROFILED(NewScObjectSpread, CallIExtended, OpSideEffect|O MACRO_WMS_PROFILED2( NewScObjArray, CallI, OpSideEffect|OpUseAllFields|OpCallInstr) // Create new ScriptObject instance MACRO_WMS_PROFILED2( NewScObjArraySpread, CallIExtended, OpSideEffect|OpUseAllFields|OpCallInstr) // Create new ScriptObject instance MACRO( NewScObject_A, Auxiliary, OpSideEffect|OpUseAllFields) // Create new ScriptObject instance passing only constants -MACRO_WMS( NewScObjectNoCtorFull, Reg2, OpTempObjectCanStoreTemp) // Create new object that will be used for the 'this' binding in a base class constructor -MACRO_BACKEND_ONLY( NewScObjectNoCtor, Empty, OpTempObjectCanStoreTemp) // Create new object that will be passed into a constructor +MACRO_WMS( NewScObjectNoCtorFull, Reg2, OpTempObjectCanStoreTemp|OpHasImplicitCall) // Create new object that will be used for the 'this' binding in a base class constructor +MACRO_BACKEND_ONLY( NewScObjectNoCtor, Empty, OpTempObjectCanStoreTemp|OpHasImplicitCall) // Create new object that will be passed into a constructor MACRO_BACKEND_ONLY( GetNewScObject, Empty, OpTempObjectTransfer) // Determine which object to finally use as the result of NewScObject (object passed into constructor as 'this', or object returned by constructor) MACRO_BACKEND_ONLY( UpdateNewScObjectCache, Empty, None) // Update the cache used for NewScObject MACRO_WMS( NewScObjectSimple, Reg1, OpTempObjectCanStoreTemp) From 32ca10f3955f2a3ca56c6671c721b1264eca06b8 Mon Sep 17 00:00:00 2001 From: Paul Leathers Date: Tue, 16 Apr 2019 17:19:00 -0700 Subject: [PATCH 03/13] [CVE-2019-0925] --- lib/Backend/GlobOpt.cpp | 1 + lib/Backend/GlobOptFields.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/Backend/GlobOpt.cpp b/lib/Backend/GlobOpt.cpp index 76af5504773..661c72dcb0b 100644 --- a/lib/Backend/GlobOpt.cpp +++ b/lib/Backend/GlobOpt.cpp @@ -13462,6 +13462,7 @@ GlobOpt::CheckJsArrayKills(IR::Instr *const instr) break; case Js::OpCode::NewScObjectNoCtor: + case Js::OpCode::NewScObjectNoCtorFull: if(doNativeArrayTypeSpec) { // Class/object construction can make something a prototype diff --git a/lib/Backend/GlobOptFields.cpp b/lib/Backend/GlobOptFields.cpp index dbddb245921..c355563523d 100644 --- a/lib/Backend/GlobOptFields.cpp +++ b/lib/Backend/GlobOptFields.cpp @@ -492,6 +492,7 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse *bv, bo case Js::OpCode::InitClass: case Js::OpCode::InitProto: case Js::OpCode::NewScObjectNoCtor: + case Js::OpCode::NewScObjectNoCtorFull: if (inGlobOpt) { // Opcodes that make an object into a prototype may break object-header-inlining and final type opt. From a2deba5e1850782014a2a34678464b251e448337 Mon Sep 17 00:00:00 2001 From: Michael Holman Date: Wed, 17 Apr 2019 09:51:46 -0700 Subject: [PATCH 04/13] [CVE-2019-0911] --- lib/Runtime/Library/ES5Array.cpp | 4 ++++ lib/Runtime/Library/JavascriptArray.cpp | 3 +++ 2 files changed, 7 insertions(+) diff --git a/lib/Runtime/Library/ES5Array.cpp b/lib/Runtime/Library/ES5Array.cpp index 67351df1b84..fa978be3af7 100644 --- a/lib/Runtime/Library/ES5Array.cpp +++ b/lib/Runtime/Library/ES5Array.cpp @@ -148,6 +148,10 @@ namespace Js { JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthAssignIncorrect); } + + // Conversion can change the type (e.g. from String), invalidating assumptions made by the JIT + scriptContext->GetThreadContext()->AddImplicitCallFlags(ImplicitCall_Accessor); + return newLen; } } diff --git a/lib/Runtime/Library/JavascriptArray.cpp b/lib/Runtime/Library/JavascriptArray.cpp index a762a4d9cbb..e05f2313551 100644 --- a/lib/Runtime/Library/JavascriptArray.cpp +++ b/lib/Runtime/Library/JavascriptArray.cpp @@ -2930,6 +2930,9 @@ using namespace Js; double dblValue = JavascriptConversion::ToNumber(newLength, scriptContext); if (dblValue == uintValue) { + // Conversion can change the type (e.g. from String), invalidating assumptions made by the JIT + scriptContext->GetThreadContext()->AddImplicitCallFlags(ImplicitCall_Accessor); + this->SetLength(uintValue); } else From 936a5af1c07e0fdec9aab85c05339dabe4aaeeb1 Mon Sep 17 00:00:00 2001 From: Michael Holman Date: Wed, 17 Apr 2019 10:18:03 -0700 Subject: [PATCH 05/13] [CVE-2019-0912] --- lib/Backend/GlobOptFields.cpp | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/lib/Backend/GlobOptFields.cpp b/lib/Backend/GlobOptFields.cpp index c355563523d..2eff8b7c61f 100644 --- a/lib/Backend/GlobOptFields.cpp +++ b/lib/Backend/GlobOptFields.cpp @@ -415,11 +415,19 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse *bv, bo case Js::OpCode::InlineArrayPush: case Js::OpCode::InlineArrayPop: - KillLiveFields(this->lengthEquivBv, bv); - if (inGlobOpt) + if(instr->m_func->GetThisOrParentInlinerHasArguments()) { - // Deleting an item, or pushing a property to a non-array, may change object layout - KillAllObjectTypes(bv); + this->KillAllFields(bv); + this->SetAnyPropertyMayBeWrittenTo(); + } + else + { + KillLiveFields(this->lengthEquivBv, bv); + if (inGlobOpt) + { + // Deleting an item, or pushing a property to a non-array, may change object layout + KillAllObjectTypes(bv); + } } break; @@ -444,14 +452,23 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse *bv, bo // Kill length field for built-ins that can update it. if (nullptr != this->lengthEquivBv) { - KillLiveFields(this->lengthEquivBv, bv); + // If has arguments, all fields are killed in fall through + if (!instr->m_func->GetThisOrParentInlinerHasArguments()) + { + KillLiveFields(this->lengthEquivBv, bv); + } } // fall through case IR::JnHelperMethod::HelperArray_Reverse: - // Deleting an item may change object layout - if (inGlobOpt) + if (instr->m_func->GetThisOrParentInlinerHasArguments()) + { + this->KillAllFields(bv); + this->SetAnyPropertyMayBeWrittenTo(); + } + else if (inGlobOpt) { + // Deleting an item may change object layout KillAllObjectTypes(bv); } break; From 87ac2b5a751710ee288fdda3fd4d9818e22387a1 Mon Sep 17 00:00:00 2001 From: Michael Holman Date: Wed, 17 Apr 2019 10:22:17 -0700 Subject: [PATCH 06/13] [CVE-2019-0927] --- lib/Backend/GlobOptFields.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Backend/GlobOptFields.cpp b/lib/Backend/GlobOptFields.cpp index 2eff8b7c61f..05594e71269 100644 --- a/lib/Backend/GlobOptFields.cpp +++ b/lib/Backend/GlobOptFields.cpp @@ -394,6 +394,7 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse *bv, bo case Js::OpCode::StRootFldStrict: case Js::OpCode::StSlot: case Js::OpCode::StSlotChkUndecl: + case Js::OpCode::StSuperFld: Assert(dstOpnd != nullptr); sym = dstOpnd->AsSymOpnd()->m_sym; if (inGlobOpt) From 1a550c67b33b27675c0553152cabd09e4ffe3abf Mon Sep 17 00:00:00 2001 From: Michael Holman Date: Wed, 17 Apr 2019 10:27:02 -0700 Subject: [PATCH 07/13] [CVE-2019-0933] --- lib/Backend/GlobOpt.cpp | 1 + lib/Backend/GlobOptExpr.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/Backend/GlobOpt.cpp b/lib/Backend/GlobOpt.cpp index 661c72dcb0b..7928a50f46d 100644 --- a/lib/Backend/GlobOpt.cpp +++ b/lib/Backend/GlobOpt.cpp @@ -13251,6 +13251,7 @@ GlobOpt::CheckJsArrayKills(IR::Instr *const instr) case Js::OpCode::StFld: case Js::OpCode::StFldStrict: + case Js::OpCode::StSuperFld: { Assert(instr->GetDst()); diff --git a/lib/Backend/GlobOptExpr.cpp b/lib/Backend/GlobOptExpr.cpp index cc1d4ff0820..13a9bfb0f49 100644 --- a/lib/Backend/GlobOptExpr.cpp +++ b/lib/Backend/GlobOptExpr.cpp @@ -822,6 +822,7 @@ GlobOpt::ProcessArrayValueKills(IR::Instr *instr) case Js::OpCode::StRootFld: case Js::OpCode::StFldStrict: case Js::OpCode::StRootFldStrict: + case Js::OpCode::StSuperFld: case Js::OpCode::StSlot: case Js::OpCode::StSlotChkUndecl: case Js::OpCode::DeleteFld: From d85b5025b047f10784c53c6c1dd771775d417f5f Mon Sep 17 00:00:00 2001 From: Michael Holman Date: Wed, 17 Apr 2019 10:34:39 -0700 Subject: [PATCH 08/13] [CVE-2019-0913][CVE-2019-0914][CVE-2019-0915][CVE-2019-0916] --- lib/Backend/GlobOpt.cpp | 5 +++++ lib/Backend/GlobOptExpr.cpp | 7 +++++++ lib/Backend/GlobOptFields.cpp | 25 +++++++++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/lib/Backend/GlobOpt.cpp b/lib/Backend/GlobOpt.cpp index 7928a50f46d..261d6b9a5ca 100644 --- a/lib/Backend/GlobOpt.cpp +++ b/lib/Backend/GlobOpt.cpp @@ -13199,6 +13199,7 @@ GlobOpt::CheckJsArrayKills(IR::Instr *const instr) const bool useValueTypes = !IsLoopPrePass(); // Source value types are not guaranteed to be correct in a loop prepass switch(instr->m_opcode) { + case Js::OpCode::StElemC: case Js::OpCode::StElemI_A: case Js::OpCode::StElemI_A_Strict: { @@ -13249,6 +13250,10 @@ GlobOpt::CheckJsArrayKills(IR::Instr *const instr) } break; + case Js::OpCode::ConsoleScopedStFld: + case Js::OpCode::ConsoleScopedStFldStrict: + case Js::OpCode::ScopedStFld: + case Js::OpCode::ScopedStFldStrict: case Js::OpCode::StFld: case Js::OpCode::StFldStrict: case Js::OpCode::StSuperFld: diff --git a/lib/Backend/GlobOptExpr.cpp b/lib/Backend/GlobOptExpr.cpp index 13a9bfb0f49..d95b63779ce 100644 --- a/lib/Backend/GlobOptExpr.cpp +++ b/lib/Backend/GlobOptExpr.cpp @@ -814,10 +814,15 @@ GlobOpt::ProcessArrayValueKills(IR::Instr *instr) { switch (instr->m_opcode) { + case Js::OpCode::StElemC: case Js::OpCode::StElemI_A: case Js::OpCode::StElemI_A_Strict: case Js::OpCode::DeleteElemI_A: case Js::OpCode::DeleteElemIStrict_A: + case Js::OpCode::ConsoleScopedStFld: + case Js::OpCode::ConsoleScopedStFldStrict: + case Js::OpCode::ScopedStFld: + case Js::OpCode::ScopedStFldStrict: case Js::OpCode::StFld: case Js::OpCode::StRootFld: case Js::OpCode::StFldStrict: @@ -829,6 +834,8 @@ GlobOpt::ProcessArrayValueKills(IR::Instr *instr) case Js::OpCode::DeleteRootFld: case Js::OpCode::DeleteFldStrict: case Js::OpCode::DeleteRootFldStrict: + case Js::OpCode::ScopedDeleteFld: + case Js::OpCode::ScopedDeleteFldStrict: case Js::OpCode::StArrViewElem: // These array helpers may change A.length (and A[i] could be A.length)... case Js::OpCode::InlineArrayPush: diff --git a/lib/Backend/GlobOptFields.cpp b/lib/Backend/GlobOptFields.cpp index 05594e71269..5a0e3fa8337 100644 --- a/lib/Backend/GlobOptFields.cpp +++ b/lib/Backend/GlobOptFields.cpp @@ -335,6 +335,7 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse *bv, bo IR::JnHelperMethod fnHelper; switch(instr->m_opcode) { + case Js::OpCode::StElemC: case Js::OpCode::StElemI_A: case Js::OpCode::StElemI_A_Strict: Assert(dstOpnd != nullptr); @@ -366,6 +367,8 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse *bv, bo case Js::OpCode::DeleteRootFld: case Js::OpCode::DeleteFldStrict: case Js::OpCode::DeleteRootFldStrict: + case Js::OpCode::ScopedDeleteFld: + case Js::OpCode::ScopedDeleteFldStrict: sym = instr->GetSrc1()->AsSymOpnd()->m_sym; KillLiveFields(sym->AsPropertySym(), bv); if (inGlobOpt) @@ -387,7 +390,29 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse *bv, bo this->KillAllObjectTypes(bv); } break; + + case Js::OpCode::ConsoleScopedStFld: + case Js::OpCode::ConsoleScopedStFldStrict: + case Js::OpCode::ScopedStFld: + case Js::OpCode::ScopedStFldStrict: + // This is already taken care of for FastFld opcodes + + if (inGlobOpt) + { + KillObjectHeaderInlinedTypeSyms(this->currentBlock, false); + } + + // fall through + case Js::OpCode::InitFld: + case Js::OpCode::InitConstFld: + case Js::OpCode::InitLetFld: + case Js::OpCode::InitRootFld: + case Js::OpCode::InitRootConstFld: + case Js::OpCode::InitRootLetFld: +#if !FLOATVAR + case Js::OpCode::StSlotBoxTemp: +#endif case Js::OpCode::StFld: case Js::OpCode::StRootFld: case Js::OpCode::StFldStrict: From 972584709518380a4a3c2410b5fa151f6f0239b1 Mon Sep 17 00:00:00 2001 From: Akrosh Gandhi Date: Wed, 17 Apr 2019 13:42:43 -0700 Subject: [PATCH 09/13] [CVE-2019-0923] Scripting Engine Memory Corruption - Individual After reparsing the coroutines (generator/async functions) the register which holds yield data has changed. This is due to scoping structure for debugger. In order to fix that allow the same scoping numbering for coroutine as well. Also added a failfast to see if the yield register information is the same before and after reparsing. --- lib/Runtime/ByteCode/ByteCodeEmitter.cpp | 1 + lib/Runtime/ByteCode/ByteCodeGenerator.cpp | 12 ++++---- lib/Runtime/Debug/DebugContext.cpp | 34 +++++++++++++++++++++- 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/lib/Runtime/ByteCode/ByteCodeEmitter.cpp b/lib/Runtime/ByteCode/ByteCodeEmitter.cpp index 5820ab38a8d..5c07982b92b 100644 --- a/lib/Runtime/ByteCode/ByteCodeEmitter.cpp +++ b/lib/Runtime/ByteCode/ByteCodeEmitter.cpp @@ -3590,6 +3590,7 @@ void ByteCodeGenerator::StartEmitFunction(ParseNodeFnc *pnodeFnc) #if ENABLE_TTD && !funcInfo->GetParsedFunctionBody()->GetScriptContext()->GetThreadContext()->IsRuntimeInTTDMode() #endif + && !funcInfo->byteCodeFunction->IsCoroutine() ); if (funcInfo->GetHasCachedScope()) diff --git a/lib/Runtime/ByteCode/ByteCodeGenerator.cpp b/lib/Runtime/ByteCode/ByteCodeGenerator.cpp index 77c52fc8361..1d6c3f1d17e 100644 --- a/lib/Runtime/ByteCode/ByteCodeGenerator.cpp +++ b/lib/Runtime/ByteCode/ByteCodeGenerator.cpp @@ -119,10 +119,10 @@ void EndVisitBlock(ParseNodeBlock *pnode, ByteCodeGenerator *byteCodeGenerator) Scope *scope = pnode->scope; FuncInfo *func = scope->GetFunc(); - if (!byteCodeGenerator->IsInDebugMode() && - scope->HasInnerScopeIndex()) + if (!(byteCodeGenerator->IsInDebugMode() || func->byteCodeFunction->IsCoroutine()) + && scope->HasInnerScopeIndex()) { - // In debug mode, don't release the current index, as we're giving each scope a unique index, regardless + // In debug mode (or for the generator/async function), don't release the current index, as we're giving each scope a unique index, regardless // of nesting. Assert(scope->GetInnerScopeIndex() == func->CurrentInnerScopeIndex()); func->ReleaseInnerScopeIndex(); @@ -155,12 +155,12 @@ void BeginVisitCatch(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator) void EndVisitCatch(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator) { Scope *scope = pnode->AsParseNodeCatch()->scope; + FuncInfo *func = scope->GetFunc(); - if (scope->HasInnerScopeIndex() && !byteCodeGenerator->IsInDebugMode()) + if (scope->HasInnerScopeIndex() && !(byteCodeGenerator->IsInDebugMode() || func->byteCodeFunction->IsCoroutine())) { - // In debug mode, don't release the current index, as we're giving each scope a unique index, + // In debug mode (or for the generator/async function), don't release the current index, as we're giving each scope a unique index, // regardless of nesting. - FuncInfo *func = scope->GetFunc(); Assert(scope->GetInnerScopeIndex() == func->CurrentInnerScopeIndex()); func->ReleaseInnerScopeIndex(); diff --git a/lib/Runtime/Debug/DebugContext.cpp b/lib/Runtime/Debug/DebugContext.cpp index 7f9eb30c8a0..35379ddf6de 100644 --- a/lib/Runtime/Debug/DebugContext.cpp +++ b/lib/Runtime/Debug/DebugContext.cpp @@ -193,6 +193,11 @@ namespace Js Js::TempArenaAllocatorObject *tempAllocator = nullptr; JsUtil::List* pFunctionsToRegister = nullptr; JsUtil::List* utf8SourceInfoList = nullptr; + typedef JsUtil::BaseDictionary FunctionStartToYieldRegister; + + // This container ensures that for Generator/Async functions the yield register is same between non-debug to debug parse. + // Each entry represent a function's start position (each function will have unique start position in a file) and that function yield register + FunctionStartToYieldRegister *yieldFunctions = nullptr; HRESULT hr = S_OK; ThreadContext* threadContext = this->scriptContext->GetThreadContext(); @@ -201,6 +206,7 @@ namespace Js tempAllocator = threadContext->GetTemporaryAllocator(_u("debuggerAlloc")); utf8SourceInfoList = JsUtil::List::New(this->scriptContext->GetRecycler()); + yieldFunctions = Anew(tempAllocator->GetAllocator(), FunctionStartToYieldRegister, tempAllocator->GetAllocator()); this->MapUTF8SourceInfoUntil([&](Js::Utf8SourceInfo * sourceInfo) -> bool { @@ -276,6 +282,23 @@ namespace Js this->hostDebugContext->SetThreadDescription(sourceInfo->GetSourceContextInfo()->url); // the HRESULT is omitted. } + if (shouldReparseFunctions) + { + yieldFunctions->Clear(); + BEGIN_TRANSLATE_OOM_TO_HRESULT_NESTED + { + sourceInfo->MapFunction([&](Js::FunctionBody *const pFuncBody) + { + if (pFuncBody->IsCoroutine() && pFuncBody->GetYieldRegister() != Js::Constants::NoRegister) + { + yieldFunctions->Add(pFuncBody->StartInDocument(), pFuncBody->GetYieldRegister()); + } + }); + } + END_TRANSLATE_OOM_TO_HRESULT(hr); + DEBUGGER_ATTACHDETACH_FATAL_ERROR_IF_FAILED(hr); + } + bool fHasDoneSourceRundown = false; for (int i = 0; i < pFunctionsToRegister->Count(); i++) { @@ -326,8 +349,17 @@ namespace Js if (shouldReparseFunctions) { - sourceInfo->MapFunction([](Js::FunctionBody *const pFuncBody) + sourceInfo->MapFunction([&](Js::FunctionBody *const pFuncBody) { + if (pFuncBody->IsCoroutine()) + { + RegSlot oldYieldRegister = Constants::NoRegister; + if (yieldFunctions->TryGetValue(pFuncBody->StartInDocument(), &oldYieldRegister)) + { + AssertOrFailFast(pFuncBody->GetYieldRegister() == oldYieldRegister); + } + } + if (pFuncBody->IsFunctionParsed()) { pFuncBody->ReinitializeExecutionModeAndLimits(); From b5f8fad1b00087bd0a24cc173c2dfedc4f8aee33 Mon Sep 17 00:00:00 2001 From: Paul Leathers Date: Wed, 17 Apr 2019 15:22:11 -0700 Subject: [PATCH 10/13] [CVE-2019-0917] --- lib/Backend/IRBuilder.cpp | 4 ++-- lib/Runtime/Language/ValueType.cpp | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/Backend/IRBuilder.cpp b/lib/Backend/IRBuilder.cpp index bef540413ba..75ee5c7f245 100644 --- a/lib/Backend/IRBuilder.cpp +++ b/lib/Backend/IRBuilder.cpp @@ -1758,7 +1758,7 @@ IRBuilder::BuildReg1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0) } case Js::OpCode::NewScObjectSimple: - dstValueType = ValueType::GetObject(ObjectType::Object); + dstValueType = ValueType::GetObject(ObjectType::UninitializedObject); // fall-through case Js::OpCode::LdFuncExpr: m_func->DisableCanDoInlineArgOpt(); @@ -5050,7 +5050,7 @@ IRBuilder::BuildAuxiliary(Js::OpCode newOpcode, uint32 offset) // lower take it from there... srcOpnd = IR::IntConstOpnd::New(auxInsn->Offset, TyUint32, m_func); dstOpnd = this->BuildDstOpnd(dstRegSlot); - dstOpnd->SetValueType(ValueType::GetObject(ObjectType::Object)); + dstOpnd->SetValueType(ValueType::GetObject(ObjectType::UninitializedObject)); instr = IR::Instr::New(newOpcode, dstOpnd, srcOpnd, m_func); // Because we're going to be making decisions based off the value, we have to defer diff --git a/lib/Runtime/Language/ValueType.cpp b/lib/Runtime/Language/ValueType.cpp index fb264cbe1e6..b86b7963f34 100644 --- a/lib/Runtime/Language/ValueType.cpp +++ b/lib/Runtime/Language/ValueType.cpp @@ -577,7 +577,8 @@ bool ValueType::IsNotArrayOrObjectWithArray() const { return IsNotObject() || - (IsObject() && GetObjectType() != ObjectType::ObjectWithArray && GetObjectType() != ObjectType::Array); + (IsObject() && GetObjectType() != ObjectType::ObjectWithArray && GetObjectType() != ObjectType::Array + && GetObjectType() != ObjectType::UninitializedObject && GetObjectType() != ObjectType::Object); } bool ValueType::IsNativeArray() const From 7827e117753052d479fabe19a25cfece88059bca Mon Sep 17 00:00:00 2001 From: Paul Leathers Date: Wed, 17 Apr 2019 15:42:35 -0700 Subject: [PATCH 11/13] [CVE-2019-0937] --- lib/Runtime/ByteCode/ByteCodeEmitter.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/Runtime/ByteCode/ByteCodeEmitter.cpp b/lib/Runtime/ByteCode/ByteCodeEmitter.cpp index 5c07982b92b..933a21d385d 100644 --- a/lib/Runtime/ByteCode/ByteCodeEmitter.cpp +++ b/lib/Runtime/ByteCode/ByteCodeEmitter.cpp @@ -4006,6 +4006,11 @@ void ByteCodeGenerator::StartEmitCatch(ParseNodeCatch *pnodeCatch) sym->SetIsGlobalCatch(true); } + if (sym->NeedsScopeObject()) + { + scope->SetIsObject(); + } + Assert(sym->GetScopeSlot() == Js::Constants::NoProperty); if (sym->NeedsSlotAlloc(this, funcInfo)) { @@ -4029,6 +4034,11 @@ void ByteCodeGenerator::StartEmitCatch(ParseNodeCatch *pnodeCatch) sym->SetIsGlobalCatch(true); } + if (sym->NeedsScopeObject()) + { + scope->SetIsObject(); + } + if (scope->GetMustInstantiate()) { if (sym->IsInSlot(this, funcInfo)) From 00ff32cb3524f66d9f502d36af7d931544969a33 Mon Sep 17 00:00:00 2001 From: Michael Holman Date: Thu, 25 Apr 2019 11:30:34 -0700 Subject: [PATCH 12/13] update chakracore version to 1.11.9 --- 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 40faed9aa40..361ffc5b4d8 100644 --- a/Build/NuGet/.pack-version +++ b/Build/NuGet/.pack-version @@ -1 +1 @@ -1.11.8 +1.11.9 diff --git a/lib/Common/ChakraCoreVersion.h b/lib/Common/ChakraCoreVersion.h index 0ade5e496a3..b7144c0f96d 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 11 -#define CHAKRA_CORE_PATCH_VERSION 8 +#define CHAKRA_CORE_PATCH_VERSION 9 #define CHAKRA_CORE_VERSION_RELEASE_QFE 0 // Redundant with PATCH_VERSION. Keep this value set to 0. // ------------- From 4594e340bc9ca9f857010a68e8b562d65b46eed6 Mon Sep 17 00:00:00 2001 From: Michael Holman Date: Tue, 14 May 2019 11:52:45 -0700 Subject: [PATCH 13/13] fix UT failure --- lib/Backend/GlobOptArrays.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Backend/GlobOptArrays.cpp b/lib/Backend/GlobOptArrays.cpp index 9ac28c59c12..4b88ed9a808 100644 --- a/lib/Backend/GlobOptArrays.cpp +++ b/lib/Backend/GlobOptArrays.cpp @@ -1740,7 +1740,7 @@ void GlobOpt::ArraySrcOpt::Optimize() { UpdateValue(nullptr, nullptr, nullptr); } - else if (globOpt->IsOperationThatLikelyKillsJsArraysWithNoMissingValues(instr) && baseValueInfo->HasNoMissingValues()) + else if (isLikelyJsArray && globOpt->IsOperationThatLikelyKillsJsArraysWithNoMissingValues(instr) && baseValueInfo->HasNoMissingValues()) { globOpt->ChangeValueType(nullptr, baseValue, baseValueInfo->Type().SetHasNoMissingValues(false), true); }