Bläddra i källkod

[1.10>master] [MERGE #5449 @atulkatti] MSFT:18139538 Remove the use of Guest Arena from parser code to avoid ScriptContext leak.

Merge pull request #5449 from atulkatti:Bug18139538.RemoveGuestArenaUsage.1

MSFT:18171347 Parser's Temporary Guest Arena leaks in case of modules with Regex patterns.
Atul Katti 7 år sedan
förälder
incheckning
dab75c53e6

+ 1 - 1
lib/Common/Memory/RecyclerRootPtr.h

@@ -51,7 +51,7 @@ public:
     }
     }
     void Unroot()
     void Unroot()
     {
     {
-        if (ptr != nullptr)
+        if (this->ptr != nullptr)
         {
         {
             __super::Unroot(recycler);
             __super::Unroot(recycler);
         }
         }

+ 22 - 8
lib/Parser/Parse.cpp

@@ -83,8 +83,10 @@ Parser::Parser(Js::ScriptContext* scriptContext, BOOL strictMode, PageAllocator
     m_doingFastScan(false),
     m_doingFastScan(false),
 #endif
 #endif
     m_nextBlockId(0),
     m_nextBlockId(0),
+    m_tempGuestArenaReleased(false),
+    m_tempGuestArena(scriptContext->GetTemporaryGuestAllocator(_u("ParserRegex")), scriptContext->GetRecycler()),
     // use the GuestArena directly for keeping the RegexPattern* alive during byte code generation
     // use the GuestArena directly for keeping the RegexPattern* alive during byte code generation
-    m_registeredRegexPatterns(scriptContext->GetGuestArena()),
+    m_registeredRegexPatterns(m_tempGuestArena->GetAllocator()),
 
 
     m_scriptContext(scriptContext),
     m_scriptContext(scriptContext),
     m_token(), // should initialize to 0/nullptrs
     m_token(), // should initialize to 0/nullptrs
@@ -155,12 +157,7 @@ Parser::Parser(Js::ScriptContext* scriptContext, BOOL strictMode, PageAllocator
 
 
 Parser::~Parser(void)
 Parser::~Parser(void)
 {
 {
-    if (m_scriptContext == nullptr || m_scriptContext->GetGuestArena() == nullptr)
-    {
-        // If the scriptContext or guestArena have gone away, there is no point clearing each item of this list.
-        // Just reset it so that destructor of the SList will be no-op
-        m_registeredRegexPatterns.Reset();
-    }
+    this->ReleaseTemporaryGuestArena();
 
 
 #if ENABLE_BACKGROUND_PARSING
 #if ENABLE_BACKGROUND_PARSING
     if (this->m_hasParallelJob)
     if (this->m_hasParallelJob)
@@ -1926,7 +1923,7 @@ void Parser::RegisterRegexPattern(UnifiedRegex::RegexPattern *const regexPattern
     Assert(regexPattern);
     Assert(regexPattern);
 
 
     // ensure a no-throw add behavior here, to catch out of memory exceptions, using the guest arena allocator
     // ensure a no-throw add behavior here, to catch out of memory exceptions, using the guest arena allocator
-    if (!m_registeredRegexPatterns.PrependNoThrow(m_scriptContext->GetGuestArena(), regexPattern))
+    if (!m_registeredRegexPatterns.PrependNoThrow(m_tempGuestArena->GetAllocator(), regexPattern))
     {
     {
         Parser::Error(ERRnoMemory);
         Parser::Error(ERRnoMemory);
     }
     }
@@ -13943,6 +13940,23 @@ void Parser::ProcessCapturedNames(ParseNodeFnc* pnodeFnc)
     }
     }
 }
 }
 
 
+void Parser::ReleaseTemporaryGuestArena()
+{
+    // In case of modules the Parser lives longer than the temporary Guest Arena. We may have already released the arena explicitly.
+    if (!m_tempGuestArenaReleased)
+    {
+        // The regex patterns list has references to the temporary Guest Arena. Reset it first.
+        m_registeredRegexPatterns.Reset();
+
+        if (this->m_scriptContext != nullptr)
+        {
+            this->m_scriptContext->ReleaseTemporaryGuestAllocator(m_tempGuestArena);
+        }
+
+        m_tempGuestArenaReleased = true;
+    }
+}
+
 bool Parser::IsCreatingStateCache()
 bool Parser::IsCreatingStateCache()
 {
 {
     return (((this->m_grfscr & fscrCreateParserState) == fscrCreateParserState)
     return (((this->m_grfscr & fscrCreateParserState) == fscrCreateParserState)

+ 6 - 3
lib/Parser/Parse.h

@@ -177,6 +177,8 @@ namespace Js
 {
 {
     class ParseableFunctionInfo;
     class ParseableFunctionInfo;
     class FunctionBody;
     class FunctionBody;
+    template <bool isGuestArena>
+    class TempArenaAllocatorWrapper;
 };
 };
 
 
 class Parser
 class Parser
@@ -192,7 +194,7 @@ public:
     ~Parser(void);
     ~Parser(void);
 
 
     Js::ScriptContext* GetScriptContext() const { return m_scriptContext; }
     Js::ScriptContext* GetScriptContext() const { return m_scriptContext; }
-
+    void ReleaseTemporaryGuestArena();
     bool IsCreatingStateCache();
     bool IsCreatingStateCache();
 
 
 #if ENABLE_BACKGROUND_PARSING
 #if ENABLE_BACKGROUND_PARSING
@@ -271,10 +273,12 @@ private:
     bool                m_isInBackground;
     bool                m_isInBackground;
     bool                m_doingFastScan;
     bool                m_doingFastScan;
 #endif
 #endif
+    bool                m_tempGuestArenaReleased;
     int                 m_nextBlockId;
     int                 m_nextBlockId;
 
 
+    AutoRecyclerRootPtr<Js::TempArenaAllocatorWrapper<true>> m_tempGuestArena;
     // RegexPattern objects created for literal regexes are recycler-allocated and need to be kept alive until the function body
     // RegexPattern objects created for literal regexes are recycler-allocated and need to be kept alive until the function body
-    // is created during byte code generation. The RegexPattern pointer is stored in the script context's guest
+    // is created during byte code generation. The RegexPattern pointer is stored in a temporary guest
     // arena for that purpose. This list is then unregistered from the guest arena at the end of parsing/scanning.
     // arena for that purpose. This list is then unregistered from the guest arena at the end of parsing/scanning.
     SList<UnifiedRegex::RegexPattern *, ArenaAllocator> m_registeredRegexPatterns;
     SList<UnifiedRegex::RegexPattern *, ArenaAllocator> m_registeredRegexPatterns;
 
 
@@ -1150,5 +1154,4 @@ private:
 public:
 public:
     charcount_t GetSourceIchLim() { return m_sourceLim; }
     charcount_t GetSourceIchLim() { return m_sourceLim; }
     static BOOL NodeEqualsName(ParseNodePtr pnode, LPCOLESTR sz, uint32 cch);
     static BOOL NodeEqualsName(ParseNodePtr pnode, LPCOLESTR sz, uint32 cch);
-
 };
 };

+ 0 - 22
lib/Runtime/Base/ScriptContext.cpp

@@ -89,7 +89,6 @@ namespace Js
         integerStringMapCacheMissCount(0),
         integerStringMapCacheMissCount(0),
         integerStringMapCacheUseCount(0),
         integerStringMapCacheUseCount(0),
 #endif
 #endif
-        guestArena(nullptr),
 #ifdef ENABLE_SCRIPT_DEBUGGING
 #ifdef ENABLE_SCRIPT_DEBUGGING
         diagnosticArena(nullptr),
         diagnosticArena(nullptr),
         raiseMessageToDebuggerFunctionType(nullptr),
         raiseMessageToDebuggerFunctionType(nullptr),
@@ -799,12 +798,6 @@ namespace Js
             interpreterArena = nullptr;
             interpreterArena = nullptr;
         }
         }
 
 
-        if (this->guestArena)
-        {
-            ReleaseGuestArena();
-            guestArena = nullptr;
-        }
-
         builtInLibraryFunctions = nullptr;
         builtInLibraryFunctions = nullptr;
 
 
         pActiveScriptDirect = nullptr;
         pActiveScriptDirect = nullptr;
@@ -1305,8 +1298,6 @@ namespace Js
 
 
     void ScriptContext::InitializePreGlobal()
     void ScriptContext::InitializePreGlobal()
     {
     {
-        this->guestArena = this->GetRecycler()->CreateGuestArena(_u("Guest"), Throw::OutOfMemory);
-
 #if ENABLE_BACKGROUND_PARSING
 #if ENABLE_BACKGROUND_PARSING
         if (PHASE_ON1(Js::ParallelParsePhase))
         if (PHASE_ON1(Js::ParallelParsePhase))
         {
         {
@@ -2760,17 +2751,6 @@ ExitTempAllocator:
         }
         }
     }
     }
 
 
-
-    void ScriptContext::ReleaseGuestArena()
-    {
-        AssertMsg(this->guestArena, "No guest arena to release");
-        if (this->guestArena)
-        {
-            this->GetRecycler()->DeleteGuestArena(this->guestArena);
-            this->guestArena = nullptr;
-        }
-    }
-
     void ScriptContext::SetScriptStartEventHandler(ScriptContext::EventHandler eventHandler)
     void ScriptContext::SetScriptStartEventHandler(ScriptContext::EventHandler eventHandler)
     {
     {
         AssertMsg(this->scriptStartEventHandler == nullptr, "Do not support multi-cast yet");
         AssertMsg(this->scriptStartEventHandler == nullptr, "Do not support multi-cast yet");
@@ -4975,7 +4955,6 @@ ExitTempAllocator:
     void ScriptContext::BindReference(void * addr)
     void ScriptContext::BindReference(void * addr)
     {
     {
         Assert(!this->isClosed);
         Assert(!this->isClosed);
-        Assert(this->guestArena);
         Assert(recycler->IsValidObject(addr));
         Assert(recycler->IsValidObject(addr));
 #if DBG
 #if DBG
         Assert(!bindRef.ContainsKey(addr));     // Make sure we don't bind the same pointer twice
         Assert(!bindRef.ContainsKey(addr));     // Make sure we don't bind the same pointer twice
@@ -5142,7 +5121,6 @@ ExitTempAllocator:
         {
         {
             return;
             return;
         }
         }
-        Assert(this->guestArena);
 
 
         if (EnableEvalMapCleanup())
         if (EnableEvalMapCleanup())
         {
         {

+ 0 - 8
lib/Runtime/Base/ScriptContext.h

@@ -578,7 +578,6 @@ namespace Js
         CacheAllocator enumeratorCacheAllocator;
         CacheAllocator enumeratorCacheAllocator;
 
 
         ArenaAllocator* interpreterArena;
         ArenaAllocator* interpreterArena;
-        ArenaAllocator* guestArena;
 
 
 #ifdef ENABLE_SCRIPT_DEBUGGING
 #ifdef ENABLE_SCRIPT_DEBUGGING
         ArenaAllocator* diagnosticArena;
         ArenaAllocator* diagnosticArena;
@@ -1378,13 +1377,6 @@ private:
         bool EnsureInterpreterArena(ArenaAllocator **);
         bool EnsureInterpreterArena(ArenaAllocator **);
         void ReleaseInterpreterArena();
         void ReleaseInterpreterArena();
 
 
-        ArenaAllocator* GetGuestArena() const
-        {
-            return guestArena;
-        }
-
-        void ReleaseGuestArena();
-
         Recycler* GetRecycler() const { return recycler; }
         Recycler* GetRecycler() const { return recycler; }
         RecyclerJavascriptNumberAllocator * GetNumberAllocator() { return &numberAllocator; }
         RecyclerJavascriptNumberAllocator * GetNumberAllocator() { return &numberAllocator; }
 #if ENABLE_NATIVE_CODEGEN
 #if ENABLE_NATIVE_CODEGEN

+ 5 - 0
lib/Runtime/Language/SourceTextModuleRecord.cpp

@@ -915,6 +915,11 @@ namespace Js
                 childModuleRecord->GenerateRootFunction();
                 childModuleRecord->GenerateRootFunction();
             });
             });
         }
         }
+
+        if (this->parser != nullptr)
+        {
+            this->parser->ReleaseTemporaryGuestArena();
+        }
     }
     }
 
 
     Var SourceTextModuleRecord::ModuleEvaluation()
     Var SourceTextModuleRecord::ModuleEvaluation()

+ 13 - 0
test/es6module/module-functionality.js

@@ -350,6 +350,19 @@ var tests = [
             testRunner.LoadModule(functionBody, 'samethread');
             testRunner.LoadModule(functionBody, 'samethread');
         }
         }
     },
     },
+    {
+        name: "OS18171347 - Module's parser leaks temporary Guest Arena allocator when module has a regex pattern.",
+        body: function() {
+            testRunner.LoadModule(` /x/ ;`, 'samethread');
+
+            try
+            {
+                // syntax error
+                testRunner.LoadModule(` /x/ ; for(i=0);`, 'samethread', {shouldFail:true});
+            }
+            catch(e){}
+        }
+    },
 ];
 ];
 
 
 testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });
 testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });