reactos/sdk/lib/rtl/i386/except.c
Hermès Bélusca-Maïto a4b6e0d929
[SDK:RTL] Add one validity check + comment documentation for RtlDispatchException().
- RtlDispatchException(): Check for invalid stack in ExceptionContinueSearch handler
  and bail out if so.
- Update few comments and fix a typo.
- Add a documenting comment about SafeSEH functionality support.
  See e.g. the following articles:
  https://www.optiv.com/blog/old-meets-new-microsoft-windows-safeseh-incompatibility
  https://msrc-blog.microsoft.com/2012/01/10/more-information-on-the-impact-of-ms12-001/
2019-11-17 23:21:47 +01:00

405 lines
13 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Run-Time Library
* PURPOSE: User-mode exception support for IA-32
* FILE: lib/rtl/i386/except.c
* PROGRAMERS: Alex Ionescu (alex@relsoft.net)
* Casper S. Hornstrup (chorns@users.sourceforge.net)
*/
/* INCLUDES *****************************************************************/
#include <rtl.h>
#define NDEBUG
#include <debug.h>
/* PUBLIC FUNCTIONS **********************************************************/
/*
* @implemented
*/
VOID
NTAPI
RtlGetCallersAddress(OUT PVOID *CallersAddress,
OUT PVOID *CallersCaller)
{
USHORT FrameCount;
PVOID BackTrace[2];
PULONG BackTraceHash = NULL;
/* Get the tow back trace address */
FrameCount = RtlCaptureStackBackTrace(2, 2, &BackTrace[0],BackTraceHash);
/* Only if user want it */
if (CallersAddress != NULL)
{
/* only when first frames exist */
if (FrameCount >= 1)
{
*CallersAddress = BackTrace[0];
}
else
{
*CallersAddress = NULL;
}
}
/* Only if user want it */
if (CallersCaller != NULL)
{
/* only when second frames exist */
if (FrameCount >= 2)
{
*CallersCaller = BackTrace[1];
}
else
{
*CallersCaller = NULL;
}
}
}
/*
* @implemented
*/
BOOLEAN
NTAPI
RtlDispatchException(IN PEXCEPTION_RECORD ExceptionRecord,
IN PCONTEXT Context)
{
PEXCEPTION_REGISTRATION_RECORD RegistrationFrame, NestedFrame = NULL;
DISPATCHER_CONTEXT DispatcherContext;
EXCEPTION_RECORD ExceptionRecord2;
EXCEPTION_DISPOSITION Disposition;
ULONG_PTR StackLow, StackHigh;
ULONG_PTR RegistrationFrameEnd;
/* Perform vectored exception handling for user mode */
if (RtlCallVectoredExceptionHandlers(ExceptionRecord, Context))
{
/* Exception handled, now call vectored continue handlers */
RtlCallVectoredContinueHandlers(ExceptionRecord, Context);
/* Continue execution */
return TRUE;
}
/* Get the current stack limits and registration frame */
RtlpGetStackLimits(&StackLow, &StackHigh);
RegistrationFrame = RtlpGetExceptionList();
/* Now loop every frame */
while (RegistrationFrame != EXCEPTION_CHAIN_END)
{
/* Registration chain entries are never NULL */
ASSERT(RegistrationFrame != NULL);
/* Find out where it ends */
RegistrationFrameEnd = (ULONG_PTR)RegistrationFrame +
sizeof(EXCEPTION_REGISTRATION_RECORD);
/* Make sure the registration frame is located within the stack */
if ((RegistrationFrameEnd > StackHigh) ||
((ULONG_PTR)RegistrationFrame < StackLow) ||
((ULONG_PTR)RegistrationFrame & 0x3))
{
/* Check if this happened in the DPC Stack */
if (RtlpHandleDpcStackException(RegistrationFrame,
RegistrationFrameEnd,
&StackLow,
&StackHigh))
{
/* Use DPC Stack Limits and restart */
continue;
}
/* Set invalid stack and bail out */
ExceptionRecord->ExceptionFlags |= EXCEPTION_STACK_INVALID;
return FALSE;
}
//
// TODO: Implement and call here RtlIsValidHandler(RegistrationFrame->Handler)
// for supporting SafeSEH functionality, see the following articles:
// https://www.optiv.com/blog/old-meets-new-microsoft-windows-safeseh-incompatibility
// https://msrc-blog.microsoft.com/2012/01/10/more-information-on-the-impact-of-ms12-001/
//
/* Check if logging is enabled */
RtlpCheckLogException(ExceptionRecord,
Context,
RegistrationFrame,
sizeof(*RegistrationFrame));
/* Call the handler */
Disposition = RtlpExecuteHandlerForException(ExceptionRecord,
RegistrationFrame,
Context,
&DispatcherContext,
RegistrationFrame->Handler);
/* Check if this is a nested frame */
if (RegistrationFrame == NestedFrame)
{
/* Mask out the flag and the nested frame */
ExceptionRecord->ExceptionFlags &= ~EXCEPTION_NESTED_CALL;
NestedFrame = NULL;
}
/* Handle the dispositions */
switch (Disposition)
{
/* Continue execution */
case ExceptionContinueExecution:
{
/* Check if it was non-continuable */
if (ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{
/* Set up the exception record */
ExceptionRecord2.ExceptionRecord = ExceptionRecord;
ExceptionRecord2.ExceptionCode =
STATUS_NONCONTINUABLE_EXCEPTION;
ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
ExceptionRecord2.NumberParameters = 0;
/* Raise the exception */
RtlRaiseException(&ExceptionRecord2);
}
else
{
/* In user mode, call any registered vectored continue handlers */
RtlCallVectoredContinueHandlers(ExceptionRecord, Context);
/* Execution continues */
return TRUE;
}
}
/* Continue searching */
case ExceptionContinueSearch:
if (ExceptionRecord->ExceptionFlags & EXCEPTION_STACK_INVALID)
{
/* We have an invalid stack, bail out */
return FALSE;
}
break;
/* Nested exception */
case ExceptionNestedException:
{
/* Turn the nested flag on */
ExceptionRecord->ExceptionFlags |= EXCEPTION_NESTED_CALL;
/* Update the current nested frame */
if (DispatcherContext.RegistrationPointer > NestedFrame)
{
/* Get the frame from the dispatcher context */
NestedFrame = DispatcherContext.RegistrationPointer;
}
break;
}
/* Anything else */
default:
{
/* Set up the exception record */
ExceptionRecord2.ExceptionRecord = ExceptionRecord;
ExceptionRecord2.ExceptionCode = STATUS_INVALID_DISPOSITION;
ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
ExceptionRecord2.NumberParameters = 0;
/* Raise the exception */
RtlRaiseException(&ExceptionRecord2);
break;
}
}
/* Go to the next frame */
RegistrationFrame = RegistrationFrame->Next;
}
/* Unhandled, bail out */
return FALSE;
}
/*
* @implemented
*/
VOID
NTAPI
RtlUnwind(IN PVOID TargetFrame OPTIONAL,
IN PVOID TargetIp OPTIONAL,
IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL,
IN PVOID ReturnValue)
{
PEXCEPTION_REGISTRATION_RECORD RegistrationFrame, OldFrame;
DISPATCHER_CONTEXT DispatcherContext;
EXCEPTION_RECORD ExceptionRecord2, ExceptionRecord3;
EXCEPTION_DISPOSITION Disposition;
ULONG_PTR StackLow, StackHigh;
ULONG_PTR RegistrationFrameEnd;
CONTEXT LocalContext;
PCONTEXT Context;
/* Get the current stack limits */
RtlpGetStackLimits(&StackLow, &StackHigh);
/* Check if we don't have an exception record */
if (!ExceptionRecord)
{
/* Overwrite the argument */
ExceptionRecord = &ExceptionRecord3;
/* Setup a local one */
ExceptionRecord3.ExceptionFlags = 0;
ExceptionRecord3.ExceptionCode = STATUS_UNWIND;
ExceptionRecord3.ExceptionRecord = NULL;
ExceptionRecord3.ExceptionAddress = _ReturnAddress();
ExceptionRecord3.NumberParameters = 0;
}
/* Check if we have a frame */
if (TargetFrame)
{
/* Set it as unwinding */
ExceptionRecord->ExceptionFlags |= EXCEPTION_UNWINDING;
}
else
{
/* Set the Exit Unwind flag as well */
ExceptionRecord->ExceptionFlags |= (EXCEPTION_UNWINDING |
EXCEPTION_EXIT_UNWIND);
}
/* Now capture the context */
Context = &LocalContext;
LocalContext.ContextFlags = CONTEXT_INTEGER |
CONTEXT_CONTROL |
CONTEXT_SEGMENTS;
RtlpCaptureContext(Context);
/* Pop the current arguments off */
Context->Esp += sizeof(TargetFrame) +
sizeof(TargetIp) +
sizeof(ExceptionRecord) +
sizeof(ReturnValue);
/* Set the new value for EAX */
Context->Eax = (ULONG)ReturnValue;
/* Get the current frame */
RegistrationFrame = RtlpGetExceptionList();
/* Now loop every frame */
while (RegistrationFrame != EXCEPTION_CHAIN_END)
{
/* Registration chain entries are never NULL */
ASSERT(RegistrationFrame != NULL);
/* If this is the target */
if (RegistrationFrame == TargetFrame) ZwContinue(Context, FALSE);
/* Check if the frame is too low */
if ((TargetFrame) &&
((ULONG_PTR)TargetFrame < (ULONG_PTR)RegistrationFrame))
{
/* Create an invalid unwind exception */
ExceptionRecord2.ExceptionCode = STATUS_INVALID_UNWIND_TARGET;
ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
ExceptionRecord2.ExceptionRecord = ExceptionRecord;
ExceptionRecord2.NumberParameters = 0;
/* Raise the exception */
RtlRaiseException(&ExceptionRecord2);
}
/* Find out where it ends */
RegistrationFrameEnd = (ULONG_PTR)RegistrationFrame +
sizeof(EXCEPTION_REGISTRATION_RECORD);
/* Make sure the registration frame is located within the stack */
if ((RegistrationFrameEnd > StackHigh) ||
((ULONG_PTR)RegistrationFrame < StackLow) ||
((ULONG_PTR)RegistrationFrame & 0x3))
{
/* Check if this happened in the DPC Stack */
if (RtlpHandleDpcStackException(RegistrationFrame,
RegistrationFrameEnd,
&StackLow,
&StackHigh))
{
/* Use DPC Stack Limits and restart */
continue;
}
/* Create an invalid stack exception */
ExceptionRecord2.ExceptionCode = STATUS_BAD_STACK;
ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
ExceptionRecord2.ExceptionRecord = ExceptionRecord;
ExceptionRecord2.NumberParameters = 0;
/* Raise the exception */
RtlRaiseException(&ExceptionRecord2);
}
else
{
/* Call the handler */
Disposition = RtlpExecuteHandlerForUnwind(ExceptionRecord,
RegistrationFrame,
Context,
&DispatcherContext,
RegistrationFrame->Handler);
switch(Disposition)
{
/* Continue searching */
case ExceptionContinueSearch:
break;
/* Collision */
case ExceptionCollidedUnwind:
{
/* Get the original frame */
RegistrationFrame = DispatcherContext.RegistrationPointer;
break;
}
/* Anything else */
default:
{
/* Set up the exception record */
ExceptionRecord2.ExceptionRecord = ExceptionRecord;
ExceptionRecord2.ExceptionCode = STATUS_INVALID_DISPOSITION;
ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
ExceptionRecord2.NumberParameters = 0;
/* Raise the exception */
RtlRaiseException(&ExceptionRecord2);
break;
}
}
/* Go to the next frame */
OldFrame = RegistrationFrame;
RegistrationFrame = RegistrationFrame->Next;
/* Remove this handler */
RtlpSetExceptionList(OldFrame);
}
}
/* Check if we reached the end */
if (TargetFrame == EXCEPTION_CHAIN_END)
{
/* Unwind completed, so we don't exit */
ZwContinue(Context, FALSE);
}
else
{
/* This is an exit_unwind or the frame wasn't present in the list */
ZwRaiseException(ExceptionRecord, Context, FALSE);
}
}
/* EOF */