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