This is an archive of the discontinued LLVM Phabricator instance.

[lldb][windows] Fix crash on getting nested exception
ClosedPublic

Authored by alvinhochun on Jun 20 2022, 6:40 AM.

Details

Summary

LLDB tries to follow EXCEPTION_RECORD::ExceptionRecord to follow the
nested exception chain. In practice this code just causes Access
Violation whenever there is a nested exception. Since there does not
appear to be any code in LLDB that is actually using the nested
exceptions, this change just removes the crashing code and adds a
comment for future reference.

Fixes https://github.com/mstorsjo/llvm-mingw/issues/292

Diff Detail

Event Timeline

alvinhochun created this revision.Jun 20 2022, 6:40 AM
Herald added a project: Restricted Project. · View Herald TranscriptJun 20 2022, 6:40 AM
Herald added a subscriber: mstorsjo. · View Herald Transcript
alvinhochun requested review of this revision.Jun 20 2022, 6:40 AM
Herald added a project: Restricted Project. · View Herald TranscriptJun 20 2022, 6:40 AM
DavidSpickett added a subscriber: DavidSpickett.

Any reason to #if 0 this instead of just removing it and maybe adding a one line comment like "nested exceptions are not supported"? So that someone can git blame that and find this commit with the removal.

If this is not used at all this is academic, but what situation would it be describing if it did? Is it something like if you had an exception in a signal handler, that was handling an initial exception?

alvinhochun added a comment.EditedJun 20 2022, 9:17 AM

Any reason to #if 0 this instead of just removing it and maybe adding a one line comment like "nested exceptions are not supported"? So that someone can git blame that and find this commit with the removal.

I want to leave a clear note explaining what not to do with record.ExceptionRecord. In case someone want to implement handling of nested exceptions in the future, they will be more likely to see it and not repeat the mistake. Though I suppose just having the comment is fine and we don't really need to keep all the code in #if 0.

If this is not used at all this is academic, but what situation would it be describing if it did? Is it something like if you had an exception in a signal handler, that was handling an initial exception?

I am not familiar with the inner workings of exception handling on Windows and there aren't any references I could find, so this is just speculation.

In the sample program from https://github.com/mstorsjo/llvm-mingw/issues/292#issuecomment-1160239522, I am triggering an Access Violation (segfault) inside WindowProc. An Access Violation will produce the exception 0xc0000005, Debuggers will get a first chance exception at that point. (GDB and WinDbg both break execution on this, but LLDB decides to let the debuggee continue for some reason.) After this, the exception is passed to an exception handler if there is one.

Because WindowProc is called by Windows, there seems to have an exception handler internally. For whatever reason it then decides to throw its own exception 0xc000041d (it seems undocumented but does have a message: "An unhandled exception was encountered during a user callback."). I suspect here the nested exception would contain info about the original exception (Access Violation 0xc0000005).

There seems to be an undocumented NtRaiseException@12 function that is used to raise the exception with a EXCEPTION_RECORD directly (which can contain a nested exception). The normal RaiseException API in kernel32 only allows specifying the exception code, flag and arguments.

Update: Actually it seems the documented RtlRaiseException also take an EXCEPTION_RECORD.

alvinhochun edited the summary of this revision. (Show Details)

Remove old code instead of #if 0.

This revision is now accepted and ready to land.Jun 21 2022, 12:52 AM
This revision was automatically updated to reflect the committed changes.
labath added a subscriber: labath.Jun 23 2022, 12:12 AM

How can one trigger this behavior? Adding a test would be preferable to a long comment (we can have both, but the need for a (long) comment diminishes if you have a test which will break if someone implements it the wrong way.