Hey everyone,
a few days ago, I started investigating a report in Bugzilla containing an application that raises an exception that goes unhandled, and the stack trace that gets printed is clearly corrupt.
The report is the following: http://www.reactos.org/bugzilla/show_bug.cgi?id=5684
At first I thought the context wasn't being recorded properly, since both EBP and ESP are reported as being 0 at the time the exception was raised. However, there are applications for which we show a correct stack trace in case of an unhandled exception, so this most likely wasn't the case.
My next thought was that we were somehow corrupting the context, but this wasn't the case either. The exception is also being dispatched nicely: exception handlers get the correct details.
Lastly, the UnhandledExceptionFilter seemed to be passing the context on to the stack trace printing function without altering it, so the problem isn't there either. So where else could something be happening? That's right, inside one of the exception handler functions.
Some of you may recognize the exception code (0xeedfade) in the bug report as a Delphi exception. I did too, so I fired up my copy of Delphi 6 and went digging in its exception handler functions.
Every occurrence of UnhandledExceptionFilter in its exception handler functions showed the context wasn't being passed to it, but instead the registration frame was. This happens in a very predictive way however, so creating a patch (read: filthy hack) that detects this and fixes up the context record was easy. It is heavily tied to exactly this kind of defect, no general case.
Running the application still triggers the exception of course, but now we get a reliable stack trace. A naive search in Bugzilla indicates at least the following reports are affected by this problem:
Bug 3602, bug 3927, bug 5123 and bug 5684.
I haven't tested them all, but the tell-tale signs are there.
What does one do when encountering such a bug in compiler? Check if it still exists in the latest version and file a bug report of course. I got confirmation that the bug is present in the recently released Delphi XE with the help of Marco van de Voort of the FreePascal project (shameless plug, I know).
A bug report has been filed with what I consider an extreme amount of detail and how to solve it in their product. Did I mention the report also contains a shameless plug to the ReactOS project?
Today, to my astonishment, I found another report on their site which.. you guessed it.. mentions this problem. This report appears to have been made in 2007, so guessing when (newly compiled) Delphi applications will finally start calling UnhandledExceptionFilter correctly is an exercise I leave to the reader.
Now to get to the question that some of you may already be asking, and is the purpose of this mail: Are we putting this hack into our codebase so we get a decent stack trace when an application messes up in the manner this mail is about, or not? Applications can mess this up in a lot of different ways, so it won't work on those.
I warmly invite everyone to weigh in on this attempt of a discussion (which I hope will be productive), and hope people will see (again) that not everything that goes wrong in ReactOS is the fault of the OS.
References for those interested in the nasty details:
Bug report: http://qc.embarcadero.com/wc/qcmain.aspx?d=89377
Patch:
Index: dll/win32/kernel32/except/except.c =================================================================== --- dll/win32/kernel32/except/except.c (revision 49448) +++ dll/win32/kernel32/except/except.c (working copy) @@ -218,6 +218,13 @@ PEXCEPTION_RECORD ExceptionRecord = ExceptionInfo->ExceptionRecord; PCONTEXT ContextRecord = ExceptionInfo->ContextRecord;
+ /* Attempt to hack around bad calls of UnhandledExceptionFilter */ + if ((ULONG_PTR) (ExceptionRecord->ExceptionAddress) != (ULONG_PTR) (ContextRecord->Eip)) + { + DPRINT1("Bogus context detected, using context record HACK!!\n"); + ContextRecord = (PCONTEXT) ExceptionInfo[1].ExceptionRecord; + } + /* Print a stack trace. */ DbgPrint("Unhandled exception\n"); DbgPrint("ExceptionCode: %8x\n", ExceptionRecord->ExceptionCode); @@ -415,11 +422,17 @@
if (dwExceptionCode == 0xeedface) { - DPRINT1("Delphi Exception at address: %p\n", ExceptionRecord.ExceptionInformation[0]); + DPRINT1("Delphi 2 Exception at address: %p\n", ExceptionRecord.ExceptionInformation[0]); DPRINT1("Exception-Object: %p\n", ExceptionRecord.ExceptionInformation[1]); DPRINT1("Exception text: %s\n", ExceptionRecord.ExceptionInformation[2]); }
+ if (dwExceptionCode == 0xeedfade) + { + DPRINT1("Delphi Exception at address: %p\n", ExceptionRecord.ExceptionInformation[0]); + DPRINT1("Exception-Object: %p\n", ExceptionRecord.ExceptionInformation[1]); + } + /* Trace the wine special error and show the modulename and functionname */
if (dwExceptionCode == 0x80000100 /*EXCEPTION_WINE_STUB*/) {
WBR,
Roel Messiant