VYPR
High severityNVD Advisory· Published Apr 12, 2018· Updated Aug 5, 2024

CVE-2018-0995

CVE-2018-0995

Description

A memory corruption vulnerability in ChakraCore and Microsoft Edge allows remote code execution via crafted JavaScript.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

A memory corruption vulnerability in ChakraCore and Microsoft Edge allows remote code execution via crafted JavaScript.

Vulnerability

A remote code execution vulnerability exists in the way that the Chakra scripting engine handles objects in memory in Microsoft Edge, also known as a Chakra Scripting Engine Memory Corruption Vulnerability [1]. This issue affects Microsoft Edge and ChakraCore [1]. The vulnerability is a memory corruption flaw that occurs when the engine processes specially crafted JavaScript code, specifically during the conversion of JavascriptNativeIntArray to a Var array, as seen in the patch that moves the fill operation before the segment replacement to prevent use-after-free [2]. Affected versions include ChakraCore and Microsoft Edge on impacted Windows platforms [3].

Exploitation

An attacker can exploit this vulnerability by hosting a specially crafted website (or leveraging a compromised site) that contains malicious JavaScript code, then convincing a user to visit that site in Microsoft Edge, or by embedding the code in an application that uses ChakraCore [1][3]. No authentication is required. The user interaction is limited to visiting the malicious page. The vulnerability lies in the failure to handle exceptional conditions, specifically a timing issue during array conversion that leads to memory corruption [2][3].

Impact

Successful exploitation allows an attacker to execute arbitrary code in the context of the current user [1]. If the user has administrative privileges, the attacker can gain complete control of the system, install programs, view/change/delete data, or create new accounts [1]. The attacker achieves remote code execution (RCE) with the privilege level of the victim user [1].

Mitigation

Microsoft released security updates on April 10, 2018, to address this vulnerability [3]. For ChakraCore, the fix is included in commit 396d78f60a3d8444218253523365965f97942225 [2]. Users should apply the latest updates from Microsoft Update or Windows Update [3]. For ChakraCore, updating to a version that includes the fix is necessary [2]. The vulnerability is not listed as KEV (Known Exploited Vulnerabilities) catalog.

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.

PackageAffected versionsPatched versions
Microsoft.ChakraCoreNuGet
< 1.8.31.8.3

Affected products

3

Patches

1
396d78f60a3d

[CVE-2018-0995] Edge - JavascriptNativeIntArray::ConvertToVarArray could lead to UAF - Individual

https://github.com/chakra-core/ChakraCoreJianchun XuFeb 16, 2018via ghsa
1 file changed · +39 31
  • lib/Runtime/Library/JavascriptArray.cpp+39 31 modified
    @@ -1921,8 +1921,21 @@ namespace Js
                     // And/or the old segment is not scanned by the recycler, so we need a new one to hold vars.
                     SparseArraySegment<Var> *newSeg =
                         SparseArraySegment<Var>::AllocateSegment(recycler, left, length, nextSeg);
    -
                     AnalysisAssert(newSeg);
    +
    +                // Fill the new segment with the overflow.
    +                for (i = 0; (uint)i < newSeg->length; i++)
    +                {
    +                    ival = ((SparseArraySegment<int32>*)seg)->elements[i];
    +                    if (ival == JavascriptNativeIntArray::MissingItem)
    +                    {
    +                        continue;
    +                    }
    +                    newSeg->elements[i] = JavascriptNumber::ToVar(ival, scriptContext);
    +                }
    +
    +                // seg elements are copied over, now it is safe to replace seg with newSeg.
    +                // seg could be GC collected if replaced by newSeg.
                     Assert((prevSeg == nullptr) == (seg == intArray->head));
                     newSeg->next = nextSeg;
                     intArray->LinkSegments((SparseArraySegment<Var>*)prevSeg, newSeg);
    @@ -1937,17 +1950,6 @@ namespace Js
                     {
                         segmentMap->SwapSegment(left, seg, newSeg);
                     }
    -
    -                // Fill the new segment with the overflow.
    -                for (i = 0; (uint)i < newSeg->length; i++)
    -                {
    -                    ival = ((SparseArraySegment<int32>*)seg)->elements[i];
    -                    if (ival == JavascriptNativeIntArray::MissingItem)
    -                    {
    -                        continue;
    -                    }
    -                    newSeg->elements[i] = JavascriptNumber::ToVar(ival, scriptContext);
    -                }
                 }
                 else
                 {
    @@ -2097,26 +2099,12 @@ namespace Js
                 }
                 uint32 left = seg->left;
                 uint32 length = seg->length;
    -            SparseArraySegment<Var> *newSeg;
    +            SparseArraySegment<Var> *newSeg = nullptr;
                 if (seg->next == nullptr && SparseArraySegmentBase::IsLeafSegment(seg, recycler))
                 {
                     // The old segment is not scanned by the recycler, so we need a new one to hold vars.
                     newSeg =
                         SparseArraySegment<Var>::AllocateSegment(recycler, left, length, nextSeg);
    -                Assert((prevSeg == nullptr) == (seg == fArray->head));
    -                newSeg->next = nextSeg;
    -                fArray->LinkSegments((SparseArraySegment<Var>*)prevSeg, newSeg);
    -                if (fArray->GetLastUsedSegment() == seg)
    -                {
    -                    fArray->SetLastUsedSegment(newSeg);
    -                }
    -                prevSeg = newSeg;
    -
    -                SegmentBTree * segmentMap = fArray->GetSegmentMap();
    -                if (segmentMap)
    -                {
    -                    segmentMap->SwapSegment(left, seg, newSeg);
    -                }
                 }
                 else
                 {
    @@ -2172,6 +2160,26 @@ namespace Js
                     // Fill the remaining slots.
                     newSeg->FillSegmentBuffer(i, seg->size);
                 }
    +
    +            // seg elements are copied over, now it is safe to replace seg with newSeg.
    +            // seg could be GC collected if replaced by newSeg.
    +            if (newSeg != seg)
    +            {
    +                Assert((prevSeg == nullptr) == (seg == fArray->head));
    +                newSeg->next = nextSeg;
    +                fArray->LinkSegments((SparseArraySegment<Var>*)prevSeg, newSeg);
    +                if (fArray->GetLastUsedSegment() == seg)
    +                {
    +                    fArray->SetLastUsedSegment(newSeg);
    +                }
    +                prevSeg = newSeg;
    +
    +                SegmentBTree * segmentMap = fArray->GetSegmentMap();
    +                if (segmentMap)
    +                {
    +                    segmentMap->SwapSegment(left, seg, newSeg);
    +                }
    +            }
             }
     
             if (fArray->GetType() == scriptContext->GetLibrary()->GetNativeFloatArrayType())
    @@ -3140,7 +3148,7 @@ namespace Js
                         JS_REENTRANT_NO_MUTATE(jsReentLock, CopyNativeIntArrayElementsToVar(pDestArray, BigIndex(idxDest).GetSmallIndex(), pIntItemArray));
                         idxDest = idxDest + pIntItemArray->length;
                     }
    -                else 
    +                else
                     {
                         JavascriptNativeFloatArray *pFloatItemArray = JavascriptOperators::TryFromVar<JavascriptNativeFloatArray>(aItem);
                         if (pFloatItemArray)
    @@ -3390,7 +3398,7 @@ namespace Js
     
                         idxDest = idxDest + pIntItemArray->length;
                     }
    -                else 
    +                else
                     {
                         JavascriptNativeFloatArray * pFloatItemArray = JavascriptOperators::TryFromVar<JavascriptNativeFloatArray>(aItem);
                         if (pFloatItemArray && !isFillFromPrototypes)
    @@ -5384,7 +5392,7 @@ namespace Js
                 {
                     RecyclableObject* protoObj = prototype;
     
    -                if (!(DynamicObject::IsAnyArray(protoObj) || JavascriptOperators::IsObject(protoObj)) 
    +                if (!(DynamicObject::IsAnyArray(protoObj) || JavascriptOperators::IsObject(protoObj))
                         || JavascriptProxy::Is(protoObj)
                         || protoObj->IsExternal())
                     {
    @@ -6099,7 +6107,7 @@ namespace Js
                 *isIntArray = true;
     #endif
             }
    -        else 
    +        else
             {
                 JavascriptNativeFloatArray* nativeFloatArray = JavascriptOperators::TryFromVar<JavascriptNativeFloatArray>(this);
                 if (nativeFloatArray)
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

8

News mentions

0

No linked articles in our index yet.