Windows2000/private/ntos/ke/raisexcp.c
2020-09-30 17:12:32 +02:00

353 lines
9.5 KiB
C

/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
raisexcp.c
Abstract:
This module implements the internal kernel code to continue execution
and raise a exception.
Author:
David N. Cutler (davec) 8-Aug-1990
Environment:
Kernel mode only.
Revision History:
18-Oct-1997 Neillc
Fix for bug #98981. KiRaiseException referenced the users exception record before probeing.
It also had a range check that was incorrect for the number of exception parameters because
it used a signed temp to make a later calculation of the size work. The exception record
was also subject to changes to the number of parameters after validation. This might cause
lower levels to fail.
--*/
#include "ki.h"
VOID
KiContinuePreviousModeUser(
IN PCONTEXT ContextRecord,
IN PKEXCEPTION_FRAME ExceptionFrame,
IN PKTRAP_FRAME TrapFrame,
IN KPROCESSOR_MODE PreviousMode
)
/*++
Routine Description:
This function is called from KiContinue if PreviousMode is
not KernelMode. In this case a kernel mode copy of the
ContextRecord is made before calling KeContextToKframes.
This is done in a seperate routine to save stack space for
the common case which is PreviousMode == Kernel.
N.B. This routine is called from within a try/except block
that will be used to handle errors like invalid context.
Arguments:
ContextRecord - Supplies a pointer to a context record.
ExceptionFrame - Supplies a pointer to an exception frame.
TrapFrame - Supplies a pointer to a trap frame.
PreviousMode - Not KernelMode.
Return Value:
None.
--*/
{
CONTEXT ContextRecord2;
// Copy the context record to kernel mode space.
ProbeForRead(ContextRecord, sizeof(CONTEXT), CONTEXT_ALIGN);
RtlMoveMemory(&ContextRecord2, ContextRecord, sizeof(CONTEXT));
ContextRecord = &ContextRecord2;
// Move information from the context record to the exception
// and trap frames.
KeContextToKframes(TrapFrame,
ExceptionFrame,
&ContextRecord2,
ContextRecord2.ContextFlags,
PreviousMode);
}
NTSTATUS
KiContinue (
IN PCONTEXT ContextRecord,
IN PKEXCEPTION_FRAME ExceptionFrame,
IN PKTRAP_FRAME TrapFrame
)
/*++
Routine Description:
This function is called to copy the specified context frame to the
specified exception and trap frames for the continue system service.
Arguments:
ContextRecord - Supplies a pointer to a context record.
ExceptionFrame - Supplies a pointer to an exception frame.
TrapFrame - Supplies a pointer to a trap frame.
Return Value:
STATUS_ACCESS_VIOLATION is returned if the context record is not readable
from user mode.
STATUS_DATATYPE_MISALIGNMENT is returned if the context record is not
properly aligned.
STATUS_SUCCESS is returned if the context frame is copied successfully
to the specified exception and trap frames.
--*/
{
KPROCESSOR_MODE PreviousMode;
NTSTATUS Status;
KIRQL OldIrql;
BOOLEAN IrqlChanged = FALSE;
// Synchronize with other context operations.
Status = STATUS_SUCCESS;
if (KeGetCurrentIrql() < APC_LEVEL) {
// To support try-except and ExRaiseStatus in device driver code we
// need to check if we are already at raised level.
IrqlChanged = TRUE;
KeRaiseIrql(APC_LEVEL, &OldIrql);
}
// Establish an exception handler and probe and capture the specified
// context record if the previous mode is user. If the probe or copy
// fails, then return the exception code as the function value. Else
// copy the context record to the specified exception and trap frames,
// and return success as the function value.
try {
// Get the previous processor mode. If the previous processor mode is
// user, then probe and copy the specified context record.
PreviousMode = KeGetPreviousMode();
if (PreviousMode != KernelMode) {
KiContinuePreviousModeUser(ContextRecord,
ExceptionFrame,
TrapFrame,
PreviousMode);
} else {
// Move information from the context record to the exception
// and trap frames.
KeContextToKframes(TrapFrame,
ExceptionFrame,
ContextRecord,
ContextRecord->ContextFlags,
PreviousMode);
}
// If an exception occurs during the probe or copy of the context
// record, then always handle the exception and return the exception
// code as the status value.
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
}
if (IrqlChanged) {
KeLowerIrql (OldIrql);
}
return Status;
}
NTSTATUS
KiRaiseException (
IN PEXCEPTION_RECORD ExceptionRecord,
IN PCONTEXT ContextRecord,
IN PKEXCEPTION_FRAME ExceptionFrame,
IN PKTRAP_FRAME TrapFrame,
IN BOOLEAN FirstChance
)
/*++
Routine Description:
This function is called to raise an exception. The exception can be
raised as a first or second chance exception.
Arguments:
ExceptionRecord - Supplies a pointer to an exception record.
ContextRecord - Supplies a pointer to a context record.
ExceptionFrame - Supplies a pointer to an exception frame.
TrapFrame - Supplies a pointer to a trap frame.
FirstChance - Supplies a boolean value that specifies whether this is
the first (TRUE) or second (FALSE) chance for the exception.
Return Value:
STATUS_ACCESS_VIOLATION is returned if either the exception or the context
record is not readable from user mode.
STATUS_DATATYPE_MISALIGNMENT is returned if the exception record or the
context record are not properly aligned.
STATUS_INVALID_PARAMETER is returned if the number of exception parameters
is greater than the maximum allowable number of exception parameters.
STATUS_SUCCESS is returned if the exception is dispatched and handled.
--*/
{
CONTEXT ContextRecord2;
EXCEPTION_RECORD ExceptionRecord2;
ULONG Length;
ULONG Params;
KPROCESSOR_MODE PreviousMode;
// Establish an exception handler and probe the specified exception and
// context records for read accessibility. If the probe fails, then
// return the exception code as the service status. Else call the exception
// dispatcher to dispatch the exception.
try {
// Get the previous processor mode. If the previous processor mode
// is user, then probe and copy the specified exception and context
// records.
PreviousMode = KeGetPreviousMode();
if (PreviousMode != KernelMode) {
ProbeForRead(ContextRecord, sizeof(CONTEXT), CONTEXT_ALIGN);
ProbeForRead(ExceptionRecord,
FIELD_OFFSET (EXCEPTION_RECORD, NumberParameters) +
sizeof (ExceptionRecord->NumberParameters), sizeof(ULONG));
Params = ExceptionRecord->NumberParameters;
if (Params > EXCEPTION_MAXIMUM_PARAMETERS) {
return STATUS_INVALID_PARAMETER;
}
// The exception record structure is defined unlike others with trailing
// information as being its maximum size rather than just a single trailing
// element.
Length = (sizeof(EXCEPTION_RECORD) -
((EXCEPTION_MAXIMUM_PARAMETERS - Params) *
sizeof(ExceptionRecord->ExceptionInformation[0])));
// The structure is currently less that 64k so we don't really need this probe.
ProbeForRead(ExceptionRecord, Length, sizeof(ULONG));
// Copy the exception and context record to local storage so an
// access violation cannot occur during exception dispatching.
RtlMoveMemory(&ContextRecord2, ContextRecord, sizeof(CONTEXT));
RtlMoveMemory(&ExceptionRecord2, ExceptionRecord, Length);
ContextRecord = &ContextRecord2;
ExceptionRecord = &ExceptionRecord2;
// The number of parameters might have changed after we validated but before we
// copied the structure. Fix this up as lower levels might not like this.
ExceptionRecord->NumberParameters = Params;
}
// If an exception occurs during the probe of the exception or context
// record, then always handle the exception and return the exception code
// as the status value.
} except(EXCEPTION_EXECUTE_HANDLER) {
return GetExceptionCode();
}
// Move information from the context record to the exception and
// trap frames.
KeContextToKframes(TrapFrame,
ExceptionFrame,
ContextRecord,
ContextRecord->ContextFlags,
PreviousMode);
// Make sure the reserved bit is clear in the exception code and
// perform exception dispatching.
// N.B. The reserved bit is used to differentiate internally gerarated
// codes from codes generated by application programs.
ExceptionRecord->ExceptionCode &= 0xefffffff;
KiDispatchException(ExceptionRecord,
ExceptionFrame,
TrapFrame,
PreviousMode,
FirstChance);
return STATUS_SUCCESS;
}