Skip to content

Commit ec180eb

Browse files
pleathSuwei Chen
authored andcommitted
[CVE-2017-8740] Fix bad byte code gen for 'with'. The original (conservative) fix for this issue relied on marking scopes as containing 'with'. But because block scopes are created lazily, we can miss the opportunity to mark a scope. Instead, implementing a more accurate fix that marks symbols that are referenced from within 'with' statements as needing scope objects if they are closure-captured.
1 parent 6ed991d commit ec180eb

File tree

8 files changed

+73
-64
lines changed

8 files changed

+73
-64
lines changed

lib/Parser/Hash.cpp

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -131,18 +131,6 @@ void HashTbl::Grow()
131131
#endif
132132
}
133133

134-
void HashTbl::ClearPidRefStacks()
135-
{
136-
// Clear pidrefstack pointers from all existing pid's.
137-
for (uint i = 0; i < m_luMask; i++)
138-
{
139-
for (IdentPtr pid = m_prgpidName[i]; pid; pid = pid->m_pidNext)
140-
{
141-
pid->m_pidRefStack = nullptr;
142-
}
143-
}
144-
}
145-
146134
#if DEBUG
147135
uint HashTbl::CountAndVerifyItems(IdentPtr *buckets, uint bucketCount, uint mask)
148136
{

lib/Parser/Hash.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,18 @@ class HashTbl
393393
NoReleaseAllocator* GetAllocator() {return &m_noReleaseAllocator;}
394394

395395
bool Contains(_In_reads_(cch) LPCOLESTR prgch, int32 cch);
396-
void ClearPidRefStacks();
396+
397+
template<typename Fn>
398+
void VisitPids(Fn fn)
399+
{
400+
for (uint i = 0; i <= m_luMask; i++)
401+
{
402+
for (IdentPtr pid = m_prgpidName[i]; pid; pid = pid->m_pidNext)
403+
{
404+
fn(pid);
405+
}
406+
}
407+
}
397408

398409
private:
399410
NoReleaseAllocator m_noReleaseAllocator; // to allocate identifiers

lib/Parser/Parse.cpp

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1726,6 +1726,10 @@ void Parser::BindPidRefsInScope(IdentPtr pid, Symbol *sym, int blockId, uint max
17261726
{
17271727
Assert(ref->GetFuncScopeId() > funcId);
17281728
sym->SetHasNonLocalReference();
1729+
if (ref->IsDynamicBinding())
1730+
{
1731+
sym->SetNeedsScopeObject();
1732+
}
17291733
}
17301734

17311735
if (ref->IsFuncAssignment())
@@ -1855,15 +1859,13 @@ void Parser::PopDynamicBlock()
18551859
return;
18561860
}
18571861
Assert(m_currentDynamicBlock);
1858-
for (BlockInfoStack *blockInfo = m_currentBlockInfo; blockInfo; blockInfo = blockInfo->pBlockInfoOuter)
1859-
{
1860-
for (ParseNodePtr pnodeDecl = blockInfo->pnodeBlock->sxBlock.pnodeLexVars;
1861-
pnodeDecl;
1862-
pnodeDecl = pnodeDecl->sxVar.pnodeNext)
1862+
1863+
m_phtbl->VisitPids([&](IdentPtr pid) {
1864+
for (PidRefStack *ref = pid->GetTopRef(); ref && ref->GetScopeId() >= blockId; ref = ref->prev)
18631865
{
1864-
this->SetPidRefsInScopeDynamic(pnodeDecl->sxVar.pid, blockId);
1866+
ref->SetDynamicBinding();
18651867
}
1866-
}
1868+
});
18671869

18681870
m_currentDynamicBlock = m_currentDynamicBlock->prev;
18691871
}
@@ -9932,10 +9934,6 @@ ParseNodePtr Parser::ParseStatement()
99329934
{
99339935
GetCurrentFunctionNode()->sxFnc.SetHasWithStmt(); // Used by DeferNested
99349936
}
9935-
for (Scope *scope = this->m_currentScope; scope; scope = scope->GetEnclosingScope())
9936-
{
9937-
scope->SetContainsWith();
9938-
}
99399937

99409938
ichMin = m_pscan->IchMinTok();
99419939
ChkNxtTok(tkLParen, ERRnoLparen);
@@ -10773,7 +10771,7 @@ void Parser::FinishDeferredFunction(ParseNodePtr pnodeScopeList)
1077310771
auto addArgsToScope = [&](ParseNodePtr pnodeArg) {
1077410772
if (pnodeArg->IsVarLetOrConst())
1077510773
{
10776-
PidRefStack *ref = this->FindOrAddPidRef(pnodeArg->sxVar.pid, blockId, funcId);//this->PushPidRef(pnodeArg->sxVar.pid);
10774+
PidRefStack *ref = this->FindOrAddPidRef(pnodeArg->sxVar.pid, blockId, funcId);
1077710775
pnodeArg->sxVar.symRef = ref->GetSymRef();
1077810776
if (ref->GetSym() != nullptr)
1077910777
{
@@ -11194,7 +11192,7 @@ ParseNodePtr Parser::Parse(LPCUTF8 pszSrc, size_t offset, size_t length, charcou
1119411192
FinishParseBlock(pnodeGlobalBlock);
1119511193

1119611194
// Clear out references to undeclared identifiers.
11197-
m_phtbl->ClearPidRefStacks();
11195+
m_phtbl->VisitPids([&](IdentPtr pid) { pid->SetTopRef(nullptr); });
1119811196

1119911197
// Restore global scope and blockinfo stacks preparatory to reparsing deferred functions.
1120011198
PushScope(pnodeGlobalBlock->sxBlock.scope);

lib/Runtime/ByteCode/ByteCodeGenerator.cpp

Lines changed: 5 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1780,6 +1780,11 @@ Symbol * ByteCodeGenerator::AddSymbolToScope(Scope *scope, const char16 *key, in
17801780

17811781
Assert(sym && sym->GetScope() && (sym->GetScope() == scope || sym->GetScope()->GetScopeType() == ScopeType_Parameter));
17821782

1783+
if (sym->NeedsScopeObject())
1784+
{
1785+
scope->SetIsObject();
1786+
}
1787+
17831788
return sym;
17841789
}
17851790

@@ -2794,34 +2799,6 @@ FuncInfo* PostVisitFunction(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerat
27942799
byteCodeGenerator->AssignParamSlotsRegister();
27952800
}
27962801

2797-
if (top->GetBodyScope()->ContainsWith() &&
2798-
(top->GetBodyScope()->GetHasOwnLocalInClosure() ||
2799-
(top->GetParamScope()->GetHasOwnLocalInClosure() &&
2800-
top->IsBodyAndParamScopeMerged())))
2801-
{
2802-
// Parent scopes may contain symbols called inside the with.
2803-
// Current implementation needs the symScope isObject.
2804-
2805-
top->GetBodyScope()->SetIsObject();
2806-
if (top->byteCodeFunction->IsFunctionBody())
2807-
{
2808-
// Record this for future use in the no-refresh debugging.
2809-
top->byteCodeFunction->GetFunctionBody()->SetHasSetIsObject(true);
2810-
}
2811-
}
2812-
2813-
if (top->GetParamScope()->ContainsWith() &&
2814-
(top->GetParamScope()->GetHasOwnLocalInClosure() &&
2815-
!top->IsBodyAndParamScopeMerged()))
2816-
{
2817-
top->GetParamScope()->SetIsObject();
2818-
if (top->byteCodeFunction->IsFunctionBody())
2819-
{
2820-
// Record this for future use in the no-refresh debugging.
2821-
top->byteCodeFunction->GetFunctionBody()->SetHasSetIsObject(true);
2822-
}
2823-
}
2824-
28252802
if (byteCodeGenerator->NeedObjectAsFunctionScope(top, top->root)
28262803
|| top->bodyScope->GetIsObject()
28272804
|| top->paramScope->GetIsObject())
@@ -3152,11 +3129,6 @@ void ByteCodeGenerator::ProcessScopeWithCapturedSym(Scope *scope)
31523129
{
31533130
Assert(scope->GetHasOwnLocalInClosure());
31543131

3155-
if (scope->ContainsWith() && scope->GetScopeType() != ScopeType_Global)
3156-
{
3157-
scope->SetIsObject();
3158-
}
3159-
31603132
// (Note: if any catch var is closure-captured, we won't merge the catch scope with the function scope.
31613133
// So don't mark the function scope "has local in closure".)
31623134
FuncInfo *func = scope->GetFunc();

lib/Runtime/ByteCode/Scope.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ class Scope
4141
BYTE canMergeWithBodyScope : 1;
4242
BYTE hasLocalInClosure : 1;
4343
BYTE isBlockInLoop : 1;
44-
BYTE containsWith : 1;
4544
public:
4645
#if DBG
4746
BYTE isRestored : 1;
@@ -61,7 +60,6 @@ class Scope
6160
canMergeWithBodyScope(true),
6261
hasLocalInClosure(false),
6362
isBlockInLoop(false),
64-
containsWith(false),
6563
location(Js::Constants::NoRegister),
6664
m_symList(nullptr),
6765
m_count(0),
@@ -264,9 +262,6 @@ class Scope
264262
void SetIsBlockInLoop(bool is = true) { isBlockInLoop = is; }
265263
bool IsBlockInLoop() const { return isBlockInLoop; }
266264

267-
void SetContainsWith(bool does = true) { containsWith = does; }
268-
bool ContainsWith() const { return containsWith; }
269-
270265
bool HasInnerScopeIndex() const { return innerScopeIndex != (uint)-1; }
271266
uint GetInnerScopeIndex() const { return innerScopeIndex; }
272267
void SetInnerScopeIndex(uint index) { innerScopeIndex = index; }

lib/Runtime/ByteCode/Symbol.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ class Symbol
4747
BYTE isModuleExportStorage : 1; // If true, this symbol should be stored in the global scope export storage array.
4848
BYTE isModuleImport : 1; // If true, this symbol is the local name of a module import statement
4949
BYTE isUsedInLdElem : 1;
50+
BYTE needsScopeObject : 1;
5051

5152
// These are get and set a lot, don't put it in bit fields, we are exceeding the number of bits anyway
5253
bool hasFuncAssignment;
@@ -84,6 +85,7 @@ class Symbol
8485
isModuleExportStorage(false),
8586
isModuleImport(false),
8687
isUsedInLdElem(false),
88+
needsScopeObject(false),
8789
moduleIndex(Js::Constants::NoProperty)
8890
{
8991
SetSymbolType(symbolType);
@@ -184,6 +186,16 @@ class Symbol
184186
return isUsedInLdElem;
185187
}
186188

189+
void SetNeedsScopeObject(bool does = true)
190+
{
191+
needsScopeObject = does;
192+
}
193+
194+
bool NeedsScopeObject() const
195+
{
196+
return needsScopeObject;
197+
}
198+
187199
void SetModuleIndex(Js::PropertyId index)
188200
{
189201
moduleIndex = index;

test/Basics/With-defer-block-scope.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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+
let h = function f(a0 = (function () {
7+
a0;
8+
a1;
9+
a2;
10+
a3;
11+
a4;
12+
a5;
13+
a6;
14+
a7 = 0x99999; // oob write
15+
16+
with ({});
17+
})(), a1, a2, a3, a4, a5, a6, a7) {
18+
function g() {
19+
f;
20+
}
21+
};
22+
23+
for (let i = 0; i < 0x10000; i++) {
24+
h();
25+
}
26+
27+
WScript.Echo('pass');

test/Basics/rlexe.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,12 @@
272272
<compile-flags>-force:deferparse</compile-flags>
273273
</default>
274274
</test>
275+
<test>
276+
<default>
277+
<files>With-defer-block-scope.js</files>
278+
<compile-flags>-force:deferparse</compile-flags>
279+
</default>
280+
</test>
275281
<test>
276282
<default>
277283
<files>withBug940841.js</files>

0 commit comments

Comments
 (0)