VYPR
High severity8.8CISA KEVNVD Advisory· Published Nov 10, 2016· Updated Apr 22, 2026

CVE-2016-7200

CVE-2016-7200

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-7201, CVE-2016-7202, CVE-2016-7203, CVE-2016-7208, CVE-2016-7240, CVE-2016-7242, and CVE-2016-7243.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
Microsoft.ChakraCoreNuGet
< 1.2.21.2.2

Affected products

1

Patches

1
c2787ef8fdb7

Change 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

https://github.com/chakra-core/ChakraCoreSuwei ChenSep 27, 2016via ghsa
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 by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

15

News mentions

0

No linked articles in our index yet.