Quellcode durchsuchen

[CVE-2020-0711]

Taylor Woll vor 6 Jahren
Ursprung
Commit
a9aee51164

+ 20 - 0
lib/Parser/Parse.cpp

@@ -1738,6 +1738,20 @@ void Parser::BindPidRefsInScope(IdentPtr pid, Symbol *sym, int blockId, uint max
             }
         }
 
+        if (m_currentNodeFunc && m_currentNodeFunc->pnodeName && pid == m_currentNodeFunc->pnodeName->pid && !m_currentNodeFunc->IsDeclaration() && m_currentNodeFunc->IsBodyAndParamScopeMerged())
+        {
+            Scope* funcExprScope = m_currentNodeFunc->scope;
+            Assert(funcExprScope->GetScopeType() == ScopeType_FuncExpr);
+
+            ParseNodeBlock* bodyScope = m_currentNodeFunc->pnodeBodyScope;
+            Assert(bodyScope->blockType == PnodeBlockType::Function);
+
+            if (ref->GetScopeId() < bodyScope->blockId && ref->GetScopeId() > blockId)
+            {
+                funcExprScope->SetIsObject();
+            }
+        }
+
         if (ref->GetScopeId() == blockId)
         {
             break;
@@ -4938,6 +4952,12 @@ ParseNodeFnc * Parser::ParseFncDeclInternal(ushort flags, LPCOLESTR pNameHint, c
     pnodeFnc->SetIsBaseClassConstructor((flags & fFncBaseClassConstructor) != 0);
     pnodeFnc->SetHomeObjLocation(Js::Constants::NoRegister);
 
+    if (this->m_currentScope && this->m_currentScope->GetScopeType() == ScopeType_Parameter)
+    {
+        pnodeFnc->SetIsDeclaredInParamScope();
+        this->m_currentScope->SetHasNestedParamFunc();
+    }
+
     IdentPtr pFncNamePid = nullptr;
     bool needScanRCurly = true;
     ParseFncDeclHelper<buildAST>(pnodeFnc, pNameHint, flags, fUnaryOrParen, noStmtContext, &needScanRCurly, fModule, &pFncNamePid, fAllowIn);

+ 3 - 1
lib/Parser/ptree.h

@@ -445,7 +445,7 @@ enum FncFlags : uint
     kFunctionIsStaticMember                     = 1 << 24,
     kFunctionIsGenerator                        = 1 << 25, // Function is an ES6 generator function
     kFunctionAsmjsMode                          = 1 << 26,
-    // Free = 1 << 27,
+    kFunctionIsDeclaredInParamScope             = 1 << 27, // Function is declared in parameter scope (ex: inside default argument)
     kFunctionIsAsync                            = 1 << 28, // function is async
     kFunctionHasDirectSuper                     = 1 << 29, // super()
     kFunctionIsDefaultModuleExport              = 1 << 30, // function is the default export of a module
@@ -583,6 +583,7 @@ public:
     void SetHasHomeObj(bool set = true) { SetFlags(kFunctionHasHomeObj, set); }
     void SetUsesArguments(bool set = true) { SetFlags(kFunctionUsesArguments, set); }
     void SetIsDefaultModuleExport(bool set = true) { SetFlags(kFunctionIsDefaultModuleExport, set); }
+    void SetIsDeclaredInParamScope(bool set = true) { SetFlags(kFunctionIsDeclaredInParamScope, set); }
     void SetNestedFuncEscapes(bool set = true) { nestedFuncEscapes = set; }
     void SetCanBeDeferred(bool set = true) { canBeDeferred = set; }
     void ResetBodyAndParamScopeMerged() { isBodyAndParamScopeMerged = false; }
@@ -623,6 +624,7 @@ public:
     bool HasHomeObj() const { return HasFlags(kFunctionHasHomeObj); }
     bool UsesArguments() const { return HasFlags(kFunctionUsesArguments); }
     bool IsDefaultModuleExport() const { return HasFlags(kFunctionIsDefaultModuleExport); }
+    bool IsDeclaredInParamScope() const { return HasFlags(kFunctionIsDeclaredInParamScope); }
     bool NestedFuncEscapes() const { return nestedFuncEscapes; }
     bool CanBeDeferred() const { return canBeDeferred; }
     bool IsBodyAndParamScopeMerged() { return isBodyAndParamScopeMerged; }

+ 7 - 2
lib/Runtime/ByteCode/ByteCodeEmitter.cpp

@@ -3411,8 +3411,6 @@ void ByteCodeGenerator::EmitScopeList(ParseNode *pnode, ParseNode *breakOnBodySc
                 }
                 this->StartEmitFunction(pnode->AsParseNodeFnc());
 
-                PushFuncInfo(_u("StartEmitFunction"), funcInfo);
-
                 if (!funcInfo->IsBodyAndParamScopeMerged())
                 {
                     this->EmitScopeList(pnode->AsParseNodeFnc()->pnodeBodyScope->pnodeScopes);
@@ -3789,6 +3787,11 @@ void ByteCodeGenerator::StartEmitFunction(ParseNodeFnc *pnodeFnc)
             else if (pnodeFnc->IsBodyAndParamScopeMerged() || bodyScope->GetScopeSlotCount() != 0)
             {
                 bodyScope->SetMustInstantiate(funcInfo->frameSlotsRegister != Js::Constants::NoRegister);
+
+                if (pnodeFnc->IsBodyAndParamScopeMerged() && paramScope && paramScope->GetHasNestedParamFunc())
+                {
+                    paramScope->SetMustInstantiate(funcInfo->frameSlotsRegister != Js::Constants::NoRegister);
+                }
             }
 
             if (!pnodeFnc->IsBodyAndParamScopeMerged())
@@ -3816,6 +3819,8 @@ void ByteCodeGenerator::StartEmitFunction(ParseNodeFnc *pnodeFnc)
         }
     }
 
+    PushFuncInfo(_u("StartEmitFunction"), funcInfo);
+
     if (!funcInfo->IsBodyAndParamScopeMerged())
     {
         ParseNodeBlock * paramBlock = pnodeFnc->pnodeScopes;

+ 15 - 2
lib/Runtime/ByteCode/ByteCodeGenerator.cpp

@@ -1818,7 +1818,7 @@ FuncInfo *ByteCodeGenerator::FindEnclosingNonLambda()
     return nullptr;
 }
 
-FuncInfo* GetParentFuncInfo(FuncInfo* child)
+FuncInfo* ByteCodeGenerator::GetParentFuncInfo(FuncInfo* child)
 {
     for (Scope* scope = child->GetBodyScope(); scope; scope = scope->GetEnclosingScope())
     {
@@ -1831,6 +1831,19 @@ FuncInfo* GetParentFuncInfo(FuncInfo* child)
     return nullptr;
 }
 
+FuncInfo* ByteCodeGenerator::GetEnclosingFuncInfo()
+{
+    FuncInfo* top = this->funcInfoStack->Pop();
+
+    Assert(!this->funcInfoStack->Empty());
+
+    FuncInfo* second = this->funcInfoStack->Top();
+
+    this->funcInfoStack->Push(top);
+
+    return second;
+}
+
 bool ByteCodeGenerator::CanStackNestedFunc(FuncInfo * funcInfo, bool trace)
 {
 #if ENABLE_DEBUG_CONFIG_OPTIONS
@@ -2605,7 +2618,7 @@ void AssignFuncSymRegister(ParseNodeFnc * pnodeFnc, ByteCodeGenerator * byteCode
                 Assert(byteCodeGenerator->GetCurrentScope()->GetFunc() == sym->GetScope()->GetFunc());
                 if (byteCodeGenerator->GetCurrentScope()->GetFunc() != sym->GetScope()->GetFunc())
                 {
-                    Assert(GetParentFuncInfo(byteCodeGenerator->GetCurrentScope()->GetFunc()) == sym->GetScope()->GetFunc());
+                    Assert(ByteCodeGenerator::GetParentFuncInfo(byteCodeGenerator->GetCurrentScope()->GetFunc()) == sym->GetScope()->GetFunc());
                     sym->GetScope()->SetMustInstantiate(true);
                     byteCodeGenerator->ProcessCapturedSym(sym);
                     sym->GetScope()->GetFunc()->SetHasLocalInClosure(true);

+ 2 - 0
lib/Runtime/ByteCode/ByteCodeGenerator.h

@@ -376,6 +376,8 @@ public:
     void PopulateFormalsScope(uint beginOffset, FuncInfo *funcInfo, ParseNodeFnc *pnodeFnc);
     void InsertPropertyToDebuggerScope(FuncInfo* funcInfo, Js::DebuggerScope* debuggerScope, Symbol* sym);
     FuncInfo *FindEnclosingNonLambda();
+    static FuncInfo* GetParentFuncInfo(FuncInfo* child);
+    FuncInfo* GetEnclosingFuncInfo();
 
     bool CanStackNestedFunc(FuncInfo * funcInfo, bool trace = false);
     void CheckDeferParseHasMaybeEscapedNestedFunc();

+ 5 - 0
lib/Runtime/ByteCode/Scope.h

@@ -41,6 +41,7 @@ private:
     BYTE canMergeWithBodyScope : 1;
     BYTE hasLocalInClosure : 1;
     BYTE isBlockInLoop : 1;
+    BYTE hasNestedParamFunc : 1;
 public:
 #if DBG
     BYTE isRestored : 1;
@@ -60,6 +61,7 @@ public:
         canMergeWithBodyScope(true),
         hasLocalInClosure(false),
         isBlockInLoop(false),
+        hasNestedParamFunc(false),
         location(Js::Constants::NoRegister),
         m_symList(nullptr),
         m_count(0),
@@ -261,6 +263,9 @@ public:
     void SetIsBlockInLoop(bool is = true) { isBlockInLoop = is; }
     bool IsBlockInLoop() const { return isBlockInLoop; }
 
+    void SetHasNestedParamFunc(bool is = true) { hasNestedParamFunc = is; }
+    bool GetHasNestedParamFunc() const { return hasNestedParamFunc; }
+
     bool HasInnerScopeIndex() const { return innerScopeIndex != (uint)-1; }
     uint GetInnerScopeIndex() const { return innerScopeIndex; }
     void SetInnerScopeIndex(uint index) { innerScopeIndex = index; }

+ 25 - 1
lib/Runtime/ByteCode/ScopeInfo.cpp

@@ -112,8 +112,17 @@ namespace Js
     ScopeInfo * ScopeInfo::SaveScopeInfo(ByteCodeGenerator* byteCodeGenerator, Scope * scope, ScriptContext * scriptContext)
     {
         // Advance past scopes that will be excluded from the closure environment. (But note that we always want the body scope.)
-        while (scope && (!scope->GetMustInstantiate() && scope != scope->GetFunc()->GetBodyScope()))
+        while (scope)
         {
+            FuncInfo* func = scope->GetFunc();
+
+            if (scope->GetMustInstantiate() ||
+                func->GetBodyScope() == scope ||
+                (func->GetParamScope() == scope && func->IsBodyAndParamScopeMerged() && scope->GetHasNestedParamFunc()))
+            {
+                break;
+            }
+
             scope = scope->GetEnclosingScope();
         }
 
@@ -162,6 +171,21 @@ namespace Js
         Scope* currentScope = byteCodeGenerator->GetCurrentScope();
         Assert(currentScope->GetFunc() == funcInfo);
 
+        if (funcInfo->root->IsDeclaredInParamScope())
+        {
+            Assert(currentScope->GetScopeType() == ScopeType_FunctionBody);
+            Assert(currentScope->GetEnclosingScope());
+
+            FuncInfo* func = byteCodeGenerator->GetEnclosingFuncInfo();
+            Assert(func);
+
+            if (func->IsBodyAndParamScopeMerged())
+            {
+                currentScope = func->GetParamScope();
+                Assert(currentScope->GetScopeType() == ScopeType_Parameter);
+            }
+        }
+
         while (currentScope->GetFunc() == funcInfo)
         {
             currentScope = currentScope->GetEnclosingScope();

+ 132 - 0
test/Bugs/bug_OS18926499.js

@@ -0,0 +1,132 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+let throwingFunctions = [{
+    msg: "Split-scope parent, param-scope child capturing symbol from parent body scope",
+    body: function foo(a = (()=>+x)()) {
+            function bar() { eval(''); }
+            var x;
+          }
+},
+{
+    msg: "Split-scope parent, param-scope child capturing symbol from parent body scope",
+    body: function foo(a = (()=>+x)()) {
+            eval('');
+            var x;
+          }
+},
+{
+    msg: "Merged-scope parent, param-scope child capturing symbol from parent body scope",
+    body: function foo(a = () => +x) {
+            var x = 1;
+            return a();
+          }
+},
+{
+    msg: "Merged-scope parent, param-scope child capturing symbol from parent body scope",
+    body: function foo(a = (()=>+x)()) {
+            var x;
+          }
+},
+{
+    msg: "Func expr parent, param-scope func expr child capturing symbol from parent body scope",
+    body: foo3 = function foo3a(a = (function foo3b() { return +x; })()) {
+            var x = 123;
+          }
+},
+{
+    msg: "Func expr parent, param-scope func expr child capturing symbol from parent body scope",
+    body: foo3 = function foo3a(a = (function foo3b() { return +x; })()) {
+            eval('');
+            var x = 123;
+          }
+},
+{
+    msg: "Func expr parent, param-scope func expr child capturing symbol from parent body scope",
+    body: foo3 = function foo3a(a = (function foo3b() { return +x; })()) {
+            function bar() { eval(''); }
+            var x = 123;
+          }
+},
+{
+    msg: "Param-scope func expr child with nested func expr capturing symbol from parent body scope",
+    body: foo5 = function foo5a(a = (function(){(function(b = 123) { +x; })()})()) {
+                    function bar() { eval(''); }
+                    var x;
+                 }
+},
+{
+    msg: "Multiple nested func expr, inner param-scope function capturing outer func expr name",
+    body: foo3 = function foo3a(a = (function foo3b(b = (function foo3c(c = (function foo3d() { +x; })()){})()){})()){ var x;}
+}];
+
+let nonThrowingFunctions = [{
+    msg: "Func expr parent, param-scope func expr child capturing parent func expr name",
+    body: foo3 = function foo3a(a = (function foo3b() { +foo3a; })()) {
+            return +a;
+          }
+},
+{
+    msg: "Func expr parent, param-scope func expr child capturing own func expr name",
+    body: foo3 = function foo3a(a = (function foo3b() { +foo3b; })()) {
+            return +a;
+          }
+},
+{
+    msg: "Func expr parent, param-scope func expr child capturing expression name hint",
+    body: foo3 = function foo3a(a = (function foo3b() { +foo3; })()) {
+            return +a;
+          }
+},
+{
+    msg: "Func expr parent, param-scope func expr child capturing parent argument name",
+    body: foo3 = function foo3a(b = 123, a = (function foo3b() { return +b; })()) {
+            if (123 !== a) throw 123;
+          }
+},
+{
+    msg: "Func expr parent, param-scope func expr child capturing symbol from parent body scope",
+    body: foo3 = function foo3a(b = 123, a = (function foo3b() { return +b; })()) {
+            if (123 !== a) throw 123;
+          }
+},
+{
+    msg: "Multiple nested func expr, inner param-scope function capturing outer func expr name",
+    body: foo3 = function foo3a(a = (function foo3b(b = (function foo3c(c = (function foo3d() { foo3d; })()){})()){})()){}
+},
+{
+    msg: "Multiple nested func expr, inner param-scope function capturing outer func expr name",
+    body: foo3 = function foo3a(a = (function foo3b(b = (function foo3c(c = (function foo3d() { foo3c; })()){})()){})()){}
+},
+{
+    msg: "Multiple nested func expr, inner param-scope function capturing outer func expr name",
+    body: foo3 = function foo3a(a = (function foo3b(b = (function foo3c(c = (function foo3d() { foo3b; })()){})()){})()){}
+},
+{
+    msg: "Multiple nested func expr, inner param-scope function capturing outer func expr name",
+    body: foo3 = function foo3a(a = (function foo3b(b = (function foo3c(c = (function foo3d() { foo3a; })()){})()){})()){}
+},
+{
+    msg: "Multiple nested func expr, inner param-scope function capturing outer func expr name",
+    body: foo3 = function foo3a(a = (function foo3b(b = (function foo3c(c = (function foo3d() { foo3; })()){})()){})()){}
+}];
+
+for (let fn of throwingFunctions) {
+    try {
+        fn.body();
+        console.log(`fail: ${fn.msg}`);
+    } catch (e) {
+        console.log("pass");
+    }
+}
+
+for (let fn of nonThrowingFunctions) {
+    try {
+        fn.body();
+        console.log("pass");
+    } catch (e) {
+        console.log(`fail: ${fn.msg}`);
+    }
+}

+ 20 - 0
test/Bugs/bug_OS23102586.js

@@ -0,0 +1,20 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+// force:deferparse
+
+function test0() {
+  var k;
+  
+  function foo(a = function() { +k; }) {
+      a();
+      function bar() { a }
+  };
+  
+  eval('')
+  foo();
+}
+test0();
+console.log('pass')

+ 12 - 0
test/Bugs/rlexe.xml

@@ -536,4 +536,16 @@
       <compile-flags>-esdynamicimport -mutehosterrormsg -args summary -endargs</compile-flags>
     </default>
   </test>
+  <test>
+    <default>
+      <files>bug_OS18926499.js</files>
+      <compile-flags>-force:deferparse</compile-flags>
+    </default>
+  </test>
+  <test>
+    <default>
+      <files>bug_OS23102586.js</files>
+      <compile-flags>-force:deferparse</compile-flags>
+    </default>
+  </test>
 </regress-exe>