Fix GC issues during exception handling on Unix
authorJan Vorlicek <janvorli@microsoft.com>
Thu, 13 Aug 2015 10:27:31 +0000 (12:27 +0200)
committerJan Vorlicek <janvorli@microsoft.com>
Thu, 13 Aug 2015 14:32:14 +0000 (16:32 +0200)
commit63653cf406ead63a79ae52920e050c385a8f2e37
treedb1502468b443faa3feb6f96ad1246f6ca76b755
parent42cf2d9a2a97a796a5a6c1080e56f15ee648234a
Fix GC issues during exception handling on Unix

This change fixes two issues that happens in some cases when GC scans stack of
a thread that is handling exception at that moment.

First issue was caused by the fact that the stack walker wasn't modified to
take into account the difference in exception handling on Unix. When an exception
is thrown from a catch handler or finally block (a funclet )on Unix, part of the stack
is unwound immediatelly, while on Windows, the stack is not reclaimed until
the exception is fully handled.
The problem was caused by the fact that when GC happens in the funclet before the exception
is processed, but after a point where GC knows that the lifetime of locals in the caller frame
is over, it doesn't update the references in the caller frame for objects that it has relocated.
On Windows, this is detected just from walking the stack, since the funclet frame is still there
and the stack walker can then skip scanning the parent frame GC references.
On Linux, the funclet is not on stack anymore, so this case was not detected and GC attempted to
scan the stale references and crashed.
The fix was to detect that a frame was a caller to a funclet from the chain of previous exception
trackers that is fortunately preserved and the trackers hold the necessary information.

The second issue was more subtle. During interleaved exception handling, when we unwind a native
portion of the stack and switch back to unwinding a managed block of stack frames, we re-create
the exception tracker and carry over just a few members necessary to continue processing the
same exception. The way it was done was that we have first removed the current tracker from
the list of trackers of the current thread, then we have destroyed it, created a new one and
put it back to the front of the list.
The issue happened when GC started walking the stack of the thread in the small time slot when
the current tracker was removed from the list, but the re-created tracker was not added there yet.
Then the detection necessary for handling the previous issue didn't work and we got a crash.
The fix was to make the whole re-creation of the exception tracker atomic w.r.t. the GC.
src/vm/exceptionhandling.cpp
src/vm/stackwalk.cpp