CVE-2018-8372
Description
A remote code execution vulnerability in the scripting engine of Microsoft browsers (ChakraCore, IE 11, Edge) due to a type confusion, allowing arbitrary code execution via a crafted webpage.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A remote code execution vulnerability in the scripting engine of Microsoft browsers (ChakraCore, IE 11, Edge) due to a type confusion, allowing arbitrary code execution via a crafted webpage.
Vulnerability
A type confusion vulnerability exists in the way the Chakra scripting engine handles objects in memory, affecting ChakraCore, Internet Explorer 11, and Microsoft Edge on all supported Windows platforms [1][3]. The flaw occurs in the Lowerer::GenerateProfiledNewScObjArrayFastPath and related functions where a memory corruption can be triggered [2]. This CVE is distinct from similar scripting engine vulnerabilities (CVE-2018-8353, etc.). Vulnerable versions include all releases before the August 2018 security updates.
Exploitation
An attacker can host a specially crafted website or inject malicious content into a site that the user visits. No special privileges are required; the user only needs to view the page with a vulnerable browser. The scripting engine's mishandling of object types leads to memory corruption, which the attacker can leverage to gain code execution.
Impact
Successful exploitation allows an attacker to execute arbitrary code in the context of the current user. If the user has administrative rights, the attacker can take full control of the system, install programs, view/change data, or create new accounts. The attack is remote and requires user interaction only to visit the malicious page.
Mitigation
Microsoft released security updates on August 14, 2018, to address this vulnerability [1][4]. Users should apply the latest updates via Windows Update. The fix is also available in ChakraCore commit 91bb6d6 [2]. No workarounds are listed; updating is the recommended mitigation.
- Microsoft Internet Explorer and Edge CVE-2018-8372 Remote Memory Corruption Vulnerability
- [CVE-2018-8372] Edge - Report a type confusion bug in Edge - 360Vulcan · chakra-core/ChakraCore@91bb6d6
- NVD - CVE-2018-8372
- Microsoft Edge Multiple Bugs Let Remote Users Execute Arbitrary Code, Obtain Potentially Sensitive Information, Gain Elevated Privileges, and Bypass Security Restrictions on the Target System
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.10.2 | 1.10.2 |
Affected products
4- Range: ChakraCore
- Range: Windows 10 for 32-bit Systems
Patches
191bb6d68bfe0[CVE-2018-8372] Edge - Report a type confusion bug in Edge - 360Vulcan
23 files changed · +280 −38
lib/Backend/amd64/LowererMDArch.cpp+19 −0 modified@@ -3448,3 +3448,22 @@ LowererMDArch::LowerEHRegionReturn(IR::Instr * insertBeforeInstr, IR::Opnd * tar // return the last instruction inserted return retInstr; } + +IR::BranchInstr* +LowererMDArch::InsertMissingItemCompareBranch(IR::Opnd* compareSrc, IR::Opnd* missingItemOpnd, Js::OpCode opcode, IR::LabelInstr* target, IR::Instr* insertBeforeInstr) +{ + Assert(compareSrc->IsFloat64() && missingItemOpnd->IsUint64()); + + IR::Opnd * compareSrcUint64Opnd = IR::RegOpnd::New(TyUint64, m_func); + + if (compareSrc->IsRegOpnd()) + { + this->lowererMD->EmitReinterpretPrimitive(compareSrcUint64Opnd, compareSrc, insertBeforeInstr); + } + else if (compareSrc->IsIndirOpnd()) + { + compareSrcUint64Opnd = compareSrc->UseWithNewType(TyUint64, m_func); + } + + return this->lowererMD->m_lowerer->InsertCompareBranch(missingItemOpnd, compareSrcUint64Opnd, opcode, target, insertBeforeInstr); +} \ No newline at end of file
lib/Backend/amd64/LowererMDArch.h+1 −1 modified@@ -151,7 +151,7 @@ class LowererMDArch void LowerInlineSpreadArgOutLoop(IR::Instr *callInstr, IR::RegOpnd *indexOpnd, IR::RegOpnd *arrayElementsStartOpnd); IR::Instr * LowerEHRegionReturn(IR::Instr * insertBeforeInstr, IR::Opnd * targetOpnd); - + IR::BranchInstr* InsertMissingItemCompareBranch(IR::Opnd* compareSrc, IR::Opnd* missingItemOpnd, Js::OpCode opcode, IR::LabelInstr* target, IR::Instr* insertBeforeInstr); private: void MovArgFromReg2Stack(IR::Instr * instr, RegNum reg, Js::ArgSlot slotNumber, IRType type = TyMachReg); void GenerateStackAllocation(IR::Instr *instr, uint32 size);
lib/Backend/arm64/LowerMD.cpp+19 −0 modified@@ -7051,6 +7051,25 @@ LowererMD::InsertObjectPoison(IR::Opnd* poisonedOpnd, IR::BranchInstr* branchIns } } +IR::BranchInstr* +LowererMD::InsertMissingItemCompareBranch(IR::Opnd* compareSrc, IR::Opnd* missingItemOpnd, Js::OpCode opcode, IR::LabelInstr* target, IR::Instr* insertBeforeInstr) +{ + Assert(compareSrc->IsFloat64() && missingItemOpnd->IsUint64()); + + IR::Opnd * compareSrcUint64Opnd = IR::RegOpnd::New(TyUint64, m_func); + if (compareSrc->IsRegOpnd()) + { + IR::Instr * movDoubleToUint64Instr = IR::Instr::New(Js::OpCode::FMOV_GEN, compareSrcUint64Opnd, compareSrc, insertBeforeInstr->m_func); + insertBeforeInstr->InsertBefore(movDoubleToUint64Instr); + } + else if (compareSrc->IsIndirOpnd()) + { + compareSrcUint64Opnd = compareSrc->UseWithNewType(TyUint64, m_func); + } + + return m_lowerer->InsertCompareBranch(compareSrcUint64Opnd, missingItemOpnd, opcode, target, insertBeforeInstr); +} + #if DBG // // Helps in debugging of fast paths.
lib/Backend/arm64/LowerMD.h+1 −0 modified@@ -251,6 +251,7 @@ class LowererMD void GenerateMemInit(IR::RegOpnd * opnd, int32 offset, size_t value, IR::Instr * insertBeforeInstr, bool isZeroed = false); static void InsertObjectPoison(IR::Opnd* poisonedOpnd, IR::BranchInstr* branchInstr, IR::Instr* insertInstr, bool isForStore); + IR::BranchInstr* InsertMissingItemCompareBranch(IR::Opnd* compareSrc, IR::Opnd* missingItemOpnd, Js::OpCode opcode, IR::LabelInstr* target, IR::Instr* insertBeforeInstr); private: static IR::Instr * ChangeToAssign(IR::Instr * instr, IRType destType);
lib/Backend/arm/ARMEncode.h+14 −0 modified@@ -1203,6 +1203,20 @@ static const FormTable Forms_VMOVARMVFP[] = FT (NOMORE, 0x0, 0), }; +static const FormTable Forms_VMOVF64R32L[] = +{ + FT(2dr______, 0x0b10ee00, Steps_FLT_FMSR_d0r), + FT(2rd______, 0x0b10ee10, Steps_FLT_FMRS_rd0), + FT(NOMORE, 0x0, 0), +}; + +static const FormTable Forms_VMOVF64R32U[] = +{ + FT(2dr______, 0x0b10ee20, Steps_FLT_FMSR_d1r), + FT(2rd______, 0x0b10ee30, Steps_FLT_FMRS_rd1), + FT(NOMORE, 0x0, 0), +}; + static const FormTable Forms_VCVTF64F32 [] = { FT (2dd______, 0x0ac0eeb7, Steps_FCVTDS_ds),
lib/Backend/arm/AssemblyStep.h+28 −0 modified@@ -789,13 +789,41 @@ static const AssemblyStep Steps_FLT_FMSR_sr [] = STEP_OPCODE, STEP_DONE }; +static const AssemblyStep Steps_FLT_FMSR_d0r[] = +{ + STEP_DREG, 0, 23, STEP_NEXTOPN, + STEP_REG, 28, + STEP_OPCODE, STEP_DONE +}; + +static const AssemblyStep Steps_FLT_FMSR_d1r[] = +{ + STEP_DREG, 0, 23, STEP_NEXTOPN, + STEP_REG, 28, + STEP_OPCODE, STEP_DONE +}; + static const AssemblyStep Steps_FLT_FMRS_rs [] = { STEP_REG, 28, STEP_NEXTOPN, STEP_SREG, 0, 23, STEP_OPCODE, STEP_DONE }; +static const AssemblyStep Steps_FLT_FMRS_rd0[] = +{ + STEP_REG, 28, STEP_NEXTOPN, + STEP_DREG, 0, 23, + STEP_OPCODE, STEP_DONE +}; + +static const AssemblyStep Steps_FLT_FMRS_rd1[] = +{ + STEP_REG, 28, STEP_NEXTOPN, + STEP_DREG, 0, 23, + STEP_OPCODE, STEP_DONE +}; + static const AssemblyStep Steps_T2_PLD_offset [] = { STEP_BASED, STEP_BASEREG, 0,
lib/Backend/arm/EncoderMD.cpp+2 −0 modified@@ -194,6 +194,8 @@ InstructionType EncoderMD::CanonicalizeInstr(IR::Instr* instr) case Js::OpCode::VSQRT: case Js::OpCode::VMOV: case Js::OpCode::VMOVARMVFP: + case Js::OpCode::VMOVF64R32L: + case Js::OpCode::VMOVF64R32U: case Js::OpCode::VCVTF64F32: case Js::OpCode::VCVTF32F64: case Js::OpCode::VCVTF64S32:
lib/Backend/arm/LowerMD.cpp+24 −0 modified@@ -7695,6 +7695,30 @@ LowererMD::LowerTypeof(IR::Instr* typeOfInstr) m_lowerer->LowerUnaryHelperMem(typeOfInstr, IR::HelperOp_Typeof); } +IR::BranchInstr* +LowererMD::InsertMissingItemCompareBranch(IR::Opnd* compareSrc, IR::Opnd* missingItemOpnd, Js::OpCode opcode, IR::LabelInstr* target, IR::Instr* insertBeforeInstr) +{ + Assert(compareSrc->IsFloat64() && missingItemOpnd->IsUInt32()); + + IR::Opnd * compareSrcUint32Opnd = IR::RegOpnd::New(TyUint32, m_func); + IR::RegOpnd* tmpDoubleRegOpnd = IR::RegOpnd::New(TyFloat64, m_func); + + if (compareSrc->IsIndirOpnd()) + { + Lowerer::InsertMove(tmpDoubleRegOpnd, compareSrc, insertBeforeInstr); + } + else + { + tmpDoubleRegOpnd = compareSrc->AsRegOpnd(); + } + + IR::Instr * movInstr = IR::Instr::New(Js::OpCode::VMOVF64R32U, compareSrcUint32Opnd, tmpDoubleRegOpnd, m_func); + insertBeforeInstr->InsertBefore(movInstr); + Legalize(movInstr); + + return m_lowerer->InsertCompareBranch(missingItemOpnd, compareSrcUint32Opnd, opcode, target, insertBeforeInstr); +} + #if DBG // // Helps in debugging of fast paths.
lib/Backend/arm/LowerMD.h+1 −0 modified@@ -247,6 +247,7 @@ class LowererMD void LowerInlineSpreadArgOutLoop(IR::Instr *callInstr, IR::RegOpnd *indexOpnd, IR::RegOpnd *arrayElementsStartOpnd); void LowerTypeof(IR::Instr * typeOfInstr); void GenerateMemInit(IR::RegOpnd * opnd, int32 offset, size_t value, IR::Instr * insertBeforeInstr, bool isZeroed = false); + IR::BranchInstr* InsertMissingItemCompareBranch(IR::Opnd* compareSrc, IR::Opnd* missingItemOpnd, Js::OpCode opcode, IR::LabelInstr* target, IR::Instr* insertBeforeInstr); private: IR::Opnd* IsOpndNegZero(IR::Opnd* opnd, IR::Instr* instr);
lib/Backend/arm/MdOpCodes.h+2 −0 modified@@ -159,6 +159,8 @@ MACRO(VLDR, Reg2, 0, 0, LEGAL_VLOAD, INSTR_TYPE(Forms MACRO(VLDR32, Reg2, 0, 0, LEGAL_VLOAD, INSTR_TYPE(Forms_VLDR32), DL__C) //single precision float load MACRO(VMOV, Reg2, 0, 0, LEGAL_REG2, INSTR_TYPE(Forms_VMOV), DM__C) MACRO(VMOVARMVFP, Reg2, 0, 0, LEGAL_REG2, INSTR_TYPE(Forms_VMOVARMVFP), DM__C) +MACRO(VMOVF64R32L, Reg2, 0, 0, LEGAL_REG2, INSTR_TYPE(Forms_VMOVF64R32L), DM__C) // transfer bits between integral register and lower 4 bytes of double register +MACRO(VMOVF64R32U, Reg2, 0, 0, LEGAL_REG2, INSTR_TYPE(Forms_VMOVF64R32U), DM__C) // transfer bits between integral register and upper 4 bytes of double register MACRO(VMRS, Empty, OpSideEffect, 0, LEGAL_NONE, INSTR_TYPE(Forms_VMRS), D___C) MACRO(VMRSR, Reg1, OpSideEffect, 0, LEGAL_NONE, INSTR_TYPE(Forms_VMRSR), D___C) MACRO(VMSR, Reg1, OpSideEffect, 0, LEGAL_NONE, INSTR_TYPE(Forms_VMSR), D___C)
lib/Backend/BackwardPass.cpp+2 −2 modified@@ -4907,8 +4907,8 @@ BackwardPass::UpdateArrayBailOutKind(IR::Instr *const instr) } IR::BailOutKind includeBailOutKinds = IR::BailOutInvalid; - if(!baseValueType.IsNotNativeArray() && - (!baseValueType.IsLikelyNativeArray() || !instr->GetSrc1()->IsInt32()) && + if (!baseValueType.IsNotNativeArray() && + (!baseValueType.IsLikelyNativeArray() || instr->GetSrc1()->IsVar()) && !currentBlock->noImplicitCallNativeArrayUses->IsEmpty() && !(instr->GetBailOutKind() & IR::BailOutOnArrayAccessHelperCall)) {
lib/Backend/i386/LowererMDArch.cpp+47 −0 modified@@ -4178,3 +4178,50 @@ LowererMDArch::LowerEHRegionReturn(IR::Instr * insertBeforeInstr, IR::Opnd * tar // return the last instruction inserted return retInstr; } + +IR::BranchInstr* +LowererMDArch::InsertMissingItemCompareBranch(IR::Opnd* compareSrc, IR::Opnd* missingItemOpnd, Js::OpCode opcode, IR::LabelInstr* target, IR::Instr* insertBeforeInstr) +{ + Assert(compareSrc->IsFloat64() && missingItemOpnd->IsUInt32()); + + IR::Opnd * compareSrcUint32Opnd = IR::RegOpnd::New(TyUint32, m_func); + + // Missing item NaN have a different bit pattern from k_Nan, but is a NaN nonetheless. Given that, it is sufficient + // to compare just the top 32 bits + // + // IF sse4.1 available + // mov xmm0, compareSrc + // pextrd ecx, xmm0, 1 <-- ecx will containg xmm0[63:32] after this + // cmp missingItemOpnd, ecx + // jcc target + // + // ELSE + // mov xmm0, compareSrc + // shufps xmm0, xmm0, (3 << 6 | 2 << 4 | 1 << 2 | 1) <-- xmm0[31:0] will contain compareSrc[63:32] after this + // movd ecx, xmm0 + // cmp missingItemOpnd, ecx + // jcc $target + + IR::RegOpnd* tmpDoubleRegOpnd = IR::RegOpnd::New(TyFloat64, m_func); + + if (AutoSystemInfo::Data.SSE4_1Available()) + { + if (compareSrc->IsIndirOpnd()) + { + Lowerer::InsertMove(tmpDoubleRegOpnd, compareSrc, insertBeforeInstr); + } + else + { + tmpDoubleRegOpnd = compareSrc->AsRegOpnd(); + } + Lowerer::InsertAndLegalize(IR::Instr::New(Js::OpCode::PEXTRD, compareSrcUint32Opnd, tmpDoubleRegOpnd, IR::IntConstOpnd::New(1, TyInt8, m_func, true), m_func), insertBeforeInstr); + } + else + { + Lowerer::InsertMove(tmpDoubleRegOpnd, compareSrc, insertBeforeInstr); + Lowerer::InsertAndLegalize(IR::Instr::New(Js::OpCode::SHUFPS, tmpDoubleRegOpnd, tmpDoubleRegOpnd, IR::IntConstOpnd::New(3 << 6 | 2 << 4 | 1 << 2 | 1, TyInt8, m_func, true), m_func), insertBeforeInstr); + Lowerer::InsertAndLegalize(IR::Instr::New(Js::OpCode::MOVD, compareSrcUint32Opnd, tmpDoubleRegOpnd, m_func), insertBeforeInstr); + } + + return this->lowererMD->m_lowerer->InsertCompareBranch(missingItemOpnd, compareSrcUint32Opnd, opcode, target, insertBeforeInstr); +} \ No newline at end of file
lib/Backend/i386/LowererMDArch.h+1 −1 modified@@ -121,7 +121,7 @@ class LowererMDArch void LowerInlineSpreadArgOutLoop(IR::Instr *callInstr, IR::RegOpnd *indexOpnd, IR::RegOpnd *arrayElementsStartOpnd); IR::Instr * LowerEHRegionReturn(IR::Instr * insertBeforeInstr, IR::Opnd * targetOpnd); - + IR::BranchInstr* InsertMissingItemCompareBranch(IR::Opnd* compareSrc, IR::Opnd* missingItemOpnd, Js::OpCode opcode, IR::LabelInstr* target, IR::Instr* insertBeforeInstr); private: void GeneratePreCall(IR::Instr * callInstr, IR::Opnd *functionObjOpnd); };
lib/Backend/Lower.cpp+79 −24 modified@@ -4207,7 +4207,7 @@ Lowerer::GenerateProfiledNewScObjArrayFastPath(IR::Instr *instr, Js::ArrayCallSi { // Ensure we don't write missingItems past allocation size Assert(offsetStart + missingItemIndex * sizeOfElement <= maxAllocationSize); - GenerateMemInit(headOpnd, offsetStart + missingItemIndex * sizeOfElement, GetMissingItemOpnd(missingItemType, func), instr, true /*isZeroed*/); + GenerateMemInit(headOpnd, offsetStart + missingItemIndex * sizeOfElement, GetMissingItemOpndForAssignment(missingItemType, func), instr, true /*isZeroed*/); missingItemIndex++; } @@ -10802,7 +10802,7 @@ Lowerer::LowerStElemC(IR::Instr * stElem) IR::Opnd* missingElementOpnd = GetMissingItemOpnd(stElem->GetSrc1()->GetType(), m_func); if (!stElem->GetSrc1()->IsEqual(missingElementOpnd)) { - InsertCompareBranch(stElem->GetSrc1(), missingElementOpnd , Js::OpCode::BrEq_A, labelBailOut, stElem, true); + InsertMissingItemCompareBranch(stElem->GetSrc1(), Js::OpCode::BrEq_A, labelBailOut, stElem); } else { @@ -11858,7 +11858,7 @@ Lowerer::GenerateHelperToArrayPopFastPath(IR::Instr * instr, IR::LabelInstr * do if(retInstr->GetDst()) { //Do this check only for native arrays with Dst. For Var arrays, this is taken care in the Runtime helper itself. - InsertCompareBranch(GetMissingItemOpnd(retInstr->GetDst()->GetType(), m_func), retInstr->GetDst(), Js::OpCode::BrNeq_A, doneLabel, bailOutLabelHelper); + InsertMissingItemCompareBranch(retInstr->GetDst(), Js::OpCode::BrNeq_A, doneLabel, bailOutLabelHelper); } else { @@ -16459,13 +16459,11 @@ Lowerer::GenerateFastElemIIntIndexCommon( Assert(instr->m_opcode != Js::OpCode::InlineArrayPush || bailOutLabelInstr); // Check for a write of the MissingItem value. - InsertCompareBranch( + InsertMissingItemCompareBranch( element, - GetMissingItemOpnd(elementType, m_func), Js::OpCode::BrEq_A, instr->m_opcode == Js::OpCode::InlineArrayPush ? bailOutLabelInstr : labelCantUseArray, - instr, - true); + instr); } if(!headSegmentOpnd) @@ -16985,17 +16983,15 @@ Lowerer::GenerateFastElemIIntIndexCommon( //If the array has missing values, check for one if (!baseValueType.HasNoMissingValues()) { - InsertCompareBranch( + InsertMissingItemCompareBranch( dst, - GetMissingItemOpnd(indirType, m_func), Js::OpCode::BrEq_A, bailOutLabelInstr, - instr, - true); + instr); } } // MOV [head + offset], missing - InsertMove(indirOpnd, GetMissingItemOpnd(indirType, m_func), instr); + InsertMove(indirOpnd, GetMissingItemOpndForAssignment(indirType, m_func), instr); IR::Opnd *newLengthOpnd; IR::AutoReuseOpnd autoReuseNewLengthOpnd; @@ -17276,6 +17272,22 @@ Lowerer::GenerateFastElemIIntIndexCommon( return indirOpnd; } +IR::BranchInstr* +Lowerer::InsertMissingItemCompareBranch(IR::Opnd* compareSrc, Js::OpCode opcode, IR::LabelInstr* target, IR::Instr* insertBeforeInstr) +{ + IR::Opnd* missingItemOpnd = GetMissingItemOpndForCompare(compareSrc->GetType(), m_func); + if (compareSrc->IsFloat64()) + { + Assert(compareSrc->IsRegOpnd() || compareSrc->IsIndirOpnd()); + return m_lowererMD.InsertMissingItemCompareBranch(compareSrc, missingItemOpnd, opcode, target, insertBeforeInstr); + } + else + { + Assert(compareSrc->IsInt32() || compareSrc->IsVar()); + return InsertCompareBranch(missingItemOpnd, compareSrc, opcode, target, insertBeforeInstr, true); + } +} + IR::RegOpnd * Lowerer::GenerateUntagVar(IR::RegOpnd * opnd, IR::LabelInstr * labelFail, IR::Instr * insertBeforeInstr, bool generateTagCheck) { @@ -17836,13 +17848,11 @@ Lowerer::GenerateFastLdElemI(IR::Instr *& ldElem, bool *instrIsInHelperBlockRef) { // TEST dst, dst // JEQ $helper | JNE $fallthrough - InsertCompareBranch( + InsertMissingItemCompareBranch( dst, - GetMissingItemOpnd(dst->GetType(), m_func), needObjectTest ? Js::OpCode::BrEq_A : Js::OpCode::BrNeq_A, needObjectTest ? labelHelper : labelFallThru, - ldElem, - true); + ldElem); if (isNativeArrayLoad) { @@ -17928,7 +17938,7 @@ Lowerer::GenerateFastLdElemI(IR::Instr *& ldElem, bool *instrIsInHelperBlockRef) labelMissingNative = IR::LabelInstr::New(Js::OpCode::Label, m_func, true); } - InsertCompareBranch(GetMissingItemOpnd(ldElem->GetDst()->GetType(), m_func), ldElem->GetDst(), Js::OpCode::BrEq_A, labelMissingNative, insertBeforeInstr, true); + InsertMissingItemCompareBranch(ldElem->GetDst(), Js::OpCode::BrEq_A, labelMissingNative, insertBeforeInstr); } InsertBranch(Js::OpCode::Br, labelFallThru, insertBeforeInstr); if(labelMissingNative) @@ -17960,7 +17970,7 @@ Lowerer::GenerateFastLdElemI(IR::Instr *& ldElem, bool *instrIsInHelperBlockRef) { if(!emitBailout) { - InsertCompareBranch(GetMissingItemOpnd(ldElem->GetDst()->GetType(), m_func), ldElem->GetDst(), Js::OpCode::BrEq_A, labelBailOut, insertBeforeInstr, true); + InsertMissingItemCompareBranch(ldElem->GetDst(), Js::OpCode::BrEq_A, labelBailOut, insertBeforeInstr); } InsertBranch(Js::OpCode::Br, labelFallThru, insertBeforeInstr); @@ -17990,8 +18000,48 @@ Lowerer::GetMissingItemOpnd(IRType type, Func *func) { return IR::IntConstOpnd::New(Js::JavascriptNativeIntArray::MissingItem, TyInt32, func, true); } - Assert(type == TyFloat64); - return IR::MemRefOpnd::New(func->GetThreadContextInfo()->GetNativeFloatArrayMissingItemAddr(), TyFloat64, func); + AssertMsg(false, "Only expecting TyVar and TyInt32 in Lowerer::GetMissingItemOpnd"); + __assume(false); +} + +IR::Opnd* +Lowerer::GetMissingItemOpndForAssignment(IRType type, Func *func) +{ + switch (type) + { + case TyVar: + case TyInt32: + return GetMissingItemOpnd(type, func); + + case TyFloat64: + return IR::MemRefOpnd::New(func->GetThreadContextInfo()->GetNativeFloatArrayMissingItemAddr(), TyFloat64, func); + + default: + AnalysisAssertMsg(false, "Unexpected type in Lowerer::GetMissingItemOpndForAssignment"); + __assume(false); + } +} + +IR::Opnd * +Lowerer::GetMissingItemOpndForCompare(IRType type, Func *func) +{ + switch (type) + { + case TyVar: + case TyInt32: + return GetMissingItemOpnd(type, func); + + case TyFloat64: +#if TARGET_64 + return IR::MemRefOpnd::New(func->GetThreadContextInfo()->GetNativeFloatArrayMissingItemAddr(), TyUint64, func); +#else + return IR::MemRefOpnd::New(func->GetThreadContextInfo()->GetNativeFloatArrayMissingItemAddr(), TyUint32, func); +#endif + + default: + AnalysisAssertMsg(false, "Unexpected type in Lowerer::GetMissingItemOpndForCompare"); + __assume(false); + } } bool @@ -18631,13 +18681,11 @@ Lowerer::GenerateFastStElemI(IR::Instr *& stElem, bool *instrIsInHelperBlockRef) // // cmp [segment + index], Js::SparseArraySegment::MissingValue // je $helper - InsertCompareBranch( + InsertMissingItemCompareBranch( indirOpnd, - GetMissingItemOpnd(src->GetType(), m_func), Js::OpCode::BrEq_A, labelHelper, - stElem, - true); + stElem); } else { @@ -28023,6 +28071,13 @@ Lowerer::AddBailoutToHelperCallInstr(IR::Instr * helperCallInstr, BailOutInfo * return helperCallInstr; } +void +Lowerer::InsertAndLegalize(IR::Instr * instr, IR::Instr* insertBeforeInstr) +{ + insertBeforeInstr->InsertBefore(instr); + LowererMD::Legalize(instr); +} + #if DBG void Lowerer::LegalizeVerifyRange(IR::Instr * instrStart, IR::Instr * instrLast)
lib/Backend/Lower.h+4 −0 modified@@ -417,6 +417,8 @@ class Lowerer public: static IR::HelperCallOpnd* CreateHelperCallOpnd(IR::JnHelperMethod helperMethod, int helperArgCount, Func* func); static IR::Opnd * GetMissingItemOpnd(IRType type, Func *func); + static IR::Opnd * GetMissingItemOpndForAssignment(IRType type, Func *func); + static IR::Opnd * GetMissingItemOpndForCompare(IRType type, Func *func); static IR::Opnd * GetImplicitCallFlagsOpnd(Func * func); inline static IR::IntConstOpnd* MakeCallInfoConst(ushort flags, int32 argCount, Func* func) { argCount = Js::CallInfo::GetArgCountWithoutExtraArgs((Js::CallFlags)flags, (uint16)argCount); @@ -430,6 +432,7 @@ class Lowerer return IR::IntConstOpnd::New(argCount | (flags << 24), TyMachReg, func, true); #endif } + static void InsertAndLegalize(IR::Instr * instr, IR::Instr* insertBeforeInstr); private: IR::IndirOpnd* GenerateFastElemICommon( _In_ IR::Instr* elemInstr, @@ -516,6 +519,7 @@ class Lowerer _Inout_ IR::RegOpnd** taggedTypeOpnd); void GenerateFastIsInSymbolOrStringIndex(IR::Instr * instrInsert, IR::RegOpnd *indexOpnd, IR::RegOpnd *baseOpnd, IR::Opnd *dest, uint32 inlineCacheOffset, const uint32 hitRateOffset, IR::LabelInstr * labelHelper, IR::LabelInstr * labelDone); + IR::BranchInstr* InsertMissingItemCompareBranch(IR::Opnd* compareSrc, Js::OpCode opcode, IR::LabelInstr* target, IR::Instr* insertBeforeInstr); bool GenerateFastLdElemI(IR::Instr *& ldElem, bool *instrIsInHelperBlockRef); bool GenerateFastStElemI(IR::Instr *& StElem, bool *instrIsInHelperBlockRef); bool GenerateFastLdLen(IR::Instr *ldLen, bool *instrIsInHelperBlockRef);
lib/Backend/LowerMDShared.cpp+6 −0 modified@@ -8624,4 +8624,10 @@ LowererMD::InsertCmovCC(const Js::OpCode opCode, IR::Opnd * dst, IR::Opnd* src1, LowererMD::Legalize(instr); return instr; +} + +IR::BranchInstr* +LowererMD::InsertMissingItemCompareBranch(IR::Opnd* compareSrc, IR::Opnd* missingItemOpnd, Js::OpCode opcode, IR::LabelInstr* target, IR::Instr* insertBeforeInstr) +{ + return this->lowererMDArch.InsertMissingItemCompareBranch(compareSrc, missingItemOpnd, opcode, target, insertBeforeInstr); } \ No newline at end of file
lib/Backend/LowerMDShared.h+1 −0 modified@@ -223,6 +223,7 @@ class LowererMD void EmitLoadFloatFromNumber(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr); void EmitLoadFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr, IR::Instr * instrBailOut = nullptr, IR::LabelInstr * labelBailOut = nullptr); static void EmitNon32BitOvfCheck(IR::Instr *instr, IR::Instr *insertInstr, IR::LabelInstr* bailOutLabel); + IR::BranchInstr* InsertMissingItemCompareBranch(IR::Opnd* compareSrc, IR::Opnd* missingItemOpnd, Js::OpCode opcode, IR::LabelInstr* target, IR::Instr* insertBeforeInstr); static void LowerInt4NegWithBailOut(IR::Instr *const instr, const IR::BailOutKind bailOutKind, IR::LabelInstr *const bailOutLabel, IR::LabelInstr *const skipBailOutLabel); static void LowerInt4AddWithBailOut(IR::Instr *const instr, const IR::BailOutKind bailOutKind, IR::LabelInstr *const bailOutLabel, IR::LabelInstr *const skipBailOutLabel);
lib/Backend/Opnd.h+1 −0 modified@@ -226,6 +226,7 @@ class Opnd bool IsUnsigned() const { return IRType_IsUnsignedInt(this->m_type); } int GetSize() const { return TySize[this->m_type]; } bool IsInt64() const { return IRType_IsInt64(this->m_type); } + bool IsUint64() const { return this->m_type == TyUint64; } bool IsInt32() const { return this->m_type == TyInt32; } bool IsUInt32() const { return this->m_type == TyUint32; } bool IsIntegral32() const { return IsInt32() || IsUInt32(); }
lib/Runtime/Library/JavascriptArray.cpp+6 −5 modified@@ -27,8 +27,10 @@ using namespace Js; { 5, 0, 0 }, // allocate space for 5 elements for array of length 4,5 { 8, 0, 0 }, // allocate space for 8 elements for array of length 6,7,8 }; + + const Var JavascriptArray::MissingItem = (Var)FloatMissingItemPattern; #if defined(TARGET_64) - const Var JavascriptArray::MissingItem = (Var)0x8000000280000002; + const Var JavascriptArray::IntMissingItemVar = (Var)(((uint64)IntMissingItemPattern << 32) | (uint32)IntMissingItemPattern); uint JavascriptNativeIntArray::allocationBuckets[][AllocationBucketsInfoSize] = { // See comments above on how to read this @@ -44,7 +46,7 @@ using namespace Js; {8, 0, 0}, }; #else - const Var JavascriptArray::MissingItem = (Var)0x80000002; + const Var JavascriptArray::IntMissingItemVar = (Var)IntMissingItemPattern; uint JavascriptNativeIntArray::allocationBuckets[][AllocationBucketsInfoSize] = { // See comments above on how to read this @@ -60,8 +62,7 @@ using namespace Js; }; #endif - const int32 JavascriptNativeIntArray::MissingItem = 0x80000002; - static const uint64 FloatMissingItemPattern = 0x8000000280000002ull; + const int32 JavascriptNativeIntArray::MissingItem = IntMissingItemPattern; const double JavascriptNativeFloatArray::MissingItem = *(double*)&FloatMissingItemPattern; // Allocate enough space for 4 inline property slots and 16 inline element slots @@ -12769,7 +12770,7 @@ using namespace Js; return TypeIds_NativeIntArray; } } - if (JavascriptNumber::Is_NoTaggedIntCheck(value)) + else if (JavascriptNumber::Is_NoTaggedIntCheck(value)) { bool isInt32; int32 i;
lib/Runtime/Library/JavascriptArray.h+1 −0 modified@@ -140,6 +140,7 @@ namespace Js #endif static uint allocationBuckets[AllocationBucketsCount][AllocationBucketsInfoSize]; static const Var MissingItem; + static const Var IntMissingItemVar; template<typename T> static T GetMissingItem(); SparseArraySegmentBase * GetHead() const { return head; }
lib/Runtime/Library/SparseArraySegment.h+12 −4 modified@@ -89,6 +89,7 @@ namespace Js static SparseArraySegment<T>* CopySegment(Recycler *recycler, SparseArraySegment<T>* dst, uint32 dstIndex, SparseArraySegment<T>* src, uint32 srcIndex, uint32 inputLen); static T GetMissingItem(); + static Var GetMissingItemVar(); static bool IsMissingItem(const T* value); template <class S> @@ -131,20 +132,27 @@ namespace Js template<> inline int32 SparseArraySegment<int32>::GetMissingItem() { - return 0x80000002; + return IntMissingItemPattern; } template<> inline double SparseArraySegment<double>::GetMissingItem() { - uint64 u = 0x8000000280000002; - return *(double*)&u; + return *(double*)&FloatMissingItemPattern; } + template<typename T> + Var SparseArraySegment<T>::GetMissingItemVar() + { + return JavascriptArray::MissingItem; + } + template<> + Var SparseArraySegment<int32>::GetMissingItemVar(); + template<> inline bool SparseArraySegment<double>::IsMissingItem(const double* value) { - return *(uint64*)value == 0x8000000280000002ull; + return *(uint64*)value == FloatMissingItemPattern; } template<typename T>
lib/Runtime/Library/SparseArraySegment.inl+7 −1 modified@@ -223,12 +223,18 @@ namespace Js return SparseArraySegment<T>::Allocate<isLeaf>(recycler, left, length, size); } + template<> + inline Var SparseArraySegment<int32>::GetMissingItemVar() + { + return JavascriptArray::IntMissingItemVar; + } + template<typename T> void SparseArraySegment<T>::FillSegmentBuffer(uint32 start, uint32 size) { // Fill the segment buffer using gp-register-sized stores. Avoid using the FPU for the sake // of perf (especially x86). - Var fill = JavascriptArray::MissingItem; + Var fill = (Var)SparseArraySegment<T>::GetMissingItemVar(); if (sizeof(Var) > sizeof(T)) { // Pointer size is greater than the element (int32 buffer on x64).
lib/Runtime/RuntimeCommon.h+2 −0 modified@@ -180,6 +180,8 @@ namespace Js #if FLOATVAR const uint64 FloatTag_Value = 0xFFFCull << 48; #endif + const uint64 FloatMissingItemPattern = 0xFFF80002FFF80002; + const int32 IntMissingItemPattern = 0xFFF80002; template <bool IsPrototypeTemplate> class NullTypeHandler; template <typename TPropertyIndex, typename TMapKey, bool IsNotExtensibleSupported> class SimpleDictionaryTypeHandlerBase;
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
9- github.com/advisories/GHSA-p349-q795-qj4qghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2018-8372ghsaADVISORY
- www.securityfocus.com/bid/105038mitrevdb-entryx_refsource_BID
- www.securitytracker.com/id/1041457mitrevdb-entryx_refsource_SECTRACK
- github.com/chakra-core/ChakraCore/commit/91bb6d68bfe0455cde08aaa5fbc3f2e4f6cc9d04ghsaWEB
- github.com/chakra-core/ChakraCore/pull/5596ghsaWEB
- portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2018-8372ghsax_refsource_CONFIRMWEB
- web.archive.org/web/20210124195605/http://www.securityfocus.com/bid/105038ghsaWEB
- web.archive.org/web/20211203061111/http://www.securitytracker.com/id/1041457ghsaWEB
News mentions
0No linked articles in our index yet.