- Fix some insidious bugs in exception handling, mostly related to the art of unwinding (RtlUnwind).

- Can't name specifics, but probably fixes multiple SEH/application bugs/regressions since about 6-8 months ago. Fixes my personal SEH test from 22 failures/crashes/BSODs to 22 succeesses...
- Also fixes some crashes in kernel-kqemu mode.

svn path=/trunk/; revision=24178
This commit is contained in:
Alex Ionescu 2006-09-17 21:09:10 +00:00
parent 45f71f78c8
commit 3e709fbbd9
3 changed files with 107 additions and 98 deletions

View file

@ -26,14 +26,12 @@ RtlRaiseException(PEXCEPTION_RECORD ExceptionRecord)
{ {
CONTEXT Context; CONTEXT Context;
NTSTATUS Status; NTSTATUS Status;
DPRINT1("RtlRaiseException(Status %p)\n", ExceptionRecord);
/* Capture the context */ /* Capture the context */
RtlCaptureContext(&Context); RtlCaptureContext(&Context);
/* Save the exception address */ /* Save the exception address */
ExceptionRecord->ExceptionAddress = RtlpGetExceptionAddress(); ExceptionRecord->ExceptionAddress = RtlpGetExceptionAddress();
DPRINT1("ExceptionAddress %p\n", ExceptionRecord->ExceptionAddress);
/* Write the context flag */ /* Write the context flag */
Context.ContextFlags = CONTEXT_FULL; Context.ContextFlags = CONTEXT_FULL;
@ -72,7 +70,6 @@ RtlRaiseStatus(NTSTATUS Status)
{ {
EXCEPTION_RECORD ExceptionRecord; EXCEPTION_RECORD ExceptionRecord;
CONTEXT Context; CONTEXT Context;
DPRINT1("RtlRaiseStatus(Status 0x%.08lx)\n", Status);
/* Capture the context */ /* Capture the context */
RtlCaptureContext(&Context); RtlCaptureContext(&Context);

View file

@ -23,7 +23,7 @@
_RtlpGetExceptionList@0: _RtlpGetExceptionList@0:
/* Return the exception list */ /* Return the exception list */
mov eax, [fs:TEB_EXCEPTION_LIST] mov eax, fs:[TEB_EXCEPTION_LIST]
ret ret
.endfunc .endfunc
@ -36,7 +36,7 @@ _RtlpSetExceptionList@4:
mov ecx, [ecx] mov ecx, [ecx]
/* Write it */ /* Write it */
mov [fs:TEB_EXCEPTION_LIST], ecx mov fs:[TEB_EXCEPTION_LIST], ecx
/* Return */ /* Return */
ret 4 ret 4

View file

@ -29,6 +29,11 @@ VOID
NTAPI NTAPI
RtlpSetExceptionList(PEXCEPTION_REGISTRATION_RECORD NewExceptionList); RtlpSetExceptionList(PEXCEPTION_REGISTRATION_RECORD NewExceptionList);
typedef struct _DISPATCHER_CONTEXT
{
PEXCEPTION_REGISTRATION_RECORD RegistrationPointer;
} DISPATCHER_CONTEXT, *PDISPATCHER_CONTEXT;
/* PUBLIC FUNCTIONS **********************************************************/ /* PUBLIC FUNCTIONS **********************************************************/
/* /*
@ -51,9 +56,9 @@ RtlDispatchException(IN PEXCEPTION_RECORD ExceptionRecord,
IN PCONTEXT Context) IN PCONTEXT Context)
{ {
PEXCEPTION_REGISTRATION_RECORD RegistrationFrame, NestedFrame = NULL; PEXCEPTION_REGISTRATION_RECORD RegistrationFrame, NestedFrame = NULL;
PEXCEPTION_REGISTRATION_RECORD DispatcherContext; DISPATCHER_CONTEXT DispatcherContext;
EXCEPTION_RECORD ExceptionRecord2; EXCEPTION_RECORD ExceptionRecord2;
EXCEPTION_DISPOSITION ReturnValue; EXCEPTION_DISPOSITION Disposition;
ULONG_PTR StackLow, StackHigh; ULONG_PTR StackLow, StackHigh;
ULONG_PTR RegistrationFrameEnd; ULONG_PTR RegistrationFrameEnd;
@ -85,9 +90,6 @@ RtlDispatchException(IN PEXCEPTION_RECORD ExceptionRecord,
/* Set invalid stack and return false */ /* Set invalid stack and return false */
ExceptionRecord->ExceptionFlags |= EXCEPTION_STACK_INVALID; ExceptionRecord->ExceptionFlags |= EXCEPTION_STACK_INVALID;
DPRINT1("Invalid exception frame: %p %p %p %p\n",
RegistrationFrame, RegistrationFrameEnd,
StackHigh, StackLow);
return FALSE; return FALSE;
} }
@ -98,11 +100,12 @@ RtlDispatchException(IN PEXCEPTION_RECORD ExceptionRecord,
sizeof(*RegistrationFrame)); sizeof(*RegistrationFrame));
/* Call the handler */ /* Call the handler */
ReturnValue = RtlpExecuteHandlerForException(ExceptionRecord, Disposition = RtlpExecuteHandlerForException(ExceptionRecord,
RegistrationFrame, RegistrationFrame,
Context, Context,
&DispatcherContext, &DispatcherContext,
RegistrationFrame->Handler); RegistrationFrame->
Handler);
/* Check if this is a nested frame */ /* Check if this is a nested frame */
if (RegistrationFrame == NestedFrame) if (RegistrationFrame == NestedFrame)
@ -113,14 +116,18 @@ RtlDispatchException(IN PEXCEPTION_RECORD ExceptionRecord,
} }
/* Handle the dispositions */ /* Handle the dispositions */
if (ReturnValue == ExceptionContinueExecution) switch (Disposition)
{ {
/* Continue searching */
case ExceptionContinueExecution:
/* Check if it was non-continuable */ /* Check if it was non-continuable */
if (ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE) if (ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{ {
/* Set up the exception record */ /* Set up the exception record */
ExceptionRecord2.ExceptionRecord = ExceptionRecord; ExceptionRecord2.ExceptionRecord = ExceptionRecord;
ExceptionRecord2.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION; ExceptionRecord2.ExceptionCode =
STATUS_NONCONTINUABLE_EXCEPTION;
ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE; ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
ExceptionRecord2.NumberParameters = 0; ExceptionRecord2.NumberParameters = 0;
@ -132,21 +139,28 @@ RtlDispatchException(IN PEXCEPTION_RECORD ExceptionRecord,
/* Return to caller */ /* Return to caller */
return TRUE; return TRUE;
} }
}
else if (ReturnValue == ExceptionNestedException) /* Continue searching */
{ case ExceptionContinueSearch:
break;
/* Nested exception */
case ExceptionNestedException:
/* Turn the nested flag on */ /* Turn the nested flag on */
ExceptionRecord->ExceptionFlags |= EXCEPTION_NESTED_CALL; ExceptionRecord->ExceptionFlags |= EXCEPTION_NESTED_CALL;
/* Update the current nested frame */ /* Update the current nested frame */
if (NestedFrame < DispatcherContext) NestedFrame = DispatcherContext; if (DispatcherContext.RegistrationPointer > NestedFrame)
}
else if (ReturnValue == ExceptionContinueSearch)
{ {
/* Do nothing */ /* Get the frame from the dispatcher context */
NestedFrame = DispatcherContext.RegistrationPointer;
} }
else break;
{
/* Anything else */
default:
/* Set up the exception record */ /* Set up the exception record */
ExceptionRecord2.ExceptionRecord = ExceptionRecord; ExceptionRecord2.ExceptionRecord = ExceptionRecord;
ExceptionRecord2.ExceptionCode = STATUS_INVALID_DISPOSITION; ExceptionRecord2.ExceptionCode = STATUS_INVALID_DISPOSITION;
@ -155,6 +169,7 @@ RtlDispatchException(IN PEXCEPTION_RECORD ExceptionRecord,
/* Raise the exception */ /* Raise the exception */
RtlRaiseException(&ExceptionRecord2); RtlRaiseException(&ExceptionRecord2);
break;
} }
/* Go to the next frame */ /* Go to the next frame */
@ -170,15 +185,15 @@ RtlDispatchException(IN PEXCEPTION_RECORD ExceptionRecord,
*/ */
VOID VOID
NTAPI NTAPI
RtlUnwind(PVOID RegistrationFrame OPTIONAL, RtlUnwind(IN PVOID TargetFrame OPTIONAL,
PVOID ReturnAddress OPTIONAL, IN PVOID TargetIp OPTIONAL,
PEXCEPTION_RECORD ExceptionRecord OPTIONAL, IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL,
PVOID EaxValue) IN PVOID ReturnValue)
{ {
PEXCEPTION_REGISTRATION_RECORD RegistrationFrame2, OldFrame; PEXCEPTION_REGISTRATION_RECORD RegistrationFrame, OldFrame;
PEXCEPTION_REGISTRATION_RECORD DispatcherContext; DISPATCHER_CONTEXT DispatcherContext;
EXCEPTION_RECORD ExceptionRecord2, ExceptionRecord3; EXCEPTION_RECORD ExceptionRecord2, ExceptionRecord3;
EXCEPTION_DISPOSITION ReturnValue; EXCEPTION_DISPOSITION Disposition;
ULONG_PTR StackLow, StackHigh; ULONG_PTR StackLow, StackHigh;
ULONG_PTR RegistrationFrameEnd; ULONG_PTR RegistrationFrameEnd;
CONTEXT LocalContext; CONTEXT LocalContext;
@ -202,7 +217,7 @@ RtlUnwind(PVOID RegistrationFrame OPTIONAL,
} }
/* Check if we have a frame */ /* Check if we have a frame */
if (RegistrationFrame) if (TargetFrame)
{ {
/* Set it as unwinding */ /* Set it as unwinding */
ExceptionRecord->ExceptionFlags |= EXCEPTION_UNWINDING; ExceptionRecord->ExceptionFlags |= EXCEPTION_UNWINDING;
@ -222,30 +237,26 @@ RtlUnwind(PVOID RegistrationFrame OPTIONAL,
RtlpCaptureContext(Context); RtlpCaptureContext(Context);
/* Pop the current arguments off */ /* Pop the current arguments off */
Context->Esp += sizeof(RegistrationFrame) + Context->Esp += sizeof(TargetFrame) +
sizeof(ReturnAddress) + sizeof(TargetIp) +
sizeof(ExceptionRecord) + sizeof(ExceptionRecord) +
sizeof(ReturnValue); sizeof(ReturnValue);
/* Set the new value for EAX */ /* Set the new value for EAX */
Context->Eax = (ULONG)EaxValue; Context->Eax = (ULONG)ReturnValue;
/* Get the current frame */ /* Get the current frame */
RegistrationFrame2 = RtlpGetExceptionList(); RegistrationFrame = RtlpGetExceptionList();
/* Now loop every frame */ /* Now loop every frame */
while (RegistrationFrame2 != EXCEPTION_CHAIN_END) while (RegistrationFrame != EXCEPTION_CHAIN_END)
{ {
/* If this is the target */ /* If this is the target */
if (RegistrationFrame2 == RegistrationFrame) if (RegistrationFrame == TargetFrame) ZwContinue(Context, FALSE);
{
/* Continue execution */
ZwContinue(Context, FALSE);
}
/* Check if the frame is too low */ /* Check if the frame is too low */
if ((RegistrationFrame) && ((ULONG_PTR)RegistrationFrame < if ((TargetFrame) &&
(ULONG_PTR)RegistrationFrame2)) ((ULONG_PTR)TargetFrame < (ULONG_PTR)RegistrationFrame))
{ {
/* Create an invalid unwind exception */ /* Create an invalid unwind exception */
ExceptionRecord2.ExceptionCode = STATUS_INVALID_UNWIND_TARGET; ExceptionRecord2.ExceptionCode = STATUS_INVALID_UNWIND_TARGET;
@ -258,8 +269,8 @@ RtlUnwind(PVOID RegistrationFrame OPTIONAL,
} }
/* Find out where it ends */ /* Find out where it ends */
RegistrationFrameEnd = (ULONG_PTR)RegistrationFrame2 + RegistrationFrameEnd = (ULONG_PTR)RegistrationFrame +
sizeof(*RegistrationFrame2); sizeof(EXCEPTION_REGISTRATION_RECORD);
/* Make sure the registration frame is located within the stack */ /* Make sure the registration frame is located within the stack */
if ((RegistrationFrameEnd > StackHigh) || if ((RegistrationFrameEnd > StackHigh) ||
@ -283,30 +294,33 @@ RtlUnwind(PVOID RegistrationFrame OPTIONAL,
ExceptionRecord2.NumberParameters = 0; ExceptionRecord2.NumberParameters = 0;
/* Raise the exception */ /* Raise the exception */
DPRINT1("Frame has bad stack\n");
RtlRaiseException(&ExceptionRecord2); RtlRaiseException(&ExceptionRecord2);
} }
else else
{ {
/* Call the handler */ /* Call the handler */
ReturnValue = RtlpExecuteHandlerForUnwind(ExceptionRecord, Disposition = RtlpExecuteHandlerForUnwind(ExceptionRecord,
RegistrationFrame2, RegistrationFrame,
Context, Context,
&DispatcherContext, &DispatcherContext,
RegistrationFrame2->Handler); RegistrationFrame->
Handler);
switch (Disposition)
{
/* Continue searching */
case ExceptionContinueSearch:
break;
/* Collission */
case ExceptionCollidedUnwind :
/* Get the original frame */
RegistrationFrame = DispatcherContext.RegistrationPointer;
break;
/* Anything else */
default:
/* Handle the dispositions */
if (ReturnValue == ExceptionContinueSearch)
{
/* Do nothing */
}
else if (ReturnValue == ExceptionCollidedUnwind)
{
/* Get the previous frame */
RegistrationFrame2 = DispatcherContext;
}
else
{
/* Set up the exception record */ /* Set up the exception record */
ExceptionRecord2.ExceptionRecord = ExceptionRecord; ExceptionRecord2.ExceptionRecord = ExceptionRecord;
ExceptionRecord2.ExceptionCode = STATUS_INVALID_DISPOSITION; ExceptionRecord2.ExceptionCode = STATUS_INVALID_DISPOSITION;
@ -315,22 +329,20 @@ RtlUnwind(PVOID RegistrationFrame OPTIONAL,
/* Raise the exception */ /* Raise the exception */
RtlRaiseException(&ExceptionRecord2); RtlRaiseException(&ExceptionRecord2);
break;
} }
/* Go to the next frame */ /* Go to the next frame */
OldFrame = RegistrationFrame2; OldFrame = RegistrationFrame;
RegistrationFrame2 = RegistrationFrame2->Next; RegistrationFrame = RegistrationFrame->Next;
/* Remove this handler */ /* Remove this handler */
if (RegistrationFrame2 != RegistrationFrame)
{
RtlpSetExceptionList(OldFrame); RtlpSetExceptionList(OldFrame);
} }
} }
}
/* Check if we reached the end */ /* Check if we reached the end */
if (RegistrationFrame == EXCEPTION_CHAIN_END) if (TargetFrame == EXCEPTION_CHAIN_END)
{ {
/* Unwind completed, so we don't exit */ /* Unwind completed, so we don't exit */
ZwContinue(Context, FALSE); ZwContinue(Context, FALSE);