Skip to content

Commit 7291e33

Browse files
committed
[MERGE #5933 @wyrichte] Fixes bug where ImplicitCallFlags and DisableImplicitFlags were not restored to previous values held.
Merge pull request #5933 from wyrichte:build/wyrichte/disableImplicitFlagBug InlineCache::TrySetProperty changes threadContext->ImplicitCallFlags and threadContext->DisableImplicitFlags because "We need to disable implicit call to ensure the check doesn't cause unwanted side effects...", calls CheckPrototypesForAccessorOrNonWritablePropertySlow, and then restores the Flags. The issue is CheckPrototypesForAccessorOrNonWritablePropertySlow can lead to an exception (StackOverflow) which then bypasses the restoration of the flags in TrySetProperty.
2 parents cbcb437 + 87b168a commit 7291e33

File tree

6 files changed

+428
-402
lines changed

6 files changed

+428
-402
lines changed

lib/Runtime/Base/ThreadContext.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,25 @@ class ThreadContext sealed :
348348
WorkerThread(HANDLE handle = nullptr) :threadHandle(handle){};
349349
};
350350

351+
struct AutoRestoreImplicitFlags
352+
{
353+
ThreadContext * threadContext;
354+
Js::ImplicitCallFlags savedImplicitCallFlags;
355+
DisableImplicitFlags savedDisableImplicitFlags;
356+
AutoRestoreImplicitFlags(ThreadContext *threadContext, Js::ImplicitCallFlags implicitCallFlags, DisableImplicitFlags disableImplicitFlags) :
357+
threadContext(threadContext),
358+
savedImplicitCallFlags(implicitCallFlags),
359+
savedDisableImplicitFlags(disableImplicitFlags)
360+
{
361+
}
362+
363+
~AutoRestoreImplicitFlags()
364+
{
365+
threadContext->SetImplicitCallFlags((Js::ImplicitCallFlags)(savedImplicitCallFlags));
366+
threadContext->SetDisableImplicitFlags((DisableImplicitFlags)savedDisableImplicitFlags);
367+
}
368+
};
369+
351370
void SetCurrentThreadId(DWORD threadId) { this->currentThreadId = threadId; }
352371
DWORD GetCurrentThreadId() const { return this->currentThreadId; }
353372
void SetIsThreadBound()

lib/Runtime/Language/InlineCache.inl

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -184,13 +184,12 @@ namespace Js
184184
bool canSetField; // To verify if we can set a field on the object
185185
Var setterValue = nullptr;
186186
{
187-
// We need to disable implicit call to ensure the check doesn't cause unwanted side effects in debug code
188-
// Save old disableImplicitFlags and implicitCallFlags and disable implicit call and exception
187+
// We need to disable implicit call to ensure the check doesn't cause unwanted side effects in debug
188+
// code Save old disableImplicitFlags and implicitCallFlags and disable implicit call and exception.
189189
ThreadContext * threadContext = requestContext->GetThreadContext();
190-
DisableImplicitFlags disableImplicitFlags = *threadContext->GetAddressOfDisableImplicitFlags();
191-
Js::ImplicitCallFlags implicitCallFlags = threadContext->GetImplicitCallFlags();
190+
ThreadContext::AutoRestoreImplicitFlags autoRestoreImplicitFlags(threadContext, threadContext->GetImplicitCallFlags(), threadContext->GetDisableImplicitFlags());
192191
threadContext->ClearImplicitCallFlags();
193-
*threadContext->GetAddressOfDisableImplicitFlags() = DisableImplicitCallAndExceptionFlag;
192+
threadContext->SetDisableImplicitFlags(DisableImplicitCallAndExceptionFlag);
194193

195194
DescriptorFlags flags = DescriptorFlags::None;
196195
canSetField = !JavascriptOperators::CheckPrototypesForAccessorOrNonWritablePropertySlow(object, propertyId, &setterValue, &flags, isRoot, requestContext);
@@ -199,15 +198,12 @@ namespace Js
199198
canSetField = true; // If there was an implicit call, inconclusive. Disable debug check.
200199
setterValue = nullptr;
201200
}
202-
else
203-
if ((flags & Accessor) == Accessor)
201+
else if ((flags & Accessor) == Accessor)
204202
{
205203
Assert(setterValue != nullptr);
206204
}
207205

208-
// Restore old disableImplicitFlags and implicitCallFlags
209-
*threadContext->GetAddressOfDisableImplicitFlags() = disableImplicitFlags;
210-
threadContext->SetImplicitCallFlags(implicitCallFlags);
206+
// ImplicitCallFlags and DisableImplicitFlags restored by AutoRestoreImplicitFlags' destructor.
211207
}
212208
#endif
213209

lib/Runtime/Library/JsBuiltInEngineInterfaceExtensionObject.cpp

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -131,26 +131,6 @@ namespace Js
131131
return;
132132
}
133133

134-
struct AutoRestoreFlags
135-
{
136-
ThreadContext * ctx;
137-
ImplicitCallFlags savedImplicitCallFlags;
138-
DisableImplicitFlags savedDisableImplicitFlags;
139-
AutoRestoreFlags(ThreadContext *ctx, Js::ImplicitCallFlags implFlags, DisableImplicitFlags disableImplFlags) :
140-
ctx(ctx),
141-
savedImplicitCallFlags(implFlags),
142-
savedDisableImplicitFlags(disableImplFlags)
143-
{
144-
ctx->ClearDisableImplicitFlags();
145-
}
146-
147-
~AutoRestoreFlags()
148-
{
149-
ctx->SetImplicitCallFlags((Js::ImplicitCallFlags)(savedImplicitCallFlags));
150-
ctx->SetDisableImplicitFlags((DisableImplicitFlags)savedDisableImplicitFlags);
151-
}
152-
};
153-
154134
try {
155135
EnsureJsBuiltInByteCode(scriptContext);
156136
Assert(jsBuiltInByteCode != nullptr);
@@ -189,7 +169,8 @@ namespace Js
189169
#endif
190170
// Clear disable implicit call bit as initialization code doesn't have any side effect
191171
{
192-
AutoRestoreFlags autoRestoreFlags(scriptContext->GetThreadContext(), scriptContext->GetThreadContext()->GetImplicitCallFlags(), scriptContext->GetThreadContext()->GetDisableImplicitFlags());
172+
ThreadContext::AutoRestoreImplicitFlags autoRestoreImplicitFlags(scriptContext->GetThreadContext(), scriptContext->GetThreadContext()->GetImplicitCallFlags(), scriptContext->GetThreadContext()->GetDisableImplicitFlags());
173+
scriptContext->GetThreadContext()->ClearDisableImplicitFlags();
193174
JavascriptFunction::CallRootFunctionInScript(functionGlobal, Js::Arguments(callInfo, args));
194175
}
195176

@@ -198,7 +179,8 @@ namespace Js
198179

199180
// Clear disable implicit call bit as initialization code doesn't have any side effect
200181
{
201-
AutoRestoreFlags autoRestoreFlags(scriptContext->GetThreadContext(), scriptContext->GetThreadContext()->GetImplicitCallFlags(), scriptContext->GetThreadContext()->GetDisableImplicitFlags());
182+
ThreadContext::AutoRestoreImplicitFlags autoRestoreImplicitFlags(scriptContext->GetThreadContext(), scriptContext->GetThreadContext()->GetImplicitCallFlags(), scriptContext->GetThreadContext()->GetDisableImplicitFlags());
183+
scriptContext->GetThreadContext()->ClearDisableImplicitFlags();
202184
JavascriptFunction::CallRootFunctionInScript(functionBuiltins, Js::Arguments(callInfo, args));
203185
}
204186

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
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+
var a = [];
7+
function f() {
8+
try {
9+
f();
10+
} catch (e) {
11+
a.foo = 100;
12+
}
13+
}
14+
f();
15+
16+
print("pass");

test/TryCatch/rlexe.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<regress-exe>
3+
<test>
4+
<default>
5+
<files>TryCatchStackOverflow.js</files>
6+
</default>
7+
</test>
8+
</regress-exe>

0 commit comments

Comments
 (0)