High severity7.5NVD Advisory· Published Sep 14, 2016· Updated May 6, 2026
CVE-2016-3377
CVE-2016-3377
Description
The Chakra JavaScript 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-3350.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
Microsoft.ChakraCoreNuGet | < 1.2.1 | 1.2.1 |
Patches
124c4d7df8199This change contains combined fixes for CVE-2016-3350, CVE-2016-3377 and a defense in depth change in the CustomHeap
10 files changed · +443 −49
lib/Common/DataStructures/UnitBitVector.h+5 −0 modified@@ -287,6 +287,11 @@ class BVUnitT T mask = ((T)AllOnesMask) >> (BitsPerWord - length) << index; return (this->word & mask) == mask; } + BOOLEAN TestAnyInRange(const BVIndex index, uint length) const + { + T mask = ((T)AllOnesMask) >> (BitsPerWord - length) << index; + return (this->word & mask) != 0; + } void SetRange(const BVIndex index, uint length) { T mask = ((T)AllOnesMask) >> (BitsPerWord - length) << index;
lib/Common/Memory/CustomHeap.cpp+53 −32 modified@@ -485,7 +485,7 @@ void Heap::FreeLargeObjects() #endif this->codePageAllocators->Release(allocation.address, allocation.GetPageCount(), allocation.largeObjectAllocation.segment); - largeObjectIter.RemoveCurrent(this->auxiliaryAllocator); + largeObjectIter.RemoveCurrent(this->auxiliaryAllocator); } NEXT_DLISTBASE_ENTRY_EDITING; } @@ -519,7 +519,13 @@ bool Heap::AllocInPage(Page* page, size_t bytes, ushort pdataCount, ushort xdata uint length = GetChunkSizeForBytes(bytes); BVIndex index = GetFreeIndexForPage(page, bytes); - Assert(index != BVInvalidIndex); + + if (index == BVInvalidIndex) + { + CustomHeap_BadPageState_fatal_error((ULONG_PTR)this); + return false; + } + char* address = page->address + Page::Alignment * index; #if PDATA_ENABLED @@ -562,6 +568,13 @@ bool Heap::AllocInPage(Page* page, size_t bytes, ushort pdataCount, ushort xdata this->freeObjectSize -= bytes; #endif + //Section of the Page should already be freed. + if (!page->freeBitVector.TestRange(index, length)) + { + CustomHeap_BadPageState_fatal_error((ULONG_PTR)this); + return false; + } + page->freeBitVector.ClearRange(index, length); VerboseHeapTrace(_u("ChunkSize: %d, Index: %d, Free bit vector in page: "), length, index); @@ -729,18 +742,19 @@ bool Heap::FreeAllocation(Allocation* object) unsigned int length = GetChunkSizeForBytes(object->size); BVIndex index = GetIndexInPage(page, object->address); -#if DBG - // Make sure that it's not already been freed - for (BVIndex i = index; i < length; i++) + uint freeBitsCount = page->freeBitVector.Count(); + + // Make sure that the section under interest or the whole page has not already been freed + if (page->IsEmpty() || page->freeBitVector.TestAnyInRange(index, length)) { - Assert(!page->freeBitVector.Test(i)); + CustomHeap_BadPageState_fatal_error((ULONG_PTR)this); + return false; } -#endif if (page->inFullList) { VerboseHeapTrace(_u("Recycling page 0x%p because address 0x%p of size %d was freed\n"), page->address, object->address, object->size); - + // If the object being freed is equal to the page size, we're // going to remove it anyway so don't add it to a bucket if (object->size != pageSize) @@ -777,14 +791,43 @@ bool Heap::FreeAllocation(Allocation* object) // If the page is about to become empty then we should not need // to set it to executable and we don't expect to restore the // previous protection settings. - if (page->freeBitVector.Count() == BVUnit::BitsPerWord - length) + if (freeBitsCount == BVUnit::BitsPerWord - length) { EnsureAllocationWriteable(object); + FreeAllocationHelper(object, index, length); + Assert(page->IsEmpty()); + + this->buckets[page->currentBucket].RemoveElement(this->auxiliaryAllocator, page); + return false; } else { EnsureAllocationExecuteWriteable(object); + + FreeAllocationHelper(object, index, length); + + // after freeing part of the page, the page should be in PAGE_EXECUTE_READWRITE protection, and turning to PAGE_EXECUTE (always with TARGETS_NO_UPDATE state) + + DWORD protectFlags = 0; + + if (AutoSystemInfo::Data.IsCFGEnabled()) + { + protectFlags = PAGE_EXECUTE_RO_TARGETS_NO_UPDATE; + } + else + { + protectFlags = PAGE_EXECUTE; + } + + this->codePageAllocators->ProtectPages(page->address, 1, segment, protectFlags, PAGE_EXECUTE_READWRITE); + + return true; } +} + +void Heap::FreeAllocationHelper(Allocation* object, BVIndex index, uint length) +{ + Page* page = object->page; // Fill the old buffer with debug breaks CustomHeap::FillDebugBreak((BYTE *)object->address, object->size); @@ -796,6 +839,7 @@ bool Heap::FreeAllocation(Allocation* object) VerboseHeapTrace(_u("\n")); page->freeBitVector.SetRange(index, length); + VerboseHeapTrace(_u("Free bit vector in page: "), length, index); #if VERBOSE_HEAP page->freeBitVector.DumpWord(); @@ -808,29 +852,6 @@ bool Heap::FreeAllocation(Allocation* object) #endif this->auxiliaryAllocator->Free(object, sizeof(Allocation)); - - if (page->IsEmpty()) - { - this->buckets[page->currentBucket].RemoveElement(this->auxiliaryAllocator, page); - return false; - } - else // after freeing part of the page, the page should be in PAGE_EXECUTE_READWRITE protection, and turning to PAGE_EXECUTE (always with TARGETS_NO_UPDATE state) - { - DWORD protectFlags = 0; - - if (AutoSystemInfo::Data.IsCFGEnabled()) - { - protectFlags = PAGE_EXECUTE_RO_TARGETS_NO_UPDATE; - } - else - { - protectFlags = PAGE_EXECUTE; - } - - this->codePageAllocators->ProtectPages(page->address, 1, segment, protectFlags, PAGE_EXECUTE_READWRITE); - - return true; - } } void Heap::FreeDecommittedBuckets()
lib/Common/Memory/CustomHeap.h+1 −0 modified@@ -504,6 +504,7 @@ class Heap void FreeBucket(DListBase<Page>* bucket, bool freeOnlyEmptyPages); void FreePage(Page* page); bool FreeAllocation(Allocation* allocation); + void FreeAllocationHelper(Allocation * allocation, BVIndex index, uint length); #if PDATA_ENABLED void FreeXdata(XDataAllocation* xdata, void* segment);
lib/Common/Memory/PageAllocator.h+2 −2 modified@@ -518,8 +518,8 @@ class PageAllocatorBase #if DBG_DUMP virtual void DumpStats() const; #endif - virtual PageSegmentBase<TVirtualAlloc> * AddPageSegment(DListBase<PageSegmentBase<TVirtualAlloc>>& segmentList); - static PageSegmentBase<TVirtualAlloc> * AllocPageSegment(DListBase<PageSegmentBase<TVirtualAlloc>>& segmentList, + PageSegmentBase<TVirtualAlloc> * AddPageSegment(DListBase<PageSegmentBase<TVirtualAlloc>>& segmentList); + static PageSegmentBase<TVirtualAlloc> * AllocPageSegment(DListBase<PageSegmentBase<TVirtualAlloc>>& segmentList, PageAllocatorBase<TVirtualAlloc> * pageAllocator, bool committed, bool allocated); // Zero Pages
lib/Parser/Parse.cpp+19 −9 modified@@ -816,6 +816,7 @@ Symbol* Parser::AddDeclForPid(ParseNodePtr pnode, IdentPtr pid, SymbolType symbo && blockInfo->pnodeBlock->sxBlock.blockType == PnodeBlockType::Function && blockInfo->pBlockInfoOuter != nullptr && blockInfo->pBlockInfoOuter->pnodeBlock->sxBlock.blockType == PnodeBlockType::Parameter + && blockInfo->pnodeBlock->sxBlock.scope->GetScopeType() != ScopeType_FuncExpr && blockInfo->pBlockInfoOuter->pnodeBlock->sxBlock.scope->GetCanMergeWithBodyScope()) { blockInfo = blockInfo->pBlockInfoOuter; @@ -5029,6 +5030,13 @@ bool Parser::ParseFncDeclHelper(ParseNodePtr pnodeFnc, ParseNodePtr pnodeFncPare } return false; }); + + if (wellKnownPropertyPids.arguments->GetTopRef() && wellKnownPropertyPids.arguments->GetTopRef()->GetScopeId() > pnodeFnc->sxFnc.pnodeScopes->sxBlock.blockId) + { + Assert(pnodeFnc->sxFnc.UsesArguments()); + // Arguments symbol is captured in the param scope + paramScope->SetCannotMergeWithBodyScope(); + } } } } @@ -5895,13 +5903,6 @@ bool Parser::ParseFncNames(ParseNodePtr pnodeFnc, ParseNodePtr pnodeFncParent, u pnodeFnc->sxFnc.pid = m_phtbl->PidHashNameLen( m_pscan->PchBase() + ichMinNames, ichLimNames - ichMinNames); } - - if(pnodeFnc->sxFnc.pid == wellKnownPropertyPids.arguments && fDeclaration && pnodeFncParent) - { - // This function declaration (or function expression in compat modes) overrides the built-in arguments object of the - // parent function - pnodeFncParent->grfpn |= PNodeFlags::fpnArguments_overriddenByDecl; - } } return true; @@ -6670,10 +6671,11 @@ void Parser::AddArgumentsNodeToVars(ParseNodePtr pnodeFnc) } else { + ParseNodePtr argNode = nullptr; if(m_ppnodeVar == &pnodeFnc->sxFnc.pnodeVars) { // There were no var declarations in the function - CreateVarDeclNode(wellKnownPropertyPids.arguments, STVariable, true, pnodeFnc)->grfpn |= PNodeFlags::fpnArguments; + argNode = CreateVarDeclNode(wellKnownPropertyPids.arguments, STVariable, true, pnodeFnc); } else { @@ -6684,10 +6686,18 @@ void Parser::AddArgumentsNodeToVars(ParseNodePtr pnodeFnc) // object until it is replaced with something else. ParseNodePtr *const ppnodeVarSave = m_ppnodeVar; m_ppnodeVar = &pnodeFnc->sxFnc.pnodeVars; - CreateVarDeclNode(wellKnownPropertyPids.arguments, STVariable, true, pnodeFnc)->grfpn |= PNodeFlags::fpnArguments; + argNode = CreateVarDeclNode(wellKnownPropertyPids.arguments, STVariable, true, pnodeFnc); m_ppnodeVar = ppnodeVarSave; } + Assert(argNode); + argNode->grfpn |= PNodeFlags::fpnArguments; + + // When a function definition with the name arguments occurs in the body the declaration of the arguments symbol will + // be set to that function declaration. We should change it to arguments declaration from the param scope as it may be + // used in the param scope and we have to load the arguments. + argNode->sxVar.sym->SetDecl(argNode); + pnodeFnc->sxFnc.SetHasReferenceableBuiltInArguments(true); } }
lib/Runtime/ByteCode/ByteCodeEmitter.cpp+3 −1 modified@@ -3693,7 +3693,9 @@ void EnsureFncDeclScopeSlot(ParseNode *pnodeFnc, FuncInfo *funcInfo) { Assert(pnodeFnc->sxFnc.pnodeName->nop == knopVarDecl); Symbol *sym = pnodeFnc->sxFnc.pnodeName->sxVar.sym; - if (sym) + // If this function is shadowing the arguments symbol in body then skip it. + // We will allocate scope slot for the arguments symbol during EmitLocalPropInit. + if (sym && !sym->GetIsArguments()) { sym->EnsureScopeSlot(funcInfo); }
lib/Runtime/Library/JavascriptArray.cpp+1 −1 modified@@ -9019,7 +9019,7 @@ namespace Js if (newArr) { - newArr->DirectSetItemAt(k, mappedValue); + newArr->SetItem(k, mappedValue, PropertyOperation_None); } else {
test/es6/default.js+235 −1 modified@@ -380,7 +380,241 @@ var tests = [ body: function () { assert.doesNotThrow(function () { eval("[ (a = function () { }) => {} ];"); }, "Lambda defined, inside an array literal, has a default as a function should not assert"); } - } + }, + { + name: "Shadowing arguments symbol", + body: function () { + function f1(a, b = arguments[0]) { + assert.areEqual(1, arguments[0], "Initial value of arguments symbol in the body should be same as the arguments from the param scope"); + var arguments = [10, 20]; + assert.areEqual(1, b, "Arguments value is the initial value in the param scope too"); + assert.areEqual(10, arguments[0], "Arguments value is updated in the body"); + } + f1(1); + + function f2(a = 1, arguments) { + assert.areEqual(2, arguments, "Initial value of arguments symbol in the body should be same as the arguments from the param scope"); + var arguments = [10, 20]; + assert.areEqual(10, arguments[0], "Arguments value is updated in the body"); + } + f2(undefined, 2); + + function f3(a, b = arguments[0]) { + assert.areEqual(10, arguments(), "Arguments symbol is overwritten by the the function definition"); + function arguments() { + return 10; + } + assert.areEqual(1, b, "Arguments value is the initial value in the param scope too"); + } + f3(1); + + function f4(a = 1, arguments, c = arguments) { + assert.areEqual(10, arguments(), "In the body function definition shadows the formal"); + assert.areEqual(2, c, "Value of the formal is assigned properly"); + function arguments() { + return 10; + } + } + f4(undefined, 2); + + function f5(a, b = arguments) { + function arguments(c) { + return arguments; + } + assert.areEqual(30, arguments(10, 20, 30)[2], "Inside the arguments function the arguments symbol should points to the passed in values"); + assert.areEqual(4, b[3], "In the param scope arguments symbol referes to the passed in values"); + } + f5(1, undefined, 3, 4, 5); + + function f6(a, b = arguments) { + function arguments(c) { + if (!arguments.length) { + return arguments.callee(10, 20, 30); + } + return arguments; + } + assert.areEqual(20, arguments()[1], "In the function body arguments refers to the inner function"); + assert.areEqual(3, b[2], "In the param scope arguments symbol referes to the passed in values"); + } + f6(1, undefined, 3, 4); + + function f7(a, b = function arguments(c) { + if (!c) { + return arguments.callee(10, 20); + } + return c + arguments[1]; + }) { + assert.areEqual(30, b(), "Function defined in the param scope can be called recursively"); + assert.areEqual(1, arguments[0], "Arguments symbol is unaffected by the function expression"); + } + f7(1); + + function f8(a, b = arguments) { + var c = function arguments(c) { + if (!arguments.length) { + return arguments.callee(10, 20, 30); + } + return arguments; + } + assert.areEqual(30, c()[2], "In the function body the arguments function expression with name is not visible"); + assert.areEqual(1, b[0], "In the param scope arguments symbol referes to the passed in values"); + } + f8(1); + + assert.throws(function () { eval("function f(a, b = arguments) { class arguments { } }"); }, SyntaxError, "Class cannot be named arguments", "Invalid usage of 'arguments' in strict mode"); + assert.throws(function () { eval("function f(a, arguments) { class arguments { } }"); }, SyntaxError, "Class cannot be named arguments even when one of the formal is named arguments", "Let/Const redeclaration"); + + function f9( a = 0, b = { + arguments() { + return 10; + } + }, c = arguments) { + with (b) { + assert.areEqual(10, arguments(), "Inside with the right the arguments function inside the object is used in the body also"); + } + assert.areEqual(1, arguments[0], "Arguments symbol should be unaffected after with construct"); + assert.areEqual(1, c[0], "Arguments symbol from param scope should be unaffected after with construct"); + } + f9(1); + + function f10(a = 1, b = () => { + assert.areEqual(undefined, arguments, "Due to the decalration in the body arguments symbol is shadowed inside the lambda"); + var arguments = 100; + assert.areEqual(100, arguments, "After the assignment value of arguments is updated inside the lambda"); + }, c = arguments) { + assert.areEqual(10, arguments[0], "In the body the value of arguments is retained"); + assert.areEqual(10, c[0], "Arguments symbol is not affected in the param scope"); + b(); + } + f10(10); + + function f11(a = 1, b = () => { + assert.areEqual(100, arguments(), "Inside the lambda the function definition shadows the parent's arguments symbol"); + function arguments() { + return 100; + } + }, c = arguments) { + assert.areEqual(10, arguments[0], "In the body the value of arguments is retained"); + b(); + assert.areEqual(10, c[0], "Arguments symbol is not affected in the param scope"); + } + f11(10); + + function f12({a = 1, arguments}) { + assert.areEqual(2, arguments, "Initial value of arguments symbol in the body should be same as the arguments from the param scope's destructured pattern"); + var arguments = [10, 20]; + assert.areEqual(10, arguments[0], "Arguments value is updated in the body"); + } + f12({arguments : 2}); + + function f13(a = 1, {arguments, c = arguments}) { + assert.areEqual(10, arguments(), "In the body function definition shadows the destructured formal"); + assert.areEqual(2, c, "Value of the formal is assigned properly"); + function arguments() { + return 10; + } + } + f13(undefined, { arguments: 2 }); + + function f14(a, b = arguments[0]) { + assert.areEqual(1, arguments[0], "Function in block causes a var declaration to be hoisted and the initial value should be same as the arguments symbol"); + { + { + function arguments() { + return 10; + } + } + } + assert.areEqual(1, b, "Arguments value is the initial value in the param scope too"); + assert.areEqual(10, arguments(), "Hoisted var binding is updated after the block is exected"); + } + f14(1); + + function f15() { + function f16() { + eval(""); + this.arguments = 1; + } + + var obj = new f16(); + + function arguments() { + return 10; + } + assert.areEqual(1, obj.arguments, "Child function having eval should work fine with a duplicate arguments definition in the parent body"); + }; + f15(); + } + }, + { + name: "Shadowing arguments symbol - Eval", + body: function () { + function f1(a, b = arguments[0]) { + assert.areEqual(1, arguments[0], "Initial value of arguments symbol in the body should be same as the arguments from the param scope"); + var arguments = [10, 20]; + assert.areEqual(1, b, "Arguments value is the initial value in the param scope too"); + assert.areEqual(10, eval("arguments[0]"), "Arguments value is updated in the body"); + } + f1(1); + + function f2(a = 1, arguments) { + assert.areEqual(2, eval("arguments"), "Initial value of arguments symbol in the body should be same as the arguments from the param scope"); + var arguments = [10, 20]; + assert.areEqual(10, arguments[0], "Arguments value is updated in the body"); + } + f2(undefined, 2); + + function f3(a, b = arguments[0]) { + assert.areEqual(10, eval("arguments()"), "Arguments symbol is overwritten by the the function definition"); + function arguments() { + return 10; + } + assert.areEqual(1, b, "Arguments value is the initial value in the param scope too"); + } + f3(1); + + function f4(a = 1, arguments, c = arguments) { + assert.areEqual(10, arguments(), "In the body function definition shadows the formal"); + assert.areEqual(2, eval("c"), "Value of the formal is assigned properly"); + function arguments() { + return 10; + } + } + f4(undefined, 2); + + function f5(a, b, c = arguments) { + function arguments(c) { + return eval("arguments"); + } + assert.areEqual(30, arguments(10, 20, 30)[2], "In the function body arguments refers to the inner function"); + assert.areEqual(2, c[1], "In the param scope arguments symbol referes to the passed in values"); + } + f5(1, 2, undefined, 4); + + function f6(a, b = function arguments(c) { + if (!c) { + return arguments.callee(10, 20); + } + return c + arguments[1]; + }) { + assert.areEqual(30, eval("b()"), "Function defined in the param scope can be called recursively"); + assert.areEqual(1, arguments[0], "Arguments symbol is unaffected by the function expression"); + } + f6(1); + + function f7(a, b = arguments) { + var c = function arguments(c) { + if (!arguments.length) { + return arguments.callee(10, 20, 30); + } + return arguments; + } + assert.areEqual(10, eval("c()[0]"), "In the function body the arguments function expression with name is not visible"); + assert.areEqual(4, b[3], "In the param scope arguments symbol referes to the passed in values"); + } + f7(1, undefined, 3, 4); + } + } ]; testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });
test/es6/default-splitscope.js+89 −3 modified@@ -734,9 +734,18 @@ var tests = [ assert.throws(function () { eval("function f(a = 10, b = (c = arguments) => a) { }"); }, SyntaxError, "Use of arguments symbol is not allowed in non-simple parameter list when captured in a lambda in split scope", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured"); assert.throws(function () { eval("function f(a, b = () => a, c = () => { return arguments; }) { }"); }, SyntaxError, "Use of arguments symbol is not allowed in non-simple parameter list in split scope when captured by a lambda method", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured"); assert.throws(function () { eval("function f(a = 10, b = () => a, c = () => () => arguments) { }"); }, SyntaxError, "Use of arguments symbol is not allowed in non-simple parameter list in split scope when captured by nested lambda", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured"); - assert.throws(function () { eval("function f3(a, arguments = function () { return a; } ) { }"); }, SyntaxError, "Use of arguments as a parameter name is not allowed in non-simple parameter list in split scope when captured by nested lambda", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured"); - assert.throws(function () { eval("function f3({a, arguments = function () { return a; }}) { }"); }, SyntaxError, "Use of arguments as a parameter name is not allowed in destructuring parameter list in split scope when captured by nested lambda", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured"); - assert.throws(function () { eval("function f3({a = arguments}, b = function () { return a; } ) { }"); }, SyntaxError, "Use of arguments is not allowed in destructuring parameter list in split scope when captured by nested lambda", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured"); + assert.throws(function () { eval("function f(a, arguments = function () { return a; } ) { }"); }, SyntaxError, "Use of arguments as a parameter name is not allowed in non-simple parameter list in split scope when captured by nested lambda", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured"); + assert.throws(function () { eval("function f({a, arguments = function () { return a; }}) { }"); }, SyntaxError, "Use of arguments as a parameter name is not allowed in destructuring parameter list in split scope when captured by nested lambda", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured"); + assert.throws(function () { eval("function f({a = arguments}, b = function () { return a; } ) { }"); }, SyntaxError, "Use of arguments is not allowed in destructuring parameter list in split scope when captured by nested lambda", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured"); + assert.throws(function () { eval("function f(a = () => arguments) { }"); }, SyntaxError, "Arguments cannot be captured in the param scope", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured"); + assert.throws(function () { eval("function f(a = () => arguments[0]) { }"); }, SyntaxError, "Arguments cannot be captured in the param scope", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured"); + assert.throws(function () { eval("function f(a = 1, b = () => arguments[0]) { }"); }, SyntaxError, "Arguments cannot be captured in the param scope at any position", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured"); + assert.throws(function () { eval("function f(a = () => arguments[0] + b, b = 10) { }"); }, SyntaxError, "Arguments cannot be captured in the param scope at any position", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured"); + assert.throws(function () { eval("function f(a = () => arguments) { var arguments }"); }, SyntaxError, "Arguments cannot be captured in the param scope even when duplicate definition occurs in the body", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured"); + assert.throws(function () { eval("function f(a = () => arguments) { function arguments() { } }"); }, SyntaxError, "Arguments cannot be captured in the param scope even when duplicate definition occurs in the body", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured"); + assert.throws(function () { eval("function f(arguments, b = () => arguments) { }"); }, SyntaxError, "Arguments cannot be captured in the param scope even if it is a formal shadowing the actual arguments", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured"); + assert.throws(function () { eval("function f({a, arguments}, b = () => a) { }"); }, SyntaxError, "Arguments cannot be used as a formal name when one of the formal is captured", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured"); + assert.throws(function () { eval("function f(a, {arguments, b = () => arguments}) { }"); }, SyntaxError, "Arguments cannot be used as a formal name when one of the formal is captured", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured"); function f1(a, b = () => a) { eval(""); @@ -913,6 +922,83 @@ var tests = [ } } assert.areEqual([2, 3], f16(1, undefined, 2, 3), "Rest should remain unaffected when arguments is updated"); + + function f18(a, b = function arguments(c) { + if (!c) { + return arguments.callee(a, 10, 20); + } + return arguments; + }) { + assert.areEqual(10, b()[1], "Function defined in the param scope can be called recursively"); + assert.areEqual(1, arguments[0], "Arguments symbol is unaffected by the function expression"); + } + f18(1); + + function f19(a, b = arguments) { + var c = function arguments(c) { + if (!arguments.length) { + return arguments.callee(a, 10, 20, 30); + } + return arguments; + } + assert.areEqual(30, c()[3], "In the function body the arguments function expression with name is not visible"); + assert.areEqual(1, b[0], "In the param scope arguments symbol referes to the passed in values"); + } + f19(1, undefined, 2, 3, 4); + + function f20(a, b = function arguments(c) { + if (!c) { + return arguments.callee(a, 10, 20); + } + return eval("arguments"); + }) { + assert.areEqual(1, b()[0], "Function defined in the param scope can be called recursively when eval occurs in its body"); + assert.areEqual(1, arguments[0], "Arguments symbol is unaffected by the function expression"); + } + f20(1); + + function f21(a, b = arguments) { + var c = function arguments(c) { + if (!arguments.length) { + return arguments.callee(a, 10, 20, 30); + } + return arguments; + } + assert.areEqual(30, c()[3], "In the function body the arguments function expression with name is not visible when eval is there in the body"); + assert.areEqual(3, eval("b[3]"), "In the param scope arguments symbol referes to the passed in values"); + } + f21(1, undefined, 2, 3, 4); + + function f22(a, b = () => a) { + assert.areEqual(1, arguments[0], "Function in block causes a var declaration to be hoisted and the initial value should be same as the arguments symbol"); + { + { + function arguments() { + return 10; + } + } + } + assert.areEqual(1, b(), "Function defined in the param scope should be able to capture the formal even when arguments in overwritten the body"); + assert.areEqual(10, arguments(), "Hoisted var binding is updated after the block is exected"); + } + f22(1); + + function f23(a, b = () => a) { + function f16() { + eval(""); + this.arguments = 1; + } + + a = 10; + var obj = new f16(); + + function arguments() { + return 10; + } + assert.areEqual(1, obj.arguments, "Inner function with eval should add the property named arguments when duplicate arguments definition occurs in the parent body"); + assert.areEqual(1, b(), "Formal captured from the param scope should be constrained to the param scope"); + }; + f23(1); } }, {
test/es6/ES6Species-bugs.js+35 −0 modified@@ -27,6 +27,41 @@ var tests = [ assert.throws(function() { Array.prototype.splice.call(arr, 0, 3); }, TypeError, "TypeError when constructor[Symbol.species] is not constructor", "Function 'constructor[Symbol.species]' is not a constructor"); } }, + { + name: "Type confusion in Array.prototype.map()", + body: function () { + function test(){ + CollectGarbage(); + + var n = []; + for (var i = 0; i < 0x10; i++) + n.push([0x12345678, 0x12345678, 0x12345678, 0x12345678]); + + class fake extends Object { + static get [Symbol.species]() { return function() { return n[5]; }; }; + } + + var f = function(a){ return a; } + + var x = ["fluorescence", 0, 0, 0x41414141]; + var y = new Proxy(x, { + get: function(t, p, r) { + return (p == "constructor") ? fake : x[p]; + } + }); + + // oob write + Array.prototype.map.apply(y, [f]); + + for (var i = 0; i < 0x10; i++) + n[i][0] = 0x42424242; + + } + + test(); + + } + }, ]; 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
9- github.com/advisories/GHSA-wv44-9w69-w43jghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2016-3377ghsaADVISORY
- docs.microsoft.com/en-us/security-updates/securitybulletins/2016/ms16-105nvdWEB
- github.com/chakra-core/ChakraCore/commit/24c4d7df8199b27d360323ce3be1d7959fd918ebghsaWEB
- github.com/chakra-core/ChakraCore/issues/6289ghsaWEB
- web.archive.org/web/20210123044830/http://www.securitytracker.com/id/1036789ghsaWEB
- web.archive.org/web/20210123164626/http://www.securityfocus.com/bid/92797ghsaWEB
- www.securityfocus.com/bid/92797nvd
- www.securitytracker.com/id/1036789nvd
News mentions
0No linked articles in our index yet.