CVE-2019-1092
Description
A memory corruption vulnerability in ChakraCore's JIT compiler due to improper array bounds handling can allow remote code execution in Microsoft Edge.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A memory corruption vulnerability in ChakraCore's JIT compiler due to improper array bounds handling can allow remote code execution in Microsoft Edge.
Vulnerability
Analysis
CVE-2019-1092 is a remote code execution vulnerability in the Chakra scripting engine used by Microsoft Edge. The root cause lies in the Just-In-Time (JIT) compiler's incorrect handling of array value information merging during compilation, leading to an out-of-bounds read/write condition [1][3]. This flaw allows an attacker to corrupt memory and potentially execute arbitrary code.
Exploitation
The attack surface is limited to users browsing the web with Microsoft Edge. An attacker can host a malicious website containing crafted JavaScript that triggers the vulnerable code path. No additional authentication or network position is required beyond standard web browsing [2].
Impact
Successful exploitation enables an attacker to run arbitrary code in the context of the current user. This could result in installation of malware, data theft, or complete compromise of the affected system [2].
Mitigation
Microsoft released a security update in July 2019 that addresses this vulnerability. Users are advised to apply the latest patches to Microsoft Edge or upgrade to a supported browser. The fix is also available in the ChakraCore open-source repository [1].
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.11 | 1.11.11 |
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
375162b7f2d8a[MERGE #6196 @atulkatti] ChakraCore servicing update for July, 2019
16 files changed · +81 −27
Build/NuGet/.pack-version+1 −1 modified@@ -1 +1 @@ -1.11.10 +1.11.11
lib/Backend/BackwardPass.cpp+8 −4 modified@@ -4151,13 +4151,17 @@ BackwardPass::UpdateImplicitCallBailOutKind(IR::Instr *const instr, bool needsBa IR::BailOutKind implicitCallBailOutKind = needsBailOutOnImplicitCall ? IR::BailOutOnImplicitCalls : IR::BailOutInvalid; - const IR::BailOutKind instrBailOutKind = instr->GetBailOutKind(); + IR::BailOutKind instrBailOutKind = instr->GetBailOutKind(); if (instrBailOutKind & IR::BailOutMarkTempObject) { - // Don't remove the implicit call pre op bailout for mark temp object // Remove the mark temp object bit, as we don't need it after the dead store pass - instr->SetBailOutKind(instrBailOutKind & ~IR::BailOutMarkTempObject); - return true; + instrBailOutKind &= ~IR::BailOutMarkTempObject; + instr->SetBailOutKind(instrBailOutKind); + + if (!instr->GetBailOutInfo()->canDeadStore) + { + return true; + } } const IR::BailOutKind instrImplicitCallBailOutKind = instrBailOutKind & ~IR::BailOutKindBits;
lib/Backend/BailOut.h+2 −1 modified@@ -27,7 +27,7 @@ class BailOutInfo BailOutInfo(uint32 bailOutOffset, Func* bailOutFunc) : bailOutOffset(bailOutOffset), bailOutFunc(bailOutFunc), byteCodeUpwardExposedUsed(nullptr), polymorphicCacheIndex((uint)-1), startCallCount(0), startCallInfo(nullptr), bailOutInstr(nullptr), - totalOutParamCount(0), argOutSyms(nullptr), bailOutRecord(nullptr), wasCloned(false), isInvertedBranch(false), sharedBailOutKind(true), isLoopTopBailOutInfo(false), + totalOutParamCount(0), argOutSyms(nullptr), bailOutRecord(nullptr), wasCloned(false), isInvertedBranch(false), sharedBailOutKind(true), isLoopTopBailOutInfo(false), canDeadStore(true), outParamInlinedArgSlot(nullptr), liveVarSyms(nullptr), liveLosslessInt32Syms(nullptr), liveFloat64Syms(nullptr), branchConditionOpnd(nullptr), stackLiteralBailOutInfoCount(0), stackLiteralBailOutInfo(nullptr) @@ -69,6 +69,7 @@ class BailOutInfo #endif bool wasCloned; bool isInvertedBranch; + bool canDeadStore; bool sharedBailOutKind; bool isLoopTopBailOutInfo;
lib/Backend/Func.cpp+9 −0 modified@@ -345,6 +345,9 @@ Func::Codegen(JitArenaAllocator *alloc, JITTimeWorkItem * workItem, case RejitReason::TrackIntOverflowDisabled: outputData->disableTrackCompoundedIntOverflow = TRUE; break; + case RejitReason::MemOpDisabled: + outputData->disableMemOp = TRUE; + break; default: Assume(UNREACHED); } @@ -1124,6 +1127,12 @@ Func::IsTrackCompoundedIntOverflowDisabled() const return (HasProfileInfo() && GetReadOnlyProfileInfo()->IsTrackCompoundedIntOverflowDisabled()) || m_output.IsTrackCompoundedIntOverflowDisabled(); } +bool +Func::IsMemOpDisabled() const +{ + return (HasProfileInfo() && GetReadOnlyProfileInfo()->IsMemOpDisabled()) || m_output.IsMemOpDisabled(); +} + bool Func::IsArrayCheckHoistDisabled() const {
lib/Backend/Func.h+1 −0 modified@@ -995,6 +995,7 @@ static const unsigned __int64 c_debugFillPattern8 = 0xcececececececece; void SetScopeObjSym(StackSym * sym); StackSym * GetScopeObjSym(); bool IsTrackCompoundedIntOverflowDisabled() const; + bool IsMemOpDisabled() const; bool IsArrayCheckHoistDisabled() const; bool IsStackArgOptDisabled() const; bool IsSwitchOptDisabled() const;
lib/Backend/GlobOptBlockData.cpp+7 −5 modified@@ -974,7 +974,8 @@ GlobOptBlockData::MergeValueInfo( fromDataValueInfo->AsArrayValueInfo(), fromDataSym, symsRequiringCompensation, - symsCreatedForMerge); + symsCreatedForMerge, + isLoopBackEdge); } // Consider: If both values are VarConstantValueInfo with the same value, we could @@ -1072,7 +1073,8 @@ ValueInfo *GlobOptBlockData::MergeArrayValueInfo( const ArrayValueInfo *const fromDataValueInfo, Sym *const arraySym, BVSparse<JitArenaAllocator> *const symsRequiringCompensation, - BVSparse<JitArenaAllocator> *const symsCreatedForMerge) + BVSparse<JitArenaAllocator> *const symsCreatedForMerge, + bool isLoopBackEdge) { Assert(mergedValueType.IsAnyOptimizedArray()); Assert(toDataValueInfo); @@ -1095,7 +1097,7 @@ ValueInfo *GlobOptBlockData::MergeArrayValueInfo( } else { - if (!this->globOpt->IsLoopPrePass()) + if (!this->globOpt->IsLoopPrePass() && !isLoopBackEdge) { // Adding compensation code in the prepass won't help, as the symstores would again be different in the main pass. Assert(symsRequiringCompensation); @@ -1123,7 +1125,7 @@ ValueInfo *GlobOptBlockData::MergeArrayValueInfo( } else { - if (!this->globOpt->IsLoopPrePass()) + if (!this->globOpt->IsLoopPrePass() && !isLoopBackEdge) { Assert(symsRequiringCompensation); symsRequiringCompensation->Set(arraySym->m_id); @@ -1150,7 +1152,7 @@ ValueInfo *GlobOptBlockData::MergeArrayValueInfo( } else { - if (!this->globOpt->IsLoopPrePass()) + if (!this->globOpt->IsLoopPrePass() && !isLoopBackEdge) { Assert(symsRequiringCompensation); symsRequiringCompensation->Set(arraySym->m_id);
lib/Backend/GlobOptBlockData.h+1 −1 modified@@ -264,7 +264,7 @@ class GlobOptBlockData Value * MergeValues(Value *toDataValue, Value *fromDataValue, Sym *fromDataSym, bool isLoopBackEdge, BVSparse<JitArenaAllocator> *const symsRequiringCompensation, BVSparse<JitArenaAllocator> *const symsCreatedForMerge); ValueInfo * MergeValueInfo(Value *toDataVal, Value *fromDataVal, Sym *fromDataSym, bool isLoopBackEdge, bool sameValueNumber, BVSparse<JitArenaAllocator> *const symsRequiringCompensation, BVSparse<JitArenaAllocator> *const symsCreatedForMerge); JsTypeValueInfo * MergeJsTypeValueInfo(JsTypeValueInfo * toValueInfo, JsTypeValueInfo * fromValueInfo, bool isLoopBackEdge, bool sameValueNumber); - ValueInfo * MergeArrayValueInfo(const ValueType mergedValueType, const ArrayValueInfo *const toDataValueInfo, const ArrayValueInfo *const fromDataValueInfo, Sym *const arraySym, BVSparse<JitArenaAllocator> *const symsRequiringCompensation, BVSparse<JitArenaAllocator> *const symsCreatedForMerge); + ValueInfo * MergeArrayValueInfo(const ValueType mergedValueType, const ArrayValueInfo *const toDataValueInfo, const ArrayValueInfo *const fromDataValueInfo, Sym *const arraySym, BVSparse<JitArenaAllocator> *const symsRequiringCompensation, BVSparse<JitArenaAllocator> *const symsCreatedForMerge, bool isLoopBackEdge); // Argument Tracking public:
lib/Backend/GlobOpt.cpp+15 −2 modified@@ -2624,7 +2624,7 @@ GlobOpt::OptInstr(IR::Instr *&instr, bool* isInstrRemoved) !(instr->IsJitProfilingInstr()) && this->currentBlock->loop && !IsLoopPrePass() && !func->IsJitInDebugMode() && - (func->HasProfileInfo() && !func->GetReadOnlyProfileInfo()->IsMemOpDisabled()) && + !func->IsMemOpDisabled() && this->currentBlock->loop->doMemOp) { CollectMemOpInfo(instrPrev, instr, src1Val, src2Val); @@ -16531,6 +16531,7 @@ GlobOpt::GenerateBailOutMarkTempObjectIfNeeded(IR::Instr * instr, IR::Opnd * opn if (instr->HasBailOutInfo()) { instr->SetBailOutKind(instr->GetBailOutKind() | IR::BailOutMarkTempObject); + instr->GetBailOutInfo()->canDeadStore = false; } else { @@ -16540,6 +16541,11 @@ GlobOpt::GenerateBailOutMarkTempObjectIfNeeded(IR::Instr * instr, IR::Opnd * opn || (instr->m_opcode == Js::OpCode::FromVar && !opnd->GetValueType().IsPrimitive()) || propertySymOpnd == nullptr || !propertySymOpnd->IsTypeCheckProtected()) + { + this->GenerateBailAtOperation(&instr, IR::BailOutMarkTempObject); + instr->GetBailOutInfo()->canDeadStore = false; + } + else if (propertySymOpnd->MayHaveImplicitCall()) { this->GenerateBailAtOperation(&instr, IR::BailOutMarkTempObject); } @@ -16680,7 +16686,14 @@ GlobOpt::GenerateInductionVariableChangeForMemOp(Loop *loop, byte unroll, IR::In } else { - uint size = (loopCount->LoopCountMinusOneConstantValue() + 1) * unroll; + int32 loopCountMinusOnePlusOne; + int32 size; + if (Int32Math::Add(loopCount->LoopCountMinusOneConstantValue(), 1, &loopCountMinusOnePlusOne) || + Int32Math::Mul(loopCountMinusOnePlusOne, unroll, &size)) + { + throw Js::RejitException(RejitReason::MemOpDisabled); + } + Assert(size > 0); sizeOpnd = IR::IntConstOpnd::New(size, IRType::TyUint32, localFunc); } loop->memOpInfo->inductionVariableOpndPerUnrollMap->Add(unroll, sizeOpnd);
lib/Backend/GlobOptFields.cpp+8 −0 modified@@ -410,6 +410,14 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *bv, bo if (inGlobOpt) { KillObjectHeaderInlinedTypeSyms(this->currentBlock, false); + if (this->objectTypeSyms) + { + if (this->currentBlock->globOptData.maybeWrittenTypeSyms == nullptr) + { + this->currentBlock->globOptData.maybeWrittenTypeSyms = JitAnew(this->alloc, BVSparse<JitArenaAllocator>, this->alloc); + } + this->currentBlock->globOptData.maybeWrittenTypeSyms->Or(this->objectTypeSyms); + } } // fall through
lib/Backend/JITOutput.cpp+6 −0 modified@@ -65,6 +65,12 @@ JITOutput::IsTrackCompoundedIntOverflowDisabled() const return m_outputData->disableTrackCompoundedIntOverflow != FALSE; } +bool +JITOutput::IsMemOpDisabled() const +{ + return m_outputData->disableMemOp != FALSE; +} + bool JITOutput::IsArrayCheckHoistDisabled() const {
lib/Backend/JITOutput.h+1 −0 modified@@ -22,6 +22,7 @@ class JITOutput void RecordXData(BYTE * xdata); #endif bool IsTrackCompoundedIntOverflowDisabled() const; + bool IsMemOpDisabled() const; bool IsArrayCheckHoistDisabled() const; bool IsStackArgOptDisabled() const; bool IsSwitchOptDisabled() const;
lib/Backend/NativeCodeGenerator.cpp+4 −0 modified@@ -1234,6 +1234,10 @@ NativeCodeGenerator::CodeGen(PageAllocator * pageAllocator, CodeGenWorkItem* wor { body->GetAnyDynamicProfileInfo()->DisableTrackCompoundedIntOverflow(); } + if (jitWriteData.disableMemOp) + { + body->GetAnyDynamicProfileInfo()->DisableMemOp(); + } } if (jitWriteData.disableInlineApply)
lib/Backend/Opnd.cpp+5 −6 modified@@ -962,7 +962,8 @@ PropertySymOpnd::IsObjectHeaderInlined() const bool PropertySymOpnd::ChangesObjectLayout() const { - JITTypeHolder cachedType = this->IsMono() ? this->GetType() : this->GetFirstEquivalentType(); + JITTypeHolder cachedType = this->HasInitialType() ? this->GetInitialType() : + this->IsMono() ? this->GetType() : this->GetFirstEquivalentType(); JITTypeHolder finalType = this->GetFinalType(); @@ -987,13 +988,11 @@ PropertySymOpnd::ChangesObjectLayout() const // This is the case where the type transition actually occurs. (This is the only case that's detectable // during the loop pre-pass, since final types are not in place yet.) - Assert(cachedType != nullptr && Js::DynamicType::Is(cachedType->GetTypeId())); - - const JITTypeHandler * cachedTypeHandler = cachedType->GetTypeHandler(); const JITTypeHandler * initialTypeHandler = initialType->GetTypeHandler(); - return cachedTypeHandler->GetInlineSlotCapacity() != initialTypeHandler->GetInlineSlotCapacity() || - cachedTypeHandler->GetOffsetOfInlineSlots() != initialTypeHandler->GetOffsetOfInlineSlots(); + // If no final type has been set in the forward pass, then we have no way of knowing how the object shape will evolve here. + // If the initial type is object-header-inlined, assume that the layout may change. + return initialTypeHandler->IsObjectHeaderInlinedTypeHandler(); } return false;
lib/Backend/Opnd.h+2 −1 modified@@ -1138,7 +1138,8 @@ class PropertySymOpnd sealed : public SymOpnd // fall back on live cache. Similarly, for fixed method checks. bool MayHaveImplicitCall() const { - return !IsRootObjectNonConfigurableFieldLoad() && !UsesFixedValue() && (!IsTypeCheckSeqCandidate() || !IsTypeCheckProtected()); + return !IsRootObjectNonConfigurableFieldLoad() && !UsesFixedValue() && (!IsTypeCheckSeqCandidate() || !IsTypeCheckProtected() + || (IsLoadedFromProto() && NeedsWriteGuardTypeCheck())); } // Is the instruction involving this operand part of a type check sequence? This is different from IsObjTypeSpecOptimized
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 10 +#define CHAKRA_CORE_PATCH_VERSION 11 #define CHAKRA_CORE_VERSION_RELEASE_QFE 0 // Redundant with PATCH_VERSION. Keep this value set to 0. // -------------
lib/JITIDL/JITTypes.h+10 −5 modified@@ -838,37 +838,42 @@ typedef struct JITOutputIDL boolean disableStackArgOpt; boolean disableSwitchOpt; boolean disableTrackCompoundedIntOverflow; - boolean isInPrereservedRegion; + boolean disableMemOp; + boolean isInPrereservedRegion; boolean hasBailoutInstr; - boolean hasJittedStackClosure; + IDL_PAD1(0) unsigned short pdataCount; unsigned short xdataSize; unsigned short argUsedForBranch; + IDL_PAD2(1) int localVarSlotsOffset; // FunctionEntryPointInfo only + int localVarChangedOffset; // FunctionEntryPointInfo only unsigned int frameHeight; - unsigned int codeSize; unsigned int throwMapOffset; + unsigned int throwMapCount; unsigned int inlineeFrameOffsetArrayOffset; - unsigned int inlineeFrameOffsetArrayCount; + unsigned int inlineeFrameOffsetArrayCount; unsigned int propertyGuardCount; + unsigned int ctorCachesCount; + X64_PAD4(2) #if TARGET_64 CHAKRA_PTR xdataAddr; #elif defined(_M_ARM) unsigned int xdataOffset; #else - X86_PAD4(0) + X86_PAD4(3) #endif CHAKRA_PTR codeAddress; CHAKRA_PTR thunkAddress;
75162b7f2d8a[MERGE #6196 @atulkatti] ChakraCore servicing update for July, 2019
16 files changed · +81 −27
Build/NuGet/.pack-version+1 −1 modified@@ -1 +1 @@ -1.11.10 +1.11.11
lib/Backend/BackwardPass.cpp+8 −4 modified@@ -4151,13 +4151,17 @@ BackwardPass::UpdateImplicitCallBailOutKind(IR::Instr *const instr, bool needsBa IR::BailOutKind implicitCallBailOutKind = needsBailOutOnImplicitCall ? IR::BailOutOnImplicitCalls : IR::BailOutInvalid; - const IR::BailOutKind instrBailOutKind = instr->GetBailOutKind(); + IR::BailOutKind instrBailOutKind = instr->GetBailOutKind(); if (instrBailOutKind & IR::BailOutMarkTempObject) { - // Don't remove the implicit call pre op bailout for mark temp object // Remove the mark temp object bit, as we don't need it after the dead store pass - instr->SetBailOutKind(instrBailOutKind & ~IR::BailOutMarkTempObject); - return true; + instrBailOutKind &= ~IR::BailOutMarkTempObject; + instr->SetBailOutKind(instrBailOutKind); + + if (!instr->GetBailOutInfo()->canDeadStore) + { + return true; + } } const IR::BailOutKind instrImplicitCallBailOutKind = instrBailOutKind & ~IR::BailOutKindBits;
lib/Backend/BailOut.h+2 −1 modified@@ -27,7 +27,7 @@ class BailOutInfo BailOutInfo(uint32 bailOutOffset, Func* bailOutFunc) : bailOutOffset(bailOutOffset), bailOutFunc(bailOutFunc), byteCodeUpwardExposedUsed(nullptr), polymorphicCacheIndex((uint)-1), startCallCount(0), startCallInfo(nullptr), bailOutInstr(nullptr), - totalOutParamCount(0), argOutSyms(nullptr), bailOutRecord(nullptr), wasCloned(false), isInvertedBranch(false), sharedBailOutKind(true), isLoopTopBailOutInfo(false), + totalOutParamCount(0), argOutSyms(nullptr), bailOutRecord(nullptr), wasCloned(false), isInvertedBranch(false), sharedBailOutKind(true), isLoopTopBailOutInfo(false), canDeadStore(true), outParamInlinedArgSlot(nullptr), liveVarSyms(nullptr), liveLosslessInt32Syms(nullptr), liveFloat64Syms(nullptr), branchConditionOpnd(nullptr), stackLiteralBailOutInfoCount(0), stackLiteralBailOutInfo(nullptr) @@ -69,6 +69,7 @@ class BailOutInfo #endif bool wasCloned; bool isInvertedBranch; + bool canDeadStore; bool sharedBailOutKind; bool isLoopTopBailOutInfo;
lib/Backend/Func.cpp+9 −0 modified@@ -345,6 +345,9 @@ Func::Codegen(JitArenaAllocator *alloc, JITTimeWorkItem * workItem, case RejitReason::TrackIntOverflowDisabled: outputData->disableTrackCompoundedIntOverflow = TRUE; break; + case RejitReason::MemOpDisabled: + outputData->disableMemOp = TRUE; + break; default: Assume(UNREACHED); } @@ -1124,6 +1127,12 @@ Func::IsTrackCompoundedIntOverflowDisabled() const return (HasProfileInfo() && GetReadOnlyProfileInfo()->IsTrackCompoundedIntOverflowDisabled()) || m_output.IsTrackCompoundedIntOverflowDisabled(); } +bool +Func::IsMemOpDisabled() const +{ + return (HasProfileInfo() && GetReadOnlyProfileInfo()->IsMemOpDisabled()) || m_output.IsMemOpDisabled(); +} + bool Func::IsArrayCheckHoistDisabled() const {
lib/Backend/Func.h+1 −0 modified@@ -995,6 +995,7 @@ static const unsigned __int64 c_debugFillPattern8 = 0xcececececececece; void SetScopeObjSym(StackSym * sym); StackSym * GetScopeObjSym(); bool IsTrackCompoundedIntOverflowDisabled() const; + bool IsMemOpDisabled() const; bool IsArrayCheckHoistDisabled() const; bool IsStackArgOptDisabled() const; bool IsSwitchOptDisabled() const;
lib/Backend/GlobOptBlockData.cpp+7 −5 modified@@ -974,7 +974,8 @@ GlobOptBlockData::MergeValueInfo( fromDataValueInfo->AsArrayValueInfo(), fromDataSym, symsRequiringCompensation, - symsCreatedForMerge); + symsCreatedForMerge, + isLoopBackEdge); } // Consider: If both values are VarConstantValueInfo with the same value, we could @@ -1072,7 +1073,8 @@ ValueInfo *GlobOptBlockData::MergeArrayValueInfo( const ArrayValueInfo *const fromDataValueInfo, Sym *const arraySym, BVSparse<JitArenaAllocator> *const symsRequiringCompensation, - BVSparse<JitArenaAllocator> *const symsCreatedForMerge) + BVSparse<JitArenaAllocator> *const symsCreatedForMerge, + bool isLoopBackEdge) { Assert(mergedValueType.IsAnyOptimizedArray()); Assert(toDataValueInfo); @@ -1095,7 +1097,7 @@ ValueInfo *GlobOptBlockData::MergeArrayValueInfo( } else { - if (!this->globOpt->IsLoopPrePass()) + if (!this->globOpt->IsLoopPrePass() && !isLoopBackEdge) { // Adding compensation code in the prepass won't help, as the symstores would again be different in the main pass. Assert(symsRequiringCompensation); @@ -1123,7 +1125,7 @@ ValueInfo *GlobOptBlockData::MergeArrayValueInfo( } else { - if (!this->globOpt->IsLoopPrePass()) + if (!this->globOpt->IsLoopPrePass() && !isLoopBackEdge) { Assert(symsRequiringCompensation); symsRequiringCompensation->Set(arraySym->m_id); @@ -1150,7 +1152,7 @@ ValueInfo *GlobOptBlockData::MergeArrayValueInfo( } else { - if (!this->globOpt->IsLoopPrePass()) + if (!this->globOpt->IsLoopPrePass() && !isLoopBackEdge) { Assert(symsRequiringCompensation); symsRequiringCompensation->Set(arraySym->m_id);
lib/Backend/GlobOptBlockData.h+1 −1 modified@@ -264,7 +264,7 @@ class GlobOptBlockData Value * MergeValues(Value *toDataValue, Value *fromDataValue, Sym *fromDataSym, bool isLoopBackEdge, BVSparse<JitArenaAllocator> *const symsRequiringCompensation, BVSparse<JitArenaAllocator> *const symsCreatedForMerge); ValueInfo * MergeValueInfo(Value *toDataVal, Value *fromDataVal, Sym *fromDataSym, bool isLoopBackEdge, bool sameValueNumber, BVSparse<JitArenaAllocator> *const symsRequiringCompensation, BVSparse<JitArenaAllocator> *const symsCreatedForMerge); JsTypeValueInfo * MergeJsTypeValueInfo(JsTypeValueInfo * toValueInfo, JsTypeValueInfo * fromValueInfo, bool isLoopBackEdge, bool sameValueNumber); - ValueInfo * MergeArrayValueInfo(const ValueType mergedValueType, const ArrayValueInfo *const toDataValueInfo, const ArrayValueInfo *const fromDataValueInfo, Sym *const arraySym, BVSparse<JitArenaAllocator> *const symsRequiringCompensation, BVSparse<JitArenaAllocator> *const symsCreatedForMerge); + ValueInfo * MergeArrayValueInfo(const ValueType mergedValueType, const ArrayValueInfo *const toDataValueInfo, const ArrayValueInfo *const fromDataValueInfo, Sym *const arraySym, BVSparse<JitArenaAllocator> *const symsRequiringCompensation, BVSparse<JitArenaAllocator> *const symsCreatedForMerge, bool isLoopBackEdge); // Argument Tracking public:
lib/Backend/GlobOpt.cpp+15 −2 modified@@ -2624,7 +2624,7 @@ GlobOpt::OptInstr(IR::Instr *&instr, bool* isInstrRemoved) !(instr->IsJitProfilingInstr()) && this->currentBlock->loop && !IsLoopPrePass() && !func->IsJitInDebugMode() && - (func->HasProfileInfo() && !func->GetReadOnlyProfileInfo()->IsMemOpDisabled()) && + !func->IsMemOpDisabled() && this->currentBlock->loop->doMemOp) { CollectMemOpInfo(instrPrev, instr, src1Val, src2Val); @@ -16531,6 +16531,7 @@ GlobOpt::GenerateBailOutMarkTempObjectIfNeeded(IR::Instr * instr, IR::Opnd * opn if (instr->HasBailOutInfo()) { instr->SetBailOutKind(instr->GetBailOutKind() | IR::BailOutMarkTempObject); + instr->GetBailOutInfo()->canDeadStore = false; } else { @@ -16540,6 +16541,11 @@ GlobOpt::GenerateBailOutMarkTempObjectIfNeeded(IR::Instr * instr, IR::Opnd * opn || (instr->m_opcode == Js::OpCode::FromVar && !opnd->GetValueType().IsPrimitive()) || propertySymOpnd == nullptr || !propertySymOpnd->IsTypeCheckProtected()) + { + this->GenerateBailAtOperation(&instr, IR::BailOutMarkTempObject); + instr->GetBailOutInfo()->canDeadStore = false; + } + else if (propertySymOpnd->MayHaveImplicitCall()) { this->GenerateBailAtOperation(&instr, IR::BailOutMarkTempObject); } @@ -16680,7 +16686,14 @@ GlobOpt::GenerateInductionVariableChangeForMemOp(Loop *loop, byte unroll, IR::In } else { - uint size = (loopCount->LoopCountMinusOneConstantValue() + 1) * unroll; + int32 loopCountMinusOnePlusOne; + int32 size; + if (Int32Math::Add(loopCount->LoopCountMinusOneConstantValue(), 1, &loopCountMinusOnePlusOne) || + Int32Math::Mul(loopCountMinusOnePlusOne, unroll, &size)) + { + throw Js::RejitException(RejitReason::MemOpDisabled); + } + Assert(size > 0); sizeOpnd = IR::IntConstOpnd::New(size, IRType::TyUint32, localFunc); } loop->memOpInfo->inductionVariableOpndPerUnrollMap->Add(unroll, sizeOpnd);
lib/Backend/GlobOptFields.cpp+8 −0 modified@@ -410,6 +410,14 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *bv, bo if (inGlobOpt) { KillObjectHeaderInlinedTypeSyms(this->currentBlock, false); + if (this->objectTypeSyms) + { + if (this->currentBlock->globOptData.maybeWrittenTypeSyms == nullptr) + { + this->currentBlock->globOptData.maybeWrittenTypeSyms = JitAnew(this->alloc, BVSparse<JitArenaAllocator>, this->alloc); + } + this->currentBlock->globOptData.maybeWrittenTypeSyms->Or(this->objectTypeSyms); + } } // fall through
lib/Backend/JITOutput.cpp+6 −0 modified@@ -65,6 +65,12 @@ JITOutput::IsTrackCompoundedIntOverflowDisabled() const return m_outputData->disableTrackCompoundedIntOverflow != FALSE; } +bool +JITOutput::IsMemOpDisabled() const +{ + return m_outputData->disableMemOp != FALSE; +} + bool JITOutput::IsArrayCheckHoistDisabled() const {
lib/Backend/JITOutput.h+1 −0 modified@@ -22,6 +22,7 @@ class JITOutput void RecordXData(BYTE * xdata); #endif bool IsTrackCompoundedIntOverflowDisabled() const; + bool IsMemOpDisabled() const; bool IsArrayCheckHoistDisabled() const; bool IsStackArgOptDisabled() const; bool IsSwitchOptDisabled() const;
lib/Backend/NativeCodeGenerator.cpp+4 −0 modified@@ -1234,6 +1234,10 @@ NativeCodeGenerator::CodeGen(PageAllocator * pageAllocator, CodeGenWorkItem* wor { body->GetAnyDynamicProfileInfo()->DisableTrackCompoundedIntOverflow(); } + if (jitWriteData.disableMemOp) + { + body->GetAnyDynamicProfileInfo()->DisableMemOp(); + } } if (jitWriteData.disableInlineApply)
lib/Backend/Opnd.cpp+5 −6 modified@@ -962,7 +962,8 @@ PropertySymOpnd::IsObjectHeaderInlined() const bool PropertySymOpnd::ChangesObjectLayout() const { - JITTypeHolder cachedType = this->IsMono() ? this->GetType() : this->GetFirstEquivalentType(); + JITTypeHolder cachedType = this->HasInitialType() ? this->GetInitialType() : + this->IsMono() ? this->GetType() : this->GetFirstEquivalentType(); JITTypeHolder finalType = this->GetFinalType(); @@ -987,13 +988,11 @@ PropertySymOpnd::ChangesObjectLayout() const // This is the case where the type transition actually occurs. (This is the only case that's detectable // during the loop pre-pass, since final types are not in place yet.) - Assert(cachedType != nullptr && Js::DynamicType::Is(cachedType->GetTypeId())); - - const JITTypeHandler * cachedTypeHandler = cachedType->GetTypeHandler(); const JITTypeHandler * initialTypeHandler = initialType->GetTypeHandler(); - return cachedTypeHandler->GetInlineSlotCapacity() != initialTypeHandler->GetInlineSlotCapacity() || - cachedTypeHandler->GetOffsetOfInlineSlots() != initialTypeHandler->GetOffsetOfInlineSlots(); + // If no final type has been set in the forward pass, then we have no way of knowing how the object shape will evolve here. + // If the initial type is object-header-inlined, assume that the layout may change. + return initialTypeHandler->IsObjectHeaderInlinedTypeHandler(); } return false;
lib/Backend/Opnd.h+2 −1 modified@@ -1138,7 +1138,8 @@ class PropertySymOpnd sealed : public SymOpnd // fall back on live cache. Similarly, for fixed method checks. bool MayHaveImplicitCall() const { - return !IsRootObjectNonConfigurableFieldLoad() && !UsesFixedValue() && (!IsTypeCheckSeqCandidate() || !IsTypeCheckProtected()); + return !IsRootObjectNonConfigurableFieldLoad() && !UsesFixedValue() && (!IsTypeCheckSeqCandidate() || !IsTypeCheckProtected() + || (IsLoadedFromProto() && NeedsWriteGuardTypeCheck())); } // Is the instruction involving this operand part of a type check sequence? This is different from IsObjTypeSpecOptimized
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 10 +#define CHAKRA_CORE_PATCH_VERSION 11 #define CHAKRA_CORE_VERSION_RELEASE_QFE 0 // Redundant with PATCH_VERSION. Keep this value set to 0. // -------------
lib/JITIDL/JITTypes.h+10 −5 modified@@ -838,37 +838,42 @@ typedef struct JITOutputIDL boolean disableStackArgOpt; boolean disableSwitchOpt; boolean disableTrackCompoundedIntOverflow; - boolean isInPrereservedRegion; + boolean disableMemOp; + boolean isInPrereservedRegion; boolean hasBailoutInstr; - boolean hasJittedStackClosure; + IDL_PAD1(0) unsigned short pdataCount; unsigned short xdataSize; unsigned short argUsedForBranch; + IDL_PAD2(1) int localVarSlotsOffset; // FunctionEntryPointInfo only + int localVarChangedOffset; // FunctionEntryPointInfo only unsigned int frameHeight; - unsigned int codeSize; unsigned int throwMapOffset; + unsigned int throwMapCount; unsigned int inlineeFrameOffsetArrayOffset; - unsigned int inlineeFrameOffsetArrayCount; + unsigned int inlineeFrameOffsetArrayCount; unsigned int propertyGuardCount; + unsigned int ctorCachesCount; + X64_PAD4(2) #if TARGET_64 CHAKRA_PTR xdataAddr; #elif defined(_M_ARM) unsigned int xdataOffset; #else - X86_PAD4(0) + X86_PAD4(3) #endif CHAKRA_PTR codeAddress; CHAKRA_PTR thunkAddress;
d4e767fb9461[CVE-2019-1092] Chakra JIT OOB R/W
2 files changed · +8 −6
lib/Backend/GlobOptBlockData.cpp+7 −5 modified@@ -974,7 +974,8 @@ GlobOptBlockData::MergeValueInfo( fromDataValueInfo->AsArrayValueInfo(), fromDataSym, symsRequiringCompensation, - symsCreatedForMerge); + symsCreatedForMerge, + isLoopBackEdge); } // Consider: If both values are VarConstantValueInfo with the same value, we could @@ -1072,7 +1073,8 @@ ValueInfo *GlobOptBlockData::MergeArrayValueInfo( const ArrayValueInfo *const fromDataValueInfo, Sym *const arraySym, BVSparse<JitArenaAllocator> *const symsRequiringCompensation, - BVSparse<JitArenaAllocator> *const symsCreatedForMerge) + BVSparse<JitArenaAllocator> *const symsCreatedForMerge, + bool isLoopBackEdge) { Assert(mergedValueType.IsAnyOptimizedArray()); Assert(toDataValueInfo); @@ -1095,7 +1097,7 @@ ValueInfo *GlobOptBlockData::MergeArrayValueInfo( } else { - if (!this->globOpt->IsLoopPrePass()) + if (!this->globOpt->IsLoopPrePass() && !isLoopBackEdge) { // Adding compensation code in the prepass won't help, as the symstores would again be different in the main pass. Assert(symsRequiringCompensation); @@ -1123,7 +1125,7 @@ ValueInfo *GlobOptBlockData::MergeArrayValueInfo( } else { - if (!this->globOpt->IsLoopPrePass()) + if (!this->globOpt->IsLoopPrePass() && !isLoopBackEdge) { Assert(symsRequiringCompensation); symsRequiringCompensation->Set(arraySym->m_id); @@ -1150,7 +1152,7 @@ ValueInfo *GlobOptBlockData::MergeArrayValueInfo( } else { - if (!this->globOpt->IsLoopPrePass()) + if (!this->globOpt->IsLoopPrePass() && !isLoopBackEdge) { Assert(symsRequiringCompensation); symsRequiringCompensation->Set(arraySym->m_id);
lib/Backend/GlobOptBlockData.h+1 −1 modified@@ -264,7 +264,7 @@ class GlobOptBlockData Value * MergeValues(Value *toDataValue, Value *fromDataValue, Sym *fromDataSym, bool isLoopBackEdge, BVSparse<JitArenaAllocator> *const symsRequiringCompensation, BVSparse<JitArenaAllocator> *const symsCreatedForMerge); ValueInfo * MergeValueInfo(Value *toDataVal, Value *fromDataVal, Sym *fromDataSym, bool isLoopBackEdge, bool sameValueNumber, BVSparse<JitArenaAllocator> *const symsRequiringCompensation, BVSparse<JitArenaAllocator> *const symsCreatedForMerge); JsTypeValueInfo * MergeJsTypeValueInfo(JsTypeValueInfo * toValueInfo, JsTypeValueInfo * fromValueInfo, bool isLoopBackEdge, bool sameValueNumber); - ValueInfo * MergeArrayValueInfo(const ValueType mergedValueType, const ArrayValueInfo *const toDataValueInfo, const ArrayValueInfo *const fromDataValueInfo, Sym *const arraySym, BVSparse<JitArenaAllocator> *const symsRequiringCompensation, BVSparse<JitArenaAllocator> *const symsCreatedForMerge); + ValueInfo * MergeArrayValueInfo(const ValueType mergedValueType, const ArrayValueInfo *const toDataValueInfo, const ArrayValueInfo *const fromDataValueInfo, Sym *const arraySym, BVSparse<JitArenaAllocator> *const symsRequiringCompensation, BVSparse<JitArenaAllocator> *const symsCreatedForMerge, bool isLoopBackEdge); // Argument Tracking public:
Vulnerability mechanics
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-2x75-mf24-588mghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2019-1092ghsaADVISORY
- github.com/chakra-core/ChakraCore/commit/75162b7f2d8ac2b37d17564e9c979ba1bae707e8ghsaWEB
- github.com/chakra-core/ChakraCore/commit/d4e767fb946128c135d77edda7a794561e1c1f06ghsaWEB
- portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2019-1092ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.