CVE-2019-1103
Description
A remote code execution vulnerability exists in the way that the Chakra scripting engine handles objects in memory in Microsoft Edge, aka 'Chakra Scripting Engine Memory Corruption Vulnerability'. This CVE ID is unique from CVE-2019-1062, CVE-2019-1092, CVE-2019-1106, CVE-2019-1107.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Remote code execution vulnerability in Microsoft Edge's Chakra scripting engine due to memory corruption in JIT compiler.
Vulnerability
Overview
CVE-2019-1103 is a remote code execution vulnerability in the Chakra scripting engine used by Microsoft Edge. The issue stems from improper handling of objects in memory during JIT compilation, leading to memory corruption. The ChakraCore commit [1] fixes the vulnerability by correcting bailout handling in the backward pass, ensuring proper dead store detection and preventing type confusion. A related commit [3] adds a canDeadStore flag to the BailOutInfo class to address the root cause.
Exploitation
An attacker can exploit this vulnerability by hosting a specially crafted website that, when visited in Microsoft Edge, triggers the memory corruption in the Chakra engine. The attack requires no special privileges beyond browsing the malicious page.
Impact
Successful exploitation allows the attacker to execute arbitrary code in the context of the current user. If the user has administrative rights, the attacker could take control of the affected system, install programs, view/change/delete data, or create new accounts.
Mitigation
Microsoft released a security update in July 2019 to address this vulnerability. Users should apply the latest patches to Microsoft Edge or update ChakraCore to version 1.11.11 or later. The NVD entry [2] provides additional details.
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
8- 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;
efab31010280[CVE-2019-1103] Chakra JIT Type Confusion
4 files changed · +18 −6
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/GlobOpt.cpp+6 −0 modified@@ -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); }
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
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-vmf5-924f-25f2ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2019-1103ghsaADVISORY
- github.com/chakra-core/ChakraCore/commit/75162b7f2d8ac2b37d17564e9c979ba1bae707e8ghsaWEB
- github.com/chakra-core/ChakraCore/commit/efab3101028045cbfa0cc21bd852f75bcc037dbaghsaWEB
- portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2019-1103ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.