Skip to content

Commit 2f088a6

Browse files
author
Atul Katti
committed
[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.
2 parents 2bd57e5 + 336bed1 commit 2f088a6

File tree

7 files changed

+47
-42
lines changed

7 files changed

+47
-42
lines changed

lib/Common/Memory/RecyclerRootPtr.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class AutoRecyclerRootPtr : public RecyclerRootPtr<T>
5151
}
5252
void Unroot()
5353
{
54-
if (ptr != nullptr)
54+
if (this->ptr != nullptr)
5555
{
5656
__super::Unroot(recycler);
5757
}

lib/Parser/Parse.cpp

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,10 @@ Parser::Parser(Js::ScriptContext* scriptContext, BOOL strictMode, PageAllocator
8282
m_doingFastScan(false),
8383
#endif
8484
m_nextBlockId(0),
85+
m_tempGuestArenaReleased(false),
86+
m_tempGuestArena(scriptContext->GetTemporaryGuestAllocator(_u("ParserRegex")), scriptContext->GetRecycler()),
8587
// use the GuestArena directly for keeping the RegexPattern* alive during byte code generation
86-
m_registeredRegexPatterns(scriptContext->GetGuestArena()),
88+
m_registeredRegexPatterns(m_tempGuestArena->GetAllocator()),
8789

8890
m_scriptContext(scriptContext),
8991
m_token(), // should initialize to 0/nullptrs
@@ -154,12 +156,7 @@ Parser::Parser(Js::ScriptContext* scriptContext, BOOL strictMode, PageAllocator
154156

155157
Parser::~Parser(void)
156158
{
157-
if (m_scriptContext == nullptr || m_scriptContext->GetGuestArena() == nullptr)
158-
{
159-
// If the scriptContext or guestArena have gone away, there is no point clearing each item of this list.
160-
// Just reset it so that destructor of the SList will be no-op
161-
m_registeredRegexPatterns.Reset();
162-
}
159+
this->ReleaseTemporaryGuestArena();
163160

164161
#if ENABLE_BACKGROUND_PARSING
165162
if (this->m_hasParallelJob)
@@ -1925,7 +1922,7 @@ void Parser::RegisterRegexPattern(UnifiedRegex::RegexPattern *const regexPattern
19251922
Assert(regexPattern);
19261923

19271924
// ensure a no-throw add behavior here, to catch out of memory exceptions, using the guest arena allocator
1928-
if (!m_registeredRegexPatterns.PrependNoThrow(m_scriptContext->GetGuestArena(), regexPattern))
1925+
if (!m_registeredRegexPatterns.PrependNoThrow(m_tempGuestArena->GetAllocator(), regexPattern))
19291926
{
19301927
Parser::Error(ERRnoMemory);
19311928
}
@@ -13909,6 +13906,23 @@ void Parser::ProcessCapturedNames(ParseNodeFnc* pnodeFnc)
1390913906
}
1391013907
}
1391113908

13909+
void Parser::ReleaseTemporaryGuestArena()
13910+
{
13911+
// In case of modules the Parser lives longer than the temporary Guest Arena. We may have already released the arena explicitly.
13912+
if (!m_tempGuestArenaReleased)
13913+
{
13914+
// The regex patterns list has references to the temporary Guest Arena. Reset it first.
13915+
m_registeredRegexPatterns.Reset();
13916+
13917+
if (this->m_scriptContext != nullptr)
13918+
{
13919+
this->m_scriptContext->ReleaseTemporaryGuestAllocator(m_tempGuestArena);
13920+
}
13921+
13922+
m_tempGuestArenaReleased = true;
13923+
}
13924+
}
13925+
1391213926
bool Parser::IsCreatingStateCache()
1391313927
{
1391413928
return (((this->m_grfscr & fscrCreateParserState) == fscrCreateParserState)

lib/Parser/Parse.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@ namespace Js
177177
{
178178
class ParseableFunctionInfo;
179179
class FunctionBody;
180+
template <bool isGuestArena>
181+
class TempArenaAllocatorWrapper;
180182
};
181183

182184
class Parser
@@ -192,7 +194,7 @@ class Parser
192194
~Parser(void);
193195

194196
Js::ScriptContext* GetScriptContext() const { return m_scriptContext; }
195-
197+
void ReleaseTemporaryGuestArena();
196198
bool IsCreatingStateCache();
197199

198200
#if ENABLE_BACKGROUND_PARSING
@@ -271,10 +273,12 @@ class Parser
271273
bool m_isInBackground;
272274
bool m_doingFastScan;
273275
#endif
276+
bool m_tempGuestArenaReleased;
274277
int m_nextBlockId;
275278

279+
AutoRecyclerRootPtr<Js::TempArenaAllocatorWrapper<true>> m_tempGuestArena;
276280
// RegexPattern objects created for literal regexes are recycler-allocated and need to be kept alive until the function body
277-
// is created during byte code generation. The RegexPattern pointer is stored in the script context's guest
281+
// is created during byte code generation. The RegexPattern pointer is stored in a temporary guest
278282
// arena for that purpose. This list is then unregistered from the guest arena at the end of parsing/scanning.
279283
SList<UnifiedRegex::RegexPattern *, ArenaAllocator> m_registeredRegexPatterns;
280284

@@ -1150,5 +1154,4 @@ class Parser
11501154
public:
11511155
charcount_t GetSourceIchLim() { return m_sourceLim; }
11521156
static BOOL NodeEqualsName(ParseNodePtr pnode, LPCOLESTR sz, uint32 cch);
1153-
11541157
};

lib/Runtime/Base/ScriptContext.cpp

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ namespace Js
8888
integerStringMapCacheMissCount(0),
8989
integerStringMapCacheUseCount(0),
9090
#endif
91-
guestArena(nullptr),
9291
#ifdef ENABLE_SCRIPT_DEBUGGING
9392
diagnosticArena(nullptr),
9493
raiseMessageToDebuggerFunctionType(nullptr),
@@ -798,12 +797,6 @@ namespace Js
798797
interpreterArena = nullptr;
799798
}
800799

801-
if (this->guestArena)
802-
{
803-
ReleaseGuestArena();
804-
guestArena = nullptr;
805-
}
806-
807800
builtInLibraryFunctions = nullptr;
808801

809802
pActiveScriptDirect = nullptr;
@@ -1304,8 +1297,6 @@ namespace Js
13041297

13051298
void ScriptContext::InitializePreGlobal()
13061299
{
1307-
this->guestArena = this->GetRecycler()->CreateGuestArena(_u("Guest"), Throw::OutOfMemory);
1308-
13091300
#if ENABLE_BACKGROUND_PARSING
13101301
if (PHASE_ON1(Js::ParallelParsePhase))
13111302
{
@@ -2680,17 +2671,6 @@ namespace Js
26802671
}
26812672
}
26822673

2683-
2684-
void ScriptContext::ReleaseGuestArena()
2685-
{
2686-
AssertMsg(this->guestArena, "No guest arena to release");
2687-
if (this->guestArena)
2688-
{
2689-
this->GetRecycler()->DeleteGuestArena(this->guestArena);
2690-
this->guestArena = nullptr;
2691-
}
2692-
}
2693-
26942674
void ScriptContext::SetScriptStartEventHandler(ScriptContext::EventHandler eventHandler)
26952675
{
26962676
AssertMsg(this->scriptStartEventHandler == nullptr, "Do not support multi-cast yet");
@@ -4895,7 +4875,6 @@ namespace Js
48954875
void ScriptContext::BindReference(void * addr)
48964876
{
48974877
Assert(!this->isClosed);
4898-
Assert(this->guestArena);
48994878
Assert(recycler->IsValidObject(addr));
49004879
#if DBG
49014880
Assert(!bindRef.ContainsKey(addr)); // Make sure we don't bind the same pointer twice
@@ -5062,7 +5041,6 @@ namespace Js
50625041
{
50635042
return;
50645043
}
5065-
Assert(this->guestArena);
50665044

50675045
if (EnableEvalMapCleanup())
50685046
{

lib/Runtime/Base/ScriptContext.h

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,6 @@ namespace Js
578578
CacheAllocator enumeratorCacheAllocator;
579579

580580
ArenaAllocator* interpreterArena;
581-
ArenaAllocator* guestArena;
582581

583582
#ifdef ENABLE_SCRIPT_DEBUGGING
584583
ArenaAllocator* diagnosticArena;
@@ -1378,13 +1377,6 @@ namespace Js
13781377
bool EnsureInterpreterArena(ArenaAllocator **);
13791378
void ReleaseInterpreterArena();
13801379

1381-
ArenaAllocator* GetGuestArena() const
1382-
{
1383-
return guestArena;
1384-
}
1385-
1386-
void ReleaseGuestArena();
1387-
13881380
Recycler* GetRecycler() const { return recycler; }
13891381
RecyclerJavascriptNumberAllocator * GetNumberAllocator() { return &numberAllocator; }
13901382
#if ENABLE_NATIVE_CODEGEN

lib/Runtime/Language/SourceTextModuleRecord.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -915,6 +915,11 @@ namespace Js
915915
childModuleRecord->GenerateRootFunction();
916916
});
917917
}
918+
919+
if (this->parser != nullptr)
920+
{
921+
this->parser->ReleaseTemporaryGuestArena();
922+
}
918923
}
919924

920925
Var SourceTextModuleRecord::ModuleEvaluation()

test/es6module/module-functionality.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,19 @@ var tests = [
350350
testRunner.LoadModule(functionBody, 'samethread');
351351
}
352352
},
353+
{
354+
name: "OS18171347 - Module's parser leaks temporary Guest Arena allocator when module has a regex pattern.",
355+
body: function() {
356+
testRunner.LoadModule(` /x/ ;`, 'samethread');
357+
358+
try
359+
{
360+
// syntax error
361+
testRunner.LoadModule(` /x/ ; for(i=0);`, 'samethread', {shouldFail:true});
362+
}
363+
catch(e){}
364+
}
365+
},
353366
];
354367

355368
testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });

0 commit comments

Comments
 (0)