diff options
author | Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com> | 2021-04-17 02:33:13 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-17 02:33:13 +0300 |
commit | 4573751df68588c7cd0eb607e7c2b7a66d2d8434 (patch) | |
tree | b5695da2720722a32e845f4deefc53ff3a583cc0 /src/coreclr/vm/gcenv.ee.cpp | |
parent | 689521757637dfbe286436456c97f23eedf8bf19 (diff) |
Fix GC hole with collectible assemblies and tailcalls (#51315)
We must take special care to keep the tailcall dispatcher targets alive
while tailcalls are in-flight. In particular, given the following
callstack:
[B]M2()
[SPC]DispatchTailCalls
[A]M()
it could happen that [B]M2() queued a tail call to a function [B]M3().
Since there is a live dispatcher on the call stack, this would result in
[B]M2() storing a function pointer pointing to [B]M3() and returning to
this dispatcher to let it take care of the tailcall.
If B was loaded in a collectible ALC, it would then be possible for
there to be nothing keeping this ALC alive, and for the assembly to be
unloaded before the dispatcher invoked the function pointer.
I was unable to come up with a test case where this happened without
making changes to the dispatcher; the window otherwise seems to be too
small. To reproduce the problem I thus had to add a Thread.Sleep(50)
into the dispatcher, which quickly resulted in an
AccessViolationException in the scenario above. With the changes in this
commit I was then no logner able to reproduce the problem.
Fix #41314
Diffstat (limited to 'src/coreclr/vm/gcenv.ee.cpp')
-rw-r--r-- | src/coreclr/vm/gcenv.ee.cpp | 17 |
1 files changed, 16 insertions, 1 deletions
diff --git a/src/coreclr/vm/gcenv.ee.cpp b/src/coreclr/vm/gcenv.ee.cpp index 7849958d068..ea327cc73d8 100644 --- a/src/coreclr/vm/gcenv.ee.cpp +++ b/src/coreclr/vm/gcenv.ee.cpp @@ -157,7 +157,22 @@ static void ScanStackRoots(Thread * pThread, promote_func* fn, ScanContext* sc) static void ScanTailCallArgBufferRoots(Thread* pThread, promote_func* fn, ScanContext* sc) { - TailCallArgBuffer* argBuffer = pThread->GetTailCallTls()->GetArgBuffer(); + TailCallTls* tls = pThread->GetTailCallTls(); + // Keep loader associated with CallTailCallTarget alive. + if (sc->promotion) + { +#ifndef DACCESS_COMPILE + const PortableTailCallFrame* frame = tls->GetFrame(); + if (frame->NextCall != NULL) + { + MethodDesc* pMD = Entry2MethodDesc((PCODE)frame->NextCall, NULL); + if (pMD != NULL) + GcReportLoaderAllocator(fn, sc, pMD->GetLoaderAllocator()); + } +#endif + } + + TailCallArgBuffer* argBuffer = tls->GetArgBuffer(); if (argBuffer == NULL || argBuffer->GCDesc == NULL) return; |