CVE-2016-7240
Description
The Chakra JavaScript scripting engine in Microsoft Edge allows remote attackers to execute arbitrary code or cause a denial of service (memory corruption) via a crafted web site, aka "Scripting Engine Memory Corruption Vulnerability," a different vulnerability than CVE-2016-7200, CVE-2016-7201, CVE-2016-7202, CVE-2016-7203, CVE-2016-7208, CVE-2016-7242, and CVE-2016-7243.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Memory corruption in Microsoft Edge's Chakra engine allows remote code execution or denial of service via crafted website.
Vulnerability
The vulnerability is a memory corruption issue in the Chakra JavaScript scripting engine within Microsoft Edge. It occurs when the engine improperly handles objects in memory, as addressed by the fix in commit c2787ef [2]. Affected software includes Microsoft Edge on Windows 10 (various versions) and Windows Server 2016 [1]. The vulnerability is distinct from several similar CVEs (CVE-2016-7200, etc.) [1][3].
Exploitation
An attacker can exploit this by hosting a specially crafted website and persuading a user to view it (e.g., via email or social engineering). No authentication is required, and the attacker does not need any special network position beyond internet access [1][4]. The user interaction is limited to simply opening the malicious site.
Impact
Successful exploitation allows arbitrary code execution with the same user rights as the current user, or can cause a denial of service due to memory corruption. An attacker could install programs, view/change/delete data, or create new accounts [1][4]. Systems where the user has fewer privileges are less impacted.
Mitigation
Microsoft released security update MS16-129 (KB 3199057) on November 8, 2016, which addresses this vulnerability [1]. Users should apply the update via Windows Update or other deployment methods. No workarounds are provided; the only mitigation is to install the patch.
- Microsoft Security Bulletin MS16-129 - Critical
- Change to address CVE-2016-7200,CVE-2016-7201,CVE-2016-7202,CVE-2016-… · chakra-core/ChakraCore@c2787ef
- NVD - CVE-2016-7240
- Microsoft Edge Multiple Flaws Let Remote Users Obtain Potentially Sensitive Information, Access Files, Spoof Content, and Execute Arbitrary Code
AI Insight generated on May 23, 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.2.2 | 1.2.2 |
Affected products
3Patches
1c2787ef8fdb7Change to address CVE-2016-7200,CVE-2016-7201,CVE-2016-7202,CVE-2016-7203,CVE-2016-7208,CVE-2016-7240,CVE-2016-7241,CVE-2016-7242,CVE-2016-7243
7 files changed · +274 −27
lib/Runtime/Base/CallInfo.h+10 −0 modified@@ -65,6 +65,16 @@ namespace Js static const ushort ksizeofCount; static const ushort ksizeofCallFlags; static const uint kMaxCountArgs; + + static bool isDirectEvalCall(CallFlags flags) + { + // This was recognized as an eval call at compile time. The last one or two args are internal to us. + // Argcount will be one of the following when called from global code + // - eval("...") : argcount 3 : this, evalString, frameDisplay + // - eval.call("..."): argcount 2 : this(which is string) , frameDisplay + + return (flags & (CallFlags_ExtraArg | CallFlags_NewTarget)) == CallFlags_ExtraArg; // ExtraArg == 1 && NewTarget == 0 + } }; struct InlineeCallInfo
lib/Runtime/Library/GlobalObject.cpp+1 −1 modified@@ -491,7 +491,7 @@ namespace Js // TODO: Handle call from global scope, strict mode BOOL isIndirect = FALSE; - if (args.Info.Flags & CallFlags_ExtraArg) + if (Js::CallInfo::isDirectEvalCall(args.Info.Flags)) { // This was recognized as an eval call at compile time. The last one or two args are internal to us. // Argcount will be one of the following when called from global code
lib/Runtime/Library/JavascriptArray.cpp+72 −20 modified@@ -714,6 +714,40 @@ namespace Js return IsMissingHeadSegmentItemImpl<double>(index); } + template<typename T> + void JavascriptArray::InternalFillFromPrototype(JavascriptArray *dstArray, const T& dstIndex, JavascriptArray *srcArray, uint32 start, uint32 end, uint32 count) + { + RecyclableObject* prototype = srcArray->GetPrototype(); + while (start + count != end && JavascriptOperators::GetTypeId(prototype) != TypeIds_Null) + { + ForEachOwnMissingArrayIndexOfObject(srcArray, dstArray, prototype, start, end, dstIndex, [&](uint32 index, Var value) { + T n = dstIndex + (index - start); + dstArray->DirectSetItemAt(n, value); + + count++; + }); + + prototype = prototype->GetPrototype(); + } + } + + template<> + void JavascriptArray::InternalFillFromPrototype<uint32>(JavascriptArray *dstArray, const uint32& dstIndex, JavascriptArray *srcArray, uint32 start, uint32 end, uint32 count) + { + RecyclableObject* prototype = srcArray->GetPrototype(); + while (start + count != end && JavascriptOperators::GetTypeId(prototype) != TypeIds_Null) + { + ForEachOwnMissingArrayIndexOfObject(srcArray, dstArray, prototype, start, end, dstIndex, [&](uint32 index, Var value) { + uint32 n = dstIndex + (index - start); + dstArray->SetItem(n, value, PropertyOperation_None); + + count++; + }); + + prototype = prototype->GetPrototype(); + } + } + /* static */ bool JavascriptArray::HasInlineHeadSegment(uint32 length) { @@ -3308,6 +3342,9 @@ namespace Js pDestObj = ArraySpeciesCreate(args[0], 0, scriptContext); if (pDestObj) { +#if ENABLE_COPYONACCESS_ARRAY + JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(pDestObj); +#endif // Check the thing that species create made. If it's a native array that can't handle the source // data, convert it. If it's a more conservative kind of array than the source data, indicate that // so that the data will be converted on copy. @@ -5863,13 +5900,19 @@ namespace Js } newArr = CreateNewArrayHelper(static_cast<uint32>(newLenT), isIntArray, isFloatArray, pArr, scriptContext); +#if ENABLE_COPYONACCESS_ARRAY + JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(newArr); +#endif newObj = newArr; } else { // If the new object we created is an array, remember that as it will save us time setting properties in the object below if (JavascriptArray::Is(newObj)) { +#if ENABLE_COPYONACCESS_ARRAY + JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(newObj); +#endif newArr = JavascriptArray::FromVar(newObj); } } @@ -6649,6 +6692,9 @@ namespace Js // If the new object we created is an array, remember that as it will save us time setting properties in the object below if (JavascriptArray::Is(newObj)) { +#if ENABLE_COPYONACCESS_ARRAY + JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(newObj); +#endif newArr = JavascriptArray::FromVar(newObj); } } @@ -6657,10 +6703,13 @@ namespace Js { pArr->GetArrayTypeAndConvert(&isIntArray, &isFloatArray); newArr = CreateNewArrayHelper(deleteLen, isIntArray, isFloatArray, pArr, scriptContext); +#if ENABLE_COPYONACCESS_ARRAY + JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(newArr); +#endif } // If return object is a JavascriptArray, we can use all the array splice helpers - if (newArr && isBuiltinArrayCtor) + if (newArr && isBuiltinArrayCtor && len == pArr->length) { // Array has a single segment (need not start at 0) and splice start lies in the range @@ -7151,6 +7200,9 @@ namespace Js if (JavascriptArray::Is(pNewObj)) { +#if ENABLE_COPYONACCESS_ARRAY + JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(pNewObj); +#endif pnewArr = JavascriptArray::FromVar(pNewObj); } @@ -8924,6 +8976,9 @@ namespace Js // If the new object we created is an array, remember that as it will save us time setting properties in the object below if (JavascriptArray::Is(newObj)) { +#if ENABLE_COPYONACCESS_ARRAY + JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(newObj); +#endif newArr = JavascriptArray::FromVar(newObj); } } @@ -9123,7 +9178,8 @@ namespace Js } // If the source object is an Array exotic object we should try to load the constructor property and use it to construct the return object. - RecyclableObject* newObj = ArraySpeciesCreate(obj, 0, scriptContext); + bool isBuiltinArrayCtor = true; + RecyclableObject* newObj = ArraySpeciesCreate(obj, 0, scriptContext, nullptr, nullptr, &isBuiltinArrayCtor); JavascriptArray* newArr = nullptr; if (newObj == nullptr) @@ -9137,6 +9193,9 @@ namespace Js // If the new object we created is an array, remember that as it will save us time setting properties in the object below if (JavascriptArray::Is(newObj)) { +#if ENABLE_COPYONACCESS_ARRAY + JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(newObj); +#endif newArr = JavascriptArray::FromVar(newObj); } } @@ -9164,7 +9223,7 @@ namespace Js if (JavascriptConversion::ToBoolean(selected, scriptContext)) { // Try to fast path if the return object is an array - if (newArr) + if (newArr && isBuiltinArrayCtor) { newArr->DirectSetItemAt(i, element); } @@ -10041,6 +10100,7 @@ namespace Js { if (JavascriptArray::Is(arr)) { + arr = EnsureNonNativeArray(arr); ArrayElementEnumerator e(arr, startIndex, limitIndex); while(e.MoveNext<Var>()) @@ -10096,6 +10156,7 @@ namespace Js { if (JavascriptArray::Is(arr)) { + arr = EnsureNonNativeArray(arr); ArrayElementEnumerator e(arr, startIndex, limitIndex); while(e.MoveNext<Var>()) @@ -10849,6 +10910,9 @@ namespace Js JavascriptArray *JavascriptArray::EnsureNonNativeArray(JavascriptArray *arr) { +#if ENABLE_COPYONACCESS_ARRAY + JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(arr); +#endif if (JavascriptNativeIntArray::Is(arr)) { arr = JavascriptNativeIntArray::ToVarArray((JavascriptNativeIntArray*)arr); @@ -10931,23 +10995,6 @@ namespace Js } } - template<typename T> - void JavascriptArray::InternalFillFromPrototype(JavascriptArray *dstArray, const T& dstIndex, JavascriptArray *srcArray, uint32 start, uint32 end, uint32 count) - { - RecyclableObject* prototype = srcArray->GetPrototype(); - while (start + count != end && JavascriptOperators::GetTypeId(prototype) != TypeIds_Null) - { - ForEachOwnMissingArrayIndexOfObject(srcArray, dstArray, prototype, start, end, dstIndex, [&](uint32 index, Var value) { - T n = dstIndex + (index - start); - dstArray->DirectSetItemAt(n, value); - - count++; - }); - - prototype = prototype->GetPrototype(); - } - } - Var JavascriptArray::SpreadArrayArgs(Var arrayToSpread, const Js::AuxArray<uint32> *spreadIndices, ScriptContext *scriptContext) { // At this stage we have an array literal with some arguments to be spread. @@ -11458,6 +11505,11 @@ namespace Js { if (!JavascriptOperators::GetProperty((RecyclableObject*)constructor, PropertyIds::_symbolSpecies, &constructor, scriptContext)) { + if (pIsBuiltinArrayCtor != nullptr) + { + *pIsBuiltinArrayCtor = false; + } + return nullptr; } if (constructor == scriptContext->GetLibrary()->GetNull())
lib/Runtime/Library/JavascriptPromise.cpp+1 −1 modified@@ -910,7 +910,7 @@ namespace Js try { - values->DirectSetItemAt(index, x); + values->SetItem(index, x, PropertyOperation_None); } catch (JavascriptExceptionObject* e) {
lib/Runtime/Library/JSONParser.cpp+1 −2 modified@@ -83,8 +83,7 @@ namespace JSON // this is a post order walk. Visit the children before calling walk on this object if (Js::DynamicObject::IsAnyArray(value)) { - Js::JavascriptArray* arrayVal = Js::JavascriptArray::FromAnyArray(value); - // REVIEW: How do we guarantee that JSON objects are not native arrays? + Js::JavascriptArray* arrayVal = JavascriptArray::EnsureNonNativeArray(Js::JavascriptArray::FromAnyArray(value)); Assert(!Js::JavascriptNativeIntArray::Is(arrayVal) && !Js::JavascriptNativeFloatArray::Is(arrayVal)); uint length = arrayVal->GetLength(); if (!arrayVal->IsCrossSiteObject())
test/Array/Array_TypeConfusion_bugs.js+181 −3 modified@@ -9,11 +9,19 @@ if (this.WScript && this.WScript.LoadScriptFile) { // Check for running in ch this.WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js"); } +var restorePropertyFromDescriptor = function (obj, prop, desc) { + if (typeof desc == 'undefined') { + delete obj[prop]; + } else { + Object.defineProperty(obj, prop, desc); + } +} + var tests = [ { - name: "OS7342663:OOB writes using type confusion in InternalCopyArrayElements", - body: function () - { + name: "OS7342663:OOB writes using type confusion in InternalCopyArrayElements", + body: function () + { function test() { var arr1 = [0xdead, 0xbabe, 0xdead, 0xbabe]; @@ -85,6 +93,7 @@ var tests = [ { function test() { var arr = [1, 2] + var desc = Object.getOwnPropertyDescriptor(arr.constructor, Symbol.species); //Our species function will get called during chakra!Js::JavascriptArray::SliceHelper<unsigned int> Object.defineProperty( @@ -108,6 +117,8 @@ var tests = [ assert.areEqual(2, brr.length, "brr.length == 2"); assert.areEqual(WScript, brr[0], "brr[0] == WScript"); assert.areEqual(2, brr[1], "brr[0] == WScript"); + + restorePropertyFromDescriptor(arr.constructor, Symbol.species, desc); } test(); test(); @@ -169,6 +180,7 @@ var tests = [ { function test() { var arr = [ 1, 2 ]; + var desc = Object.getOwnPropertyDescriptor(arr.constructor, Symbol.species); Object.defineProperty( arr.constructor, @@ -192,6 +204,8 @@ var tests = [ assert.areEqual(2, brr.length, "brr.length == 2"); assert.areEqual(WScript, brr[0], "brr[0] == WScript"); assert.areEqual(undefined, brr[1], "brr[1] == undefined"); + + restorePropertyFromDescriptor(arr.constructor, Symbol.species, desc); } test(); test(); @@ -246,6 +260,7 @@ var tests = [ function test() { //create a TypeIds_Array holding two 64 bit values (The same amount of space for four 32 bit values). var arr = [ WScript, WScript ]; + var desc = Object.getOwnPropertyDescriptor(arr.constructor, Symbol.species); //Our species function will get called during chakra!Js::JavascriptArray::EntrySplice Object.defineProperty( @@ -270,6 +285,8 @@ var tests = [ assert.areEqual(WScript, brr[1], "brr[1] == WScript"); assert.areEqual(undefined, brr[2], "brr[2] == undefined"); assert.areEqual(undefined, brr[3], "brr[3] == undefined"); + + restorePropertyFromDescriptor(arr.constructor, Symbol.species, desc); } test(); test(); @@ -396,5 +413,166 @@ var tests = [ test(); } }, + { + name: "[MSRC34910] type confusion in Array.prototype.filter", + body: function () + { + function mappingFn(elem, index, arr) { + arr[1] = 'hello'; + return true; + } + + var arr = [1, 2, 3]; + + var desc = Object.getOwnPropertyDescriptor(arr.constructor, Symbol.species); + Object.defineProperty(arr.constructor, Symbol.species, { get : function () { return function() { return [22, 33]; } } } ); + var b = Array.prototype.filter.call(arr, mappingFn); + assert.areEqual('hello', b[1]); + + restorePropertyFromDescriptor(arr.constructor, Symbol.species, desc); + } + }, + { + name: "[MSRC35046] heap overflow in Array.prototype.splice", + body: function () + { + var a = []; + var o = {}; + + Object.defineProperty(o, 'constructor', { + get: function() { + a.length = 0xfffffffe; + [].fill.call(a, 0, 0xfffff000, 0xfffffffe); + return Array; + }}); + + a.__proto__ = o; + var q = new Array(50).fill(1.1); + + var b = [].splice.call(a, 0, 0, ...q); + assert.areEqual(50, a.length); + assert.areEqual(q, a); + } + }, + { + name: "[MSRC35086] type confusion in FillFromPrototypes", + body: function () + { + var a = new Array(0x11111111, 0x22222222, 0x33333333, 0x44444444, 0x12121212); + + var handler = { + getPrototypeOf: function(target, name) { + return a; + } + }; + + var p = new Proxy([], handler); + var b = [{}, [], "abc"]; + + b.__proto__ = p; + b.length = 4; + var c = [[],"abc",1145324612]; + + a.shift.call(b); + assert.areEqual(3, b.length); + assert.areEqual([], b[0]); + assert.areEqual("abc", b[1]); + assert.areEqual(1145324612, b[2]); + } + }, + { + name: "[MSRC35272] type confusion in JSON.parse", + body: function () + { + var a = 1; + var once = false; + function f(){ + if(!once){ + a = new Array(2) + this[2] = a; + } + once = true; + return 0x41414141; + } + + var r = JSON.parse("[1111, 22222, 333333]", f); + assert.areEqual(0x41414141, r); + } + }, + { + name: "[MSRC35383] type confusion in Array.prototype.concat", + body: function () + { + var n = []; + for (var i = 0; i < 0x10; i++) + n.push([0x11111111, 0x11111111, 0, 0x11111111,0x11111111, 0x11111111, 0, 0x11111111,0x11111111, 0x11111111, 0, 0x11111111,0x11111111,0x11111111, 0, 0x11111111,0x11111111 ,1 ,2 ,3 ,4]); + + class fake extends Object { + static get [Symbol.species]() { return function() { return n[3]; }; }; + } + + var f = function(a){ return a; } + + var x = ["dabao", 0, 0, 0x41414141]; + var y = new Proxy(x, { + get: function(t, p, r) { + return (p == "constructor") ? fake : x[p]; + } + }); + + assert.areEqual(x, Array.prototype.concat.apply(y)); + } + }, + { + name: "[MSRC35389] type confusion in Array.prototype.splice", + body: function () + { + var arr = [0x41424344,0x41424344,0x41424344,0x41424344,0x41424344,0x41424344,0x41424344,0x41424344,0x41424344] + class fake extends Object { + static get [Symbol.species]() { return function() { + return arr; + }; }; + } + + var x = [0, 2, 0, 0x41414141]; + var y = new Proxy(x, { + get: function(t, p, r) { + return (p == "constructor") ? fake : x[p]; + } + }); + + Array.prototype.splice.apply(y); + assert.areEqual(x, y); + } + }, + { + name: "[MSRC35389 variation] type confusion in Array.prototype.slice", + body: function () + { + var arr = [0x41424344,0x41424344,0x41424344,0x41424344,0x41424344,0x41424344,0x41424344,0x41424344,0x41424344]; + + class fake extends Object { + static get [Symbol.species]() { return function() { + return arr; + }; }; + } + + var x = [0, 2, 0, 0x41414141]; + var y = new Proxy(x, { + get: function(t, p, r) { + if (p == "constructor") + return fake + if (p == 'length'); + return 1; + return x[p]; + }, + has: function() { + return false; + } + }); + + assert.areEqual([0x41424344], Array.prototype.slice.call(y)); + } + }, ]; testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });
test/es6/ES6NewTarget_bugfixes.js+8 −0 modified@@ -25,6 +25,14 @@ var tests = [ new.target; // bug repro: SyntaxError: Invalid use of the 'new.target' keyword } }, + { + name: "[MSRC35208] parameter type confusion in eval", + body: function () + { + var proxy = new Proxy(eval, {}); + assert.areEqual(0, proxy("Math.sin(0)")); + } + }, ]; testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
11- github.com/advisories/GHSA-hh3v-5chw-wgh7ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2016-7240ghsaADVISORY
- docs.microsoft.com/en-us/security-updates/securitybulletins/2016/ms16-129nvdWEB
- github.com/chakra-core/ChakraCore/commit/c2787ef8fdb7401922e9ec6540e4e5895d11c631ghsaWEB
- github.com/chakra-core/ChakraCore/pull/1982ghsaWEB
- web.archive.org/web/20210803102008/http://www.securityfocus.com/bid/94046ghsaWEB
- web.archive.org/web/20211126224744/http://www.securitytracker.com/id/1037245ghsaWEB
- www.exploit-db.com/exploits/40773ghsaWEB
- www.securityfocus.com/bid/94046nvd
- www.securitytracker.com/id/1037245nvd
- www.exploit-db.com/exploits/40773/nvd
News mentions
0No linked articles in our index yet.