CVE-2018-8588
Description
A memory corruption vulnerability in ChakraCore/Microsoft Edge allows remote code execution via crafted web content.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A memory corruption vulnerability in ChakraCore/Microsoft Edge allows remote code execution via crafted web content.
Vulnerability
A remote code execution vulnerability exists in the way that the Chakra scripting engine handles objects in memory in Microsoft Edge and ChakraCore [1][2]. This is a memory corruption issue classified as a failure to handle exceptional conditions [2]. Affected versions include Microsoft Edge on multiple Windows 10 editions and ChakraCore prior to the fix [2][3]. The vulnerability is distinct from several other Chakra-related CVEs [1].
Exploitation
An attacker can host specially crafted content on a website and convince a user to visit it (typically via social engineering or by compromising a legitimate site) [2][4]. No authentication or special privileges are required; the attacker only needs the user to open the malicious page in an affected version of Microsoft Edge [2][4]. The Chakra engine improperly handles objects in memory while processing the crafted content, leading to exploitable memory corruption [1][2].
Impact
Successful exploitation allows the attacker to execute arbitrary code in the context of the current user [1][2][4]. If the user is running with administrative privileges, the attacker could gain full control of the system, including installing programs, viewing/changing data, or creating new accounts [1][2]. The impact is remote code execution with the same privileges as the logged-on user [2][4].
Mitigation
Microsoft released a security update as part of the November 2018 Patch Tuesday, which addresses this vulnerability [2][4]. Users should apply the latest updates for Microsoft Edge and Windows 10 immediately [2][4]. ChakraCore users should update to the patched version from the commit that fixed the issue [3]. No workaround is available besides applying the vendor-supplied fix [4].
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.3 | 1.11.3 |
Affected products
3- Range: ChakraCore
Patches
16 files changed · +37 −24
lib/Backend/Lower.cpp+17 −22 modified@@ -3765,13 +3765,11 @@ Lowerer::GenerateProfiledNewScArrayFastPath(IR::Instr *instr, Js::ArrayCallSiteI GenerateMemInit(dstOpnd, Js::JavascriptNativeFloatArray::GetOffsetOfWeakFuncRef(), IR::AddrOpnd::New(weakFuncRef, IR::AddrOpndKindDynamicFunctionBodyWeakRef, m_func), instr, isZeroed); // Js::JavascriptArray::MissingItem is a Var, so it may be 32-bit or 64 bit. uint const offsetStart = sizeof(Js::SparseArraySegmentBase); - uint const missingItemCount = size * sizeof(double) / sizeof(Js::JavascriptArray::MissingItem); - i = i * sizeof(double) / sizeof(Js::JavascriptArray::MissingItem); - for (; i < missingItemCount; i++) + for (; i < size; i++) { GenerateMemInit( - headOpnd, offsetStart + i * sizeof(Js::JavascriptArray::MissingItem), - IR::AddrOpnd::New(Js::JavascriptArray::MissingItem, IR::AddrOpndKindConstantAddress, m_func, true), + headOpnd, offsetStart + i * sizeof(double), + GetMissingItemOpndForAssignment(TyFloat64, m_func), instr, isZeroed); } } @@ -3788,7 +3786,7 @@ Lowerer::GenerateProfiledNewScArrayFastPath(IR::Instr *instr, Js::ArrayCallSiteI { GenerateMemInit( headOpnd, offsetStart + i * sizeof(Js::Var), - IR::AddrOpnd::New(Js::JavascriptArray::MissingItem, IR::AddrOpndKindConstantAddress, m_func, true), + GetMissingItemOpndForAssignment(TyVar, m_func), instr, isZeroed); } } @@ -4111,12 +4109,11 @@ Lowerer::GenerateProfiledNewScObjArrayFastPath(IR::Instr *instr, Js::ArrayCallSi // Js::JavascriptArray::MissingItem is a Var, so it may be 32-bit or 64 bit. uint const offsetStart = sizeof(Js::SparseArraySegmentBase); - uint const missingItemCount = size * sizeof(double) / sizeof(Js::JavascriptArray::MissingItem); - for (uint i = 0; i < missingItemCount; i++) + for (uint i = 0; i < size; i++) { GenerateMemInit( - headOpnd, offsetStart + i * sizeof(Js::JavascriptArray::MissingItem), - IR::AddrOpnd::New(Js::JavascriptArray::MissingItem, IR::AddrOpndKindConstantAddress, m_func, true), + headOpnd, offsetStart + i * sizeof(double), + GetMissingItemOpndForAssignment(TyFloat64, m_func), instr, isZeroed); } } @@ -4126,9 +4123,9 @@ Lowerer::GenerateProfiledNewScObjArrayFastPath(IR::Instr *instr, Js::ArrayCallSi headOpnd = GenerateArrayObjectsAlloc<Js::JavascriptArray>(instr, &size, arrayInfo, &isZeroed, isNoArgs); for (uint i = 0; i < size; i++) { - GenerateMemInit( + GenerateMemInit( headOpnd, offsetStart + i * sizeof(Js::Var), - IR::AddrOpnd::New(Js::JavascriptArray::MissingItem, IR::AddrOpndKindConstantAddress, m_func, true), + GetMissingItemOpndForAssignment(TyVar, m_func), instr, isZeroed); } } @@ -4159,8 +4156,8 @@ Lowerer::GenerateProfiledNewScObjArrayFastPath(IR::Instr *instr, Js::ArrayCallSi uint allocationBucketsCount = ArrayType::AllocationBucketsCount; uint(*allocationBuckets)[Js::JavascriptArray::AllocationBucketsInfoSize]; allocationBuckets = ArrayType::allocationBuckets; - uint sizeFactor = 1; - IRType missingItemType = (arrayInfo && arrayInfo->IsNativeIntArray()) ? IRType::TyInt32 : IRType::TyVar; + + IRType missingItemType = (arrayInfo ? arrayInfo->IsNativeIntArray() ? IRType::TyInt32 : arrayInfo->IsNativeFloatArray() ? IRType::TyFloat64 : IRType::TyVar : IRType::TyVar); IR::LabelInstr * arrayInitDone = IR::LabelInstr::New(Js::OpCode::Label, func); bool isNativeArray = arrayInfo && (arrayInfo->IsNativeIntArray() || arrayInfo->IsNativeFloatArray()); @@ -4172,9 +4169,7 @@ Lowerer::GenerateProfiledNewScObjArrayFastPath(IR::Instr *instr, Js::ArrayCallSi } else if (arrayInfo && arrayInfo->IsNativeFloatArray()) { - // Js::JavascriptArray::MissingItem is a Var, so it may be 32-bit or 64 bit. - sizeFactor = sizeof(double) / sizeof(Js::JavascriptArray::MissingItem); - sizeOfElement = sizeof(Js::JavascriptArray::MissingItem); + sizeOfElement = sizeof(double); GenerateArrayInfoIsNativeFloatAndNotIntArrayTest(instr, arrayInfo, arrayInfoAddr, helperLabel); } else @@ -4204,7 +4199,7 @@ Lowerer::GenerateProfiledNewScObjArrayFastPath(IR::Instr *instr, Js::ArrayCallSi for (uint8 i = 0;i < allocationBucketsCount;i++) { - missingItemCount = allocationBuckets[i][Js::JavascriptArray::MissingElementsCountIndex] * sizeFactor; + missingItemCount = allocationBuckets[i][Js::JavascriptArray::MissingElementsCountIndex]; if (i > 0) { @@ -4235,7 +4230,7 @@ Lowerer::GenerateProfiledNewScObjArrayFastPath(IR::Instr *instr, Js::ArrayCallSi // Ensure no. of missingItems written are same Assert(missingItemIndex == missingItemInitializedSoFar); // Ensure no. of missingItems match what present in allocationBuckets - Assert(missingItemIndex == allocationBuckets[allocationBucketsCount - 1][Js::JavascriptArray::MissingElementsCountIndex] * sizeFactor); + Assert(missingItemIndex == allocationBuckets[allocationBucketsCount - 1][Js::JavascriptArray::MissingElementsCountIndex]); instr->InsertBefore(arrayInitDone); @@ -4363,11 +4358,11 @@ Lowerer::GenerateProfiledNewScFloatArrayFastPath(IR::Instr *instr, Js::ArrayCall // Js::JavascriptArray::MissingItem is a Var, so it may be 32-bit or 64 bit. uint const offsetStart = sizeof(Js::SparseArraySegmentBase) + doubles->count * sizeof(double); - uint const missingItem = (size - doubles->count) * sizeof(double) / sizeof(Js::JavascriptArray::MissingItem); + uint const missingItem = (size - doubles->count); for (uint i = 0; i < missingItem; i++) { - GenerateMemInit(headOpnd, offsetStart + i * sizeof(Js::JavascriptArray::MissingItem), - IR::AddrOpnd::New(Js::JavascriptArray::MissingItem, IR::AddrOpndKindConstantAddress, m_func, true), instr, isHeadSegmentZeroed); + GenerateMemInit(headOpnd, offsetStart + i * sizeof(double), + GetMissingItemOpndForAssignment(TyFloat64, m_func), instr, isHeadSegmentZeroed); } // Skip pass the helper call IR::LabelInstr * doneLabel = IR::LabelInstr::New(Js::OpCode::Label, func);
lib/Runtime/Library/JavascriptArray.cpp+4 −2 modified@@ -28,7 +28,7 @@ using namespace Js; { 8, 0, 0 }, // allocate space for 8 elements for array of length 6,7,8 }; - const Var JavascriptArray::MissingItem = (Var)FloatMissingItemPattern; + const Var JavascriptArray::MissingItem = (Var)VarMissingItemPattern; #if defined(TARGET_64) const Var JavascriptArray::IntMissingItemVar = (Var)(((uint64)IntMissingItemPattern << 32) | (uint32)IntMissingItemPattern); @@ -2048,6 +2048,8 @@ using namespace Js; { ((SparseArraySegment<Var>*)seg)->elements[i] = JavascriptNumber::ToVar(ival, scriptContext); } + SparseArraySegment<Var>* newSeg = (SparseArraySegment<Var>*)seg; + newSeg->FillSegmentBuffer(seg->length, seg->size); } prevSeg = seg; } @@ -2243,7 +2245,7 @@ using namespace Js; } } } - if (seg == newSeg && shrinkFactor != 1) + if (seg == newSeg) { // Fill the remaining slots. newSeg->FillSegmentBuffer(i, seg->size);
lib/Runtime/Library/JavascriptArray.inl+6 −0 modified@@ -486,6 +486,8 @@ namespace Js template<typename T> inline void JavascriptArray::DirectSetItemInLastUsedSegmentAt(const uint32 offset, const T newValue) { + Assert(!SparseArraySegment<T>::IsMissingItem(&newValue)); + SparseArraySegment<T> *const seg = (SparseArraySegment<T>*)GetLastUsedSegment(); Assert(seg); Assert(offset < seg->size); @@ -526,6 +528,8 @@ namespace Js const T newValue, StElemInfo *const stElemInfo) { + Assert(!SparseArraySegment<T>::IsMissingItem(&newValue)); + SparseArraySegment<T> *const seg = SparseArraySegment<T>::From(head); Assert(seg); Assert(offset < seg->size); @@ -1219,6 +1223,8 @@ SECOND_PASS: template<typename T> void JavascriptArray::DirectSetItem_Full(uint32 itemIndex, T newValue) { + Assert(!SparseArraySegment<T>::IsMissingItem(&newValue)); + DebugOnly(VerifyNotNeedMarshal(newValue)); this->EnsureHead<T>(); AnalysisAssert(head);
lib/Runtime/Library/SparseArraySegment.h+1 −0 modified@@ -147,6 +147,7 @@ namespace Js return JavascriptArray::MissingItem; } template<> Var SparseArraySegment<int32>::GetMissingItemVar(); + Var SparseArraySegment<double>::GetMissingItemVar(); template<> inline bool SparseArraySegment<double>::IsMissingItem(const double* value)
lib/Runtime/Library/SparseArraySegment.inl+6 −0 modified@@ -229,6 +229,12 @@ namespace Js return JavascriptArray::IntMissingItemVar; } + template<> + inline Var SparseArraySegment<double>::GetMissingItemVar() + { + return (Var)FloatMissingItemPattern; + } + template<typename T> void SparseArraySegment<T>::FillSegmentBuffer(uint32 start, uint32 size) {
lib/Runtime/RuntimeCommon.h+3 −0 modified@@ -179,6 +179,9 @@ namespace Js #if FLOATVAR const uint64 FloatTag_Value = 0xFFFCull << 48; + const uint64 VarMissingItemPattern = 0x00040002FFF80002; // Float-tagged representation of FloatMissingItemPattern +#else + const int32 VarMissingItemPattern = 0xFFF80002; #endif const uint64 FloatMissingItemPattern = 0xFFF80002FFF80002; const int32 IntMissingItemPattern = 0xFFF80002;
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-8f4c-h6m3-22rwghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2018-8588ghsaADVISORY
- www.securityfocus.com/bid/105782mitrevdb-entryx_refsource_BID
- www.securitytracker.com/id/1042107mitrevdb-entryx_refsource_SECTRACK
- github.com/chakra-core/ChakraCore/commit/c76da44c27fcf06ad5ac3ee6f8d45efb8d8bc04fghsaWEB
- github.com/chakra-core/ChakraCore/pull/5827ghsaWEB
- portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2018-8588ghsax_refsource_CONFIRMWEB
- web.archive.org/web/20210124214051/http://www.securityfocus.com/bid/105782ghsaWEB
- web.archive.org/web/20211126224439/http://www.securitytracker.com/id/1042107ghsaWEB
News mentions
0No linked articles in our index yet.