Skip to content

Commit 6c60ebf

Browse files
committed
[MERGE #5197 @boingoing] OS#17542375 Correct possible overflow of deferred stubs array
Merge pull request #5197 from boingoing:overflow_deferredstubs_array When undeferring a function with deferred stubs, we try and use those stubs to determine if expressions beginning with a left paren are nested lambda functions. We do this when we see a left paren and the next deferred stub is a lambda function starting at the same character. Unfortunately we don't keep track of the count of deferred stubs in the current deferred stubs array. If all of the nested functions in the function we're undefering are located in source before the left paren character, we should not check for the next deferred stub as this would overflow the array and cause a possible AV. Fix this by tracking the count of stubs in the current deferred stubs array. Fixes: https://microsoft.visualstudio.com/OS/_workitems/edit/17542375
2 parents 20fa861 + aae4cd9 commit 6c60ebf

File tree

4 files changed

+40
-2
lines changed

4 files changed

+40
-2
lines changed

lib/Parser/Parse.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ Parser::Parser(Js::ScriptContext* scriptContext, BOOL strictMode, PageAllocator
9595
m_currentNodeDeferredFunc(nullptr),
9696
m_currentNodeProg(nullptr),
9797
m_currDeferredStub(nullptr),
98+
m_currDeferredStubCount(0),
9899
m_pCurrentAstSize(nullptr),
99100
m_ppnodeScope(nullptr),
100101
m_ppnodeExprScope(nullptr),
@@ -3068,7 +3069,7 @@ ParseNodePtr Parser::ParseTerm(BOOL fAllowCall,
30683069
// is a lambda at the current character. If it is, we know this LParen is the beginning of a lambda nested
30693070
// function and we can avoid parsing the next series of tokens as a parenthetical expression and reparsing
30703071
// after finding the => token.
3071-
if (buildAST && m_currDeferredStub != nullptr && GetCurrentFunctionNode() != nullptr)
3072+
if (buildAST && m_currDeferredStub != nullptr && GetCurrentFunctionNode() != nullptr && GetCurrentFunctionNode()->nestedCount < m_currDeferredStubCount)
30723073
{
30733074
DeferredFunctionStub* stub = m_currDeferredStub + GetCurrentFunctionNode()->nestedCount;
30743075
if (stub->ichMin == ichMin)
@@ -5577,13 +5578,17 @@ void Parser::ParseFncDeclHelper(ParseNodeFnc * pnodeFnc, LPCOLESTR pNameHint, us
55775578
{
55785579
*pNeedScanRCurly = false;
55795580
}
5581+
uint savedStubCount = m_currDeferredStubCount;
55805582
DeferredFunctionStub* savedStub = m_currDeferredStub;
55815583
if (pnodeFnc->IsNested() && pnodeFncSave != nullptr && m_currDeferredStub != nullptr && pnodeFncSave->ichMin != pnodeFnc->ichMin)
55825584
{
5583-
m_currDeferredStub = (m_currDeferredStub + (pnodeFncSave->nestedCount - 1))->deferredStubs;
5585+
DeferredFunctionStub* childStub = m_currDeferredStub + (pnodeFncSave->nestedCount - 1);
5586+
m_currDeferredStubCount = childStub->nestedCount;
5587+
m_currDeferredStub = childStub->deferredStubs;
55845588
}
55855589
this->FinishFncDecl(pnodeFnc, pNameHint, fLambda, skipFormals);
55865590
m_currDeferredStub = savedStub;
5591+
m_currDeferredStubCount = savedStubCount;
55875592
}
55885593
else
55895594
{
@@ -11859,6 +11864,7 @@ HRESULT Parser::ParseSourceWithOffset(__out ParseNodeProg ** parseTree, LPCUTF8
1185911864
if (m_functionBody)
1186011865
{
1186111866
m_currDeferredStub = m_functionBody->GetDeferredStubs();
11867+
m_currDeferredStubCount = m_currDeferredStub != nullptr ? m_functionBody->GetNestedCount() : 0;
1186211868
m_InAsmMode = grfscr & fscrNoAsmJs ? false : m_functionBody->GetIsAsmjsMode();
1186311869
}
1186411870
m_deferAsmJs = !m_InAsmMode;

lib/Parser/Parse.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,7 @@ class Parser
415415
ParseNodeFnc * m_currentNodeDeferredFunc; // current function or NULL
416416
ParseNodeProg * m_currentNodeProg; // current program
417417
DeferredFunctionStub *m_currDeferredStub;
418+
uint m_currDeferredStubCount;
418419
int32 * m_pCurrentAstSize;
419420
ParseNodePtr * m_ppnodeScope; // function list tail
420421
ParseNodePtr * m_ppnodeExprScope; // function expression list tail

test/Basics/bug_os17542375.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//-------------------------------------------------------------------------------------------------------
2+
// Copyright (C) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
4+
//-------------------------------------------------------------------------------------------------------
5+
6+
// With pageheap:2 we should not hit an AV
7+
8+
function foo() {
9+
var r = {};
10+
var b = {};
11+
var a = {};
12+
a.$ = r;
13+
14+
function foo1() {
15+
}
16+
function foo2() {
17+
}
18+
return r.noConflict = function(b) {
19+
return a.$ === r && (a.$ = Wb), b && a.jQuery === r && (a.jQuery = Vb), r
20+
}, b || (a.jQuery = a.$ = r), r
21+
}
22+
23+
foo();
24+
console.log('pass');

test/Basics/rlexe.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,4 +414,11 @@
414414
<tags>exclude_test,exclude_jshost,exclude_dynapogo</tags>
415415
</default>
416416
</test>
417+
<test>
418+
<default>
419+
<files>bug_os17542375.js</files>
420+
<compile-flags>-UseParserStateCache -ParserStateCache -Force:DeferParse -pageheap:2</compile-flags>
421+
<tags>exclude_test,exclude_jshost</tags>
422+
</default>
423+
</test>
417424
</regress-exe>

0 commit comments

Comments
 (0)