CVE-2020-0969
Description
A remote code execution vulnerability exists in the way that the Chakra scripting engine handles objects in memory in Microsoft Edge (HTML-based), aka 'Chakra Scripting Engine Memory Corruption Vulnerability'.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A memory corruption vulnerability in the Chakra scripting engine allows remote code execution via Microsoft Edge (HTML-based).
Vulnerability
CVE-2020-0969 is a remote code execution vulnerability in the way the Chakra scripting engine handles objects in memory in Microsoft Edge (HTML-based) [1][2]. The issue is a memory corruption bug that arises during bytecode generation, specifically in the EmitLoadInstance function where the handling of with statements and scope objects was flawed [4]. The root cause is a missing check or improper handling of object types when loading environment objects and unwrapping with scopes, leading to potential use-after-free or corruption [1].
Exploitation
The vulnerability can be triggered remotely by convincing a user to view a specially crafted web page or document that leverages the Chakra engine. No authentication is required, and the attack surface is through the browser, with the user visiting a malicious site or opening a crafted file [2]. The exploit involves manipulating the JavaScript engine's internal state through carefully constructed with statements or similar constructs to corrupt memory [4].
Impact
Successful exploitation could allow an attacker to execute arbitrary code in the context of the current user. If the user has administrative privileges, the attacker could gain full control of the system, install programs, view, change, or delete data, or create new accounts [2]. The vulnerability is rated as 'Critical' in severity [2].
Mitigation
Microsoft released a security update as part of the April 2020 Patch Tuesday, and the fix is included in ChakraCore pull request #6420 [1][2]. The patch modifies the bytecode generator to correctly handle with scopes and ensure proper register usage and object type assertions [4]. Users should apply the update to Microsoft Edge (HTML-based) or update their ChakraCore library to the patched version. ChakraCore 1.11 continued to receive security updates until March 2021, but master branch fixes were merged for the community [3].
AI Insight generated on May 21, 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.18 | 1.11.18 |
Affected products
23- Microsoft/ChakraCorev5Range: unspecified
- Microsoft/Microsoft Edge (EdgeHTML-based) on Windows 10 for 32-bit Systemsv5Range: unspecified
- Microsoft/Microsoft Edge (EdgeHTML-based) on Windows 10 for x64-based Systemsv5Range: unspecified
- Microsoft/Microsoft Edge (EdgeHTML-based) on Windows 10 Version 1607 for 32-bit Systemsv5Range: unspecified
- Microsoft/Microsoft Edge (EdgeHTML-based) on Windows 10 Version 1607 for x64-based Systemsv5Range: unspecified
- Microsoft/Microsoft Edge (EdgeHTML-based) on Windows 10 Version 1709 for 32-bit Systemsv5Range: unspecified
- Microsoft/Microsoft Edge (EdgeHTML-based) on Windows 10 Version 1709 for ARM64-based Systemsv5Range: unspecified
- Microsoft/Microsoft Edge (EdgeHTML-based) on Windows 10 Version 1709 for x64-based Systemsv5Range: unspecified
- Microsoft/Microsoft Edge (EdgeHTML-based) on Windows 10 Version 1803 for 32-bit Systemsv5Range: unspecified
- Microsoft/Microsoft Edge (EdgeHTML-based) on Windows 10 Version 1803 for ARM64-based Systemsv5Range: unspecified
- Microsoft/Microsoft Edge (EdgeHTML-based) on Windows 10 Version 1803 for x64-based Systemsv5Range: unspecified
- Microsoft/Microsoft Edge (EdgeHTML-based) on Windows 10 Version 1809 for 32-bit Systemsv5Range: unspecified
- Microsoft/Microsoft Edge (EdgeHTML-based) on Windows 10 Version 1809 for ARM64-based Systemsv5Range: unspecified
- Microsoft/Microsoft Edge (EdgeHTML-based) on Windows 10 Version 1809 for x64-based Systemsv5Range: unspecified
- Microsoft/Microsoft Edge (EdgeHTML-based) on Windows 10 Version 1903 for 32-bit Systemsv5Range: unspecified
- Microsoft/Microsoft Edge (EdgeHTML-based) on Windows 10 Version 1903 for ARM64-based Systemsv5Range: unspecified
- Microsoft/Microsoft Edge (EdgeHTML-based) on Windows 10 Version 1903 for x64-based Systemsv5Range: unspecified
- Microsoft/Microsoft Edge (EdgeHTML-based) on Windows 10 Version 1909 for 32-bit Systemsv5Range: unspecified
- Microsoft/Microsoft Edge (EdgeHTML-based) on Windows 10 Version 1909 for ARM64-based Systemsv5Range: unspecified
- Microsoft/Microsoft Edge (EdgeHTML-based) on Windows 10 Version 1909 for x64-based Systemsv5Range: unspecified
- Microsoft/Microsoft Edge (EdgeHTML-based) on Windows Server 2016v5Range: unspecified
- Microsoft/Microsoft Edge (EdgeHTML-based) on Windows Server 2019v5Range: unspecified
Patches
1cd58e8e6799a[CVE-2020-0969]
6 files changed · +53 −18
lib/Runtime/ByteCode/ByteCodeEmitter.cpp+37 −15 modified@@ -4262,21 +4262,33 @@ void ByteCodeGenerator::EmitLoadInstance(Symbol *sym, IdentPtr pid, Js::RegSlot funcInfo->FindOrAddReferencedPropertyId(propertyId), envIndex + Js::FrameDisplay::GetOffsetOfScopes() / sizeof(Js::Var)); - Js::RegSlot tmpReg = funcInfo->AcquireTmpRegister(); - AssertOrFailFast(scope->GetIsObject()); - this->m_writer.SlotI1(Js::OpCode::LdEnvObj, tmpReg, - envIndex + Js::FrameDisplay::GetOffsetOfScopes() / sizeof(Js::Var)); - - Js::OpCode op = unwrapWithObj ? Js::OpCode::UnwrapWithObj : Js::OpCode::Ld_A; - this->m_writer.Reg2(op, instLocation, tmpReg); - if (thisLocation != Js::Constants::NoRegister) + if (unwrapWithObj) { - this->m_writer.Reg2(op, thisLocation, tmpReg); + Js::RegSlot tmpReg = funcInfo->AcquireTmpRegister(); + + this->m_writer.SlotI1(Js::OpCode::LdEnvObj, tmpReg, + envIndex + Js::FrameDisplay::GetOffsetOfScopes() / sizeof(Js::Var)); + + this->m_writer.Reg2(Js::OpCode::UnwrapWithObj, instLocation, tmpReg); + if (thisLocation != Js::Constants::NoRegister) + { + this->m_writer.Reg2(Js::OpCode::UnwrapWithObj, thisLocation, tmpReg); + } + + funcInfo->ReleaseTmpRegister(tmpReg); } + else + { + this->m_writer.SlotI1(Js::OpCode::LdEnvObj, instLocation, + envIndex + Js::FrameDisplay::GetOffsetOfScopes() / sizeof(Js::Var)); - funcInfo->ReleaseTmpRegister(tmpReg); + if (thisLocation != Js::Constants::NoRegister) + { + this->m_writer.Reg2(Js::OpCode::Ld_A, thisLocation, funcInfo->undefinedConstantRegister); + } + } } else if (scopeLocation != Js::Constants::NoRegister && scopeLocation == funcInfo->frameObjRegister) { @@ -4288,19 +4300,29 @@ void ByteCodeGenerator::EmitLoadInstance(Symbol *sym, IdentPtr pid, Js::RegSlot this->m_writer.Reg1(Js::OpCode::LdLocalObj, instLocation); if (thisLocation != Js::Constants::NoRegister) { - this->m_writer.Reg1(Js::OpCode::LdLocalObj, thisLocation); + this->m_writer.Reg2(Js::OpCode::Ld_A, thisLocation, funcInfo->undefinedConstantRegister); } } else { this->m_writer.BrProperty(Js::OpCode::BrOnNoProperty, nextLabel, scopeLocation, funcInfo->FindOrAddReferencedPropertyId(propertyId)); - Js::OpCode op = unwrapWithObj ? Js::OpCode::UnwrapWithObj : Js::OpCode::Ld_A; - this->m_writer.Reg2(op, instLocation, scopeLocation); - if (thisLocation != Js::Constants::NoRegister) + if (unwrapWithObj) { - this->m_writer.Reg2(op, thisLocation, scopeLocation); + this->m_writer.Reg2(Js::OpCode::UnwrapWithObj, instLocation, scopeLocation); + if (thisLocation != Js::Constants::NoRegister) + { + this->m_writer.Reg2(Js::OpCode::UnwrapWithObj, thisLocation, scopeLocation); + } + } + else + { + this->m_writer.Reg2(Js::OpCode::Ld_A, instLocation, scopeLocation); + if (thisLocation != Js::Constants::NoRegister) + { + this->m_writer.Reg2(Js::OpCode::Ld_A, thisLocation, funcInfo->undefinedConstantRegister); + } } }
lib/Runtime/Language/JavascriptOperators.cpp+1 −2 modified@@ -2072,8 +2072,7 @@ using namespace Js; // HasProperty will call UnscopablesWrapperObject's HasProperty which will do the filtering // All we have to do here is unwrap the object hence the api call - *thisVar = obj->GetThisObjectOrUnWrap(); - return *thisVar; + return obj->GetThisAndUnwrappedInstance(thisVar); } }
lib/Runtime/Types/RecyclableObject.cpp+6 −0 modified@@ -331,6 +331,12 @@ namespace Js return this; } + RecyclableObject* RecyclableObject::GetThisAndUnwrappedInstance(Var* thisVar) const + { + *thisVar = this->GetLibrary()->GetUndefined(); + return (RecyclableObject*)this; + } + // In order to avoid a branch, every object has an entry point if it gets called like a // function - however, if it can't be called like a function, it's set to DefaultEntryPoint // which will emit an error.
lib/Runtime/Types/RecyclableObject.h+1 −0 modified@@ -353,6 +353,7 @@ namespace Js { virtual uint GetSpecialPropertyCount() const { return 0; } virtual PropertyId const * GetSpecialPropertyIds() const { return nullptr; } virtual RecyclableObject* GetThisObjectOrUnWrap(); // Due to the withScope object there are times we need to unwrap + virtual RecyclableObject* GetThisAndUnwrappedInstance(Var* thisVar) const; virtual BOOL HasInstance(Var instance, ScriptContext* scriptContext, IsInstInlineCache* inlineCache = NULL);
lib/Runtime/Types/UnscopablesWrapperObject.cpp+6 −0 modified@@ -23,6 +23,12 @@ namespace Js return static_cast<UnscopablesWrapperObject*>(aValue); } + RecyclableObject * UnscopablesWrapperObject::GetThisAndUnwrappedInstance(Var* thisVar) const + { + *thisVar = this->GetWrappedObject(); + return this->GetWrappedObject(); + } + PropertyQueryFlags UnscopablesWrapperObject::HasPropertyQuery(PropertyId propertyId, _Inout_opt_ PropertyValueInfo* info) { return JavascriptConversion::BooleanToPropertyQueryFlags(JavascriptOperators::HasPropertyUnscopables(wrappedObject, propertyId));
lib/Runtime/Types/UnscopablesWrapperObject.h+2 −1 modified@@ -28,7 +28,8 @@ namespace Js static bool Is(Var aValue); static UnscopablesWrapperObject* FromVar(Var value); static UnscopablesWrapperObject* UnsafeFromVar(Var value); - RecyclableObject *GetWrappedObject() { return wrappedObject; } + RecyclableObject *GetWrappedObject() const { return wrappedObject; } + virtual RecyclableObject* GetThisAndUnwrappedInstance(Var* thisVar) const override; virtual PropertyQueryFlags HasPropertyQuery(PropertyId propertyId, _Inout_opt_ PropertyValueInfo* info) override; virtual BOOL HasOwnProperty(PropertyId propertyId) override; virtual BOOL SetProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info) override;
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-jr84-p554-62pmghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2020-0969ghsaADVISORY
- github.com/chakra-core/ChakraCore/commit/cd58e8e6799ab11b02a1cfc30bac9a2171dabd4dghsaWEB
- github.com/chakra-core/ChakraCore/pull/6420ghsaWEB
- portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-0969ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.