CVE-2019-0914
Description
CVE-2019-0914 is a Chakra scripting engine memory corruption vulnerability in Microsoft Edge that enables remote code execution.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
CVE-2019-0914 is a Chakra scripting engine memory corruption vulnerability in Microsoft Edge that enables remote code execution.
Vulnerability
CVE-2019-0914 is a memory corruption vulnerability within the Chakra scripting engine as used in Microsoft Edge. The flaw arises from how the engine handles objects in memory, specifically during optimization passes such as ProcessFieldKills in the global optimizer. The patch addresses missing handling for several opcodes (e.g., StElemC, ScopedDeleteFld, ScopedStFld, StSuperFld) that could allow improper type tracking or object header inlining, leading to memory corruption [1][2][3].
Exploitation
Exploitation requires a victim to browse to a specially crafted web page using an affected version of Microsoft Edge. No additional authentication is needed; the attacker simply hosts a malicious site or injects content into a trusted site. The vulnerability is triggered when the Chakra engine compiles and executes JavaScript that exercises the unhandled opcodes during JIT compilation [2][3].
Impact
Successful exploitation grants an attacker the ability to execute arbitrary code in the context of the current user. This can lead to full system compromise, including data theft, installation of malware, or further lateral movement within an enterprise network. The vulnerability is rated with a CVSS score of 7.5 (High) [1].
Mitigation
Microsoft released a security update in May 2019 that addresses this vulnerability [1]. The fix is part of a cumulative update that also resolves several related Chakra memory corruption issues (CVE-2019-0912, CVE-2019-0913, CVE-2019-0915, CVE-2019-0916, etc.). Organizations should ensure that systems running Microsoft Edge are updated to the latest version. No workarounds are available; applying the patch is the only mitigation.
AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
Microsoft.ChakraCoreNuGet | < 1.11.9 | 1.11.9 |
Affected products
6- Range: unspecified
- Microsoft/Microsoft Edge on Windows 10 Version 1903 for 32-bit Systemsv5Range: unspecified
- Microsoft/Microsoft Edge on Windows 10 Version 1903 for ARM64-based Systemsv5Range: unspecified
- Microsoft/Microsoft Edge on Windows 10 Version 1903 for x64-based Systemsv5Range: unspecified
Patches
2d797e3f00e34[MERGE #6122 @MikeHolman] May 2019 Security Update
18 files changed · +182 −38
Build/NuGet/.pack-version+1 −1 modified@@ -1 +1 @@ -1.11.8 +1.11.9
lib/Backend/BackwardPass.cpp+1 −2 modified@@ -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;
lib/Backend/GlobOptArrays.cpp+8 −1 modified@@ -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 (isLikelyJsArray && globOpt->IsOperationThatLikelyKillsJsArraysWithNoMissingValues(instr) && baseValueInfo->HasNoMissingValues()) + { + globOpt->ChangeValueType(nullptr, baseValue, baseValueInfo->Type().SetHasNoMissingValues(false), true); + } } // For javascript arrays and objects with javascript arrays:
lib/Backend/GlobOptBailOut.cpp+8 −0 modified@@ -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; }
lib/Backend/GlobOpt.cpp+7 −0 modified@@ -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,8 +13250,13 @@ 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: { Assert(instr->GetDst()); @@ -13462,6 +13468,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
lib/Backend/GlobOptExpr.cpp+8 −0 modified@@ -814,20 +814,28 @@ 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: case Js::OpCode::StRootFldStrict: + case Js::OpCode::StSuperFld: case Js::OpCode::StSlot: case Js::OpCode::StSlotChkUndecl: case Js::OpCode::DeleteFld: 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:
lib/Backend/GlobOptFields.cpp+51 −7 modified@@ -335,6 +335,7 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *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<JitArenaAllocator> *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,13 +390,36 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *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: 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) @@ -415,11 +441,19 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *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 +478,23 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *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; @@ -492,6 +535,7 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *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.
lib/Backend/Inline.cpp+2 −0 modified@@ -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));
lib/Backend/IRBuilder.cpp+2 −2 modified@@ -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
lib/Backend/Lower.cpp+32 −14 modified@@ -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,21 +4879,17 @@ 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; } 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;
lib/Common/ChakraCoreVersion.h+1 −1 modified@@ -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. // -------------
lib/Runtime/ByteCode/ByteCodeEmitter.cpp+11 −0 modified@@ -3590,6 +3590,7 @@ void ByteCodeGenerator::StartEmitFunction(ParseNodeFnc *pnodeFnc) #if ENABLE_TTD && !funcInfo->GetParsedFunctionBody()->GetScriptContext()->GetThreadContext()->IsRuntimeInTTDMode() #endif + && !funcInfo->byteCodeFunction->IsCoroutine() ); if (funcInfo->GetHasCachedScope()) @@ -4005,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)) { @@ -4028,6 +4034,11 @@ void ByteCodeGenerator::StartEmitCatch(ParseNodeCatch *pnodeCatch) sym->SetIsGlobalCatch(true); } + if (sym->NeedsScopeObject()) + { + scope->SetIsObject(); + } + if (scope->GetMustInstantiate()) { if (sym->IsInSlot(this, funcInfo))
lib/Runtime/ByteCode/ByteCodeGenerator.cpp+6 −6 modified@@ -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();
lib/Runtime/ByteCode/OpCodes.h+2 −2 modified@@ -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)
lib/Runtime/Debug/DebugContext.cpp+33 −1 modified@@ -193,6 +193,11 @@ namespace Js Js::TempArenaAllocatorObject *tempAllocator = nullptr; JsUtil::List<Js::FunctionInfo *, Recycler>* pFunctionsToRegister = nullptr; JsUtil::List<Js::Utf8SourceInfo *, Recycler, false, Js::CopyRemovePolicy, RecyclerPointerComparer>* utf8SourceInfoList = nullptr; + typedef JsUtil::BaseDictionary<uint32, RegSlot, ArenaAllocator, PowerOf2SizePolicy> 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<Js::Utf8SourceInfo *, Recycler, false, Js::CopyRemovePolicy, RecyclerPointerComparer>::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();
lib/Runtime/Language/ValueType.cpp+2 −1 modified@@ -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
lib/Runtime/Library/ES5Array.cpp+4 −0 modified@@ -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; } }
lib/Runtime/Library/JavascriptArray.cpp+3 −0 modified@@ -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
d85b5025b047[CVE-2019-0913][CVE-2019-0914][CVE-2019-0915][CVE-2019-0916]
3 files changed · +37 −0
lib/Backend/GlobOpt.cpp+5 −0 modified@@ -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:
lib/Backend/GlobOptExpr.cpp+7 −0 modified@@ -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:
lib/Backend/GlobOptFields.cpp+25 −0 modified@@ -335,6 +335,7 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *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<JitArenaAllocator> *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<JitArenaAllocator> *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:
Vulnerability mechanics
Root cause
"The Chakra scripting engine improperly handles objects in memory, leading to memory corruption."
Attack vector
An attacker can trigger this vulnerability by convincing a user to visit a specially crafted website using Microsoft Edge. The attack relies on the Chakra scripting engine's improper handling of objects in memory, which can lead to remote code execution. The specific preconditions and payload structure are not detailed in the provided advisory [patch_id=15630, patch_id=15631].
Affected code
The vulnerability exists within the Chakra scripting engine, which is used by Microsoft Edge. The affected code paths involve the engine's internal handling of objects in memory [patch_id=15630, patch_id=15631].
What the fix does
The patches [patch_id=15630, patch_id=15631] modify the Chakra scripting engine to correctly manage objects in memory. By ensuring proper memory handling, the engine prevents the corruption that could otherwise be exploited for remote code execution. The specific code changes are contained within the referenced commits in the ChakraCore repository.
Preconditions
- networkThe user must visit a specially crafted website using Microsoft Edge.
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-h6wf-hvwc-fm77ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2019-0914ghsaADVISORY
- github.com/chakra-core/ChakraCore/commit/d797e3f00e34c12c8c0ae52f56344325439dccd7ghsaWEB
- github.com/chakra-core/ChakraCore/commit/d85b5025b047f10784c53c6c1dd771775d417f5fghsaWEB
- portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2019-0914ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.