reactos/ntoskrnl/ex/harderr.c
2018-04-01 22:51:24 +02:00

789 lines
24 KiB
C

/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/ex/harderr.c
* PURPOSE: Error Functions and Status/Exception Dispatching/Raising
* PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
*/
/* INCLUDES *****************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
#define TAG_ERR ' rrE'
/* GLOBALS ******************************************************************/
BOOLEAN ExReadyForErrors = FALSE;
PVOID ExpDefaultErrorPort = NULL;
PEPROCESS ExpDefaultErrorPortProcess = NULL;
/* FUNCTIONS ****************************************************************/
/*++
* @name ExpSystemErrorHandler
*
* For now it's a stub
*
* @param ErrorStatus
* FILLME
*
* @param NumberOfParameters
* FILLME
*
* @param UnicodeStringParameterMask
* FILLME
*
* @param Parameters
* FILLME
*
* @param ValidResponseOptions
* FILLME
*
* @param Response
* FILLME
*
* @return None
*
* @remarks None
*
*--*/
NTSTATUS
NTAPI
ExpSystemErrorHandler(IN NTSTATUS ErrorStatus,
IN ULONG NumberOfParameters,
IN ULONG UnicodeStringParameterMask,
IN PULONG_PTR Parameters,
IN BOOLEAN Shutdown)
{
ULONG_PTR BugCheckParameters[MAXIMUM_HARDERROR_PARAMETERS] = {0, 0, 0, 0};
ULONG i;
/* Sanity check */
ASSERT(NumberOfParameters <= MAXIMUM_HARDERROR_PARAMETERS);
/*
* KeBugCheck expects MAXIMUM_HARDERROR_PARAMETERS parameters,
* but we might get called with less, so use a local buffer here.
*/
for (i = 0; i < NumberOfParameters; i++)
{
/* Copy them over */
BugCheckParameters[i] = Parameters[i];
}
/* FIXME: STUB */
KeBugCheckEx(FATAL_UNHANDLED_HARD_ERROR,
ErrorStatus,
(ULONG_PTR)BugCheckParameters,
0,
0);
return STATUS_SUCCESS;
}
/*++
* @name ExpRaiseHardError
* @implemented
*
* See ExRaiseHardError and NtRaiseHardError, same parameters.
*
* This function performs the central work for both ExRaiseHardError
* and NtRaiseHardError. ExRaiseHardError is the service for kernel-mode
* that copies the parameters to user-mode, and NtRaiseHardError is the
* service for both kernel-mode and user-mode that performs parameters
* validation and capture if necessary.
*
*--*/
NTSTATUS
NTAPI
ExpRaiseHardError(IN NTSTATUS ErrorStatus,
IN ULONG NumberOfParameters,
IN ULONG UnicodeStringParameterMask,
IN PULONG_PTR Parameters,
IN ULONG ValidResponseOptions,
OUT PULONG Response)
{
NTSTATUS Status;
PEPROCESS Process = PsGetCurrentProcess();
PETHREAD Thread = PsGetCurrentThread();
UCHAR Buffer[PORT_MAXIMUM_MESSAGE_LENGTH];
PHARDERROR_MSG Message = (PHARDERROR_MSG)Buffer;
HANDLE PortHandle;
KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
PAGED_CODE();
/* Check if this error will shutdown the system */
if (ValidResponseOptions == OptionShutdownSystem)
{
/*
* Check if we have the privileges.
*
* NOTE: In addition to the Shutdown privilege we also check whether
* the caller has the Tcb privilege. The purpose is to allow only
* SYSTEM processes to "shutdown" the system on hard errors (BSOD)
* while forbidding regular processes to do so. This behaviour differs
* from Windows, where any user-mode process, as soon as it has the
* Shutdown privilege, can trigger a hard-error BSOD.
*/
if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode) ||
!SeSinglePrivilegeCheck(SeShutdownPrivilege, PreviousMode))
{
/* No rights */
*Response = ResponseNotHandled;
return STATUS_PRIVILEGE_NOT_HELD;
}
/* Don't handle any new hard errors */
ExReadyForErrors = FALSE;
}
/* Check if hard errors are not disabled */
if (!Thread->HardErrorsAreDisabled)
{
/* Check if we can't do errors anymore, and this is serious */
if (!ExReadyForErrors && NT_ERROR(ErrorStatus))
{
/* Use the system handler */
ExpSystemErrorHandler(ErrorStatus,
NumberOfParameters,
UnicodeStringParameterMask,
Parameters,
(PreviousMode != KernelMode) ? TRUE : FALSE);
}
}
/*
* Enable hard error processing if it is enabled for the process
* or if the exception status forces it.
*/
if ((Process->DefaultHardErrorProcessing & SEM_FAILCRITICALERRORS) ||
(ErrorStatus & HARDERROR_OVERRIDE_ERRORMODE))
{
/* Check if we have an exception port */
if (Process->ExceptionPort)
{
/* Use the port */
PortHandle = Process->ExceptionPort;
}
else
{
/* Use our default system port */
PortHandle = ExpDefaultErrorPort;
}
}
else
{
/* Don't process the error */
PortHandle = NULL;
}
/* If hard errors are disabled, do nothing */
if (Thread->HardErrorsAreDisabled) PortHandle = NULL;
/*
* If this is not the system thread, check whether hard errors are
* disabled for this thread on user-mode side, and if so, do nothing.
*/
if (!Thread->SystemThread && (PortHandle != NULL))
{
/* Check if we have a TEB */
PTEB Teb = PsGetCurrentThread()->Tcb.Teb;
if (Teb)
{
_SEH2_TRY
{
if (Teb->HardErrorMode & RTL_SEM_FAILCRITICALERRORS)
{
PortHandle = NULL;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
NOTHING;
}
_SEH2_END;
}
}
/* Now check if we have a port */
if (PortHandle == NULL)
{
/* Just return to caller */
*Response = ResponseReturnToCaller;
return STATUS_SUCCESS;
}
/* Check if this is the default process */
if (Process == ExpDefaultErrorPortProcess)
{
/* We can't handle the error, check if this is critical */
if (NT_ERROR(ErrorStatus))
{
/* It is, invoke the system handler */
ExpSystemErrorHandler(ErrorStatus,
NumberOfParameters,
UnicodeStringParameterMask,
Parameters,
(PreviousMode != KernelMode) ? TRUE : FALSE);
/* If we survived, return to caller */
*Response = ResponseReturnToCaller;
return STATUS_SUCCESS;
}
}
/* Setup the LPC Message */
Message->h.u1.Length = (sizeof(HARDERROR_MSG) << 16) |
(sizeof(HARDERROR_MSG) - sizeof(PORT_MESSAGE));
Message->h.u2.ZeroInit = 0;
Message->h.u2.s2.Type = LPC_ERROR_EVENT;
Message->Status = ErrorStatus & ~HARDERROR_OVERRIDE_ERRORMODE;
Message->ValidResponseOptions = ValidResponseOptions;
Message->UnicodeStringParameterMask = UnicodeStringParameterMask;
Message->NumberOfParameters = NumberOfParameters;
KeQuerySystemTime(&Message->ErrorTime);
/* Copy the parameters */
if (Parameters)
{
RtlMoveMemory(&Message->Parameters,
Parameters,
sizeof(ULONG_PTR) * NumberOfParameters);
}
/* Send the LPC Message */
Status = LpcRequestWaitReplyPort(PortHandle,
(PPORT_MESSAGE)Message,
(PPORT_MESSAGE)Message);
if (NT_SUCCESS(Status))
{
/* Check what kind of response we got */
if ((Message->Response != ResponseReturnToCaller) &&
(Message->Response != ResponseNotHandled) &&
(Message->Response != ResponseAbort) &&
(Message->Response != ResponseCancel) &&
(Message->Response != ResponseIgnore) &&
(Message->Response != ResponseNo) &&
(Message->Response != ResponseOk) &&
(Message->Response != ResponseRetry) &&
(Message->Response != ResponseYes) &&
(Message->Response != ResponseTryAgain) &&
(Message->Response != ResponseContinue))
{
/* Reset to a default one */
Message->Response = ResponseReturnToCaller;
}
/* Set the response */
*Response = Message->Response;
}
else
{
/* Set the response */
*Response = ResponseReturnToCaller;
}
/* Return status */
return Status;
}
/*++
* @name ExRaiseAccessViolation
* @implemented
*
* The ExRaiseAccessViolation routine can be used with structured exception
* handling to throw a driver-determined exception for a memory access
* violation that occurs when a driver processes I/O requests.
* See: http://msdn.microsoft.com/library/en-us/Kernel_r/hh/Kernel_r/k102_71b4c053-599c-4a6d-8a59-08aae6bdc534.xml.asp?frame=true
* http://www.osronline.com/ddkx/kmarch/k102_814i.htm
*
* @return None
*
* @remarks None
*
*--*/
VOID
NTAPI
ExRaiseAccessViolation(VOID)
{
/* Raise the Right Status */
RtlRaiseStatus(STATUS_ACCESS_VIOLATION);
}
/*++
* @name ExRaiseDatatypeMisalignment
* @implemented
*
* ExRaiseDatatypeMisalignment raises an exception with the exception
* code set to STATUS_DATATYPE_MISALIGNMENT
* See: MSDN / DDK
* http://www.osronline.com/ddkx/kmarch/k102_814i.htm
*
* @return None
*
* @remarks None
*
*--*/
VOID
NTAPI
ExRaiseDatatypeMisalignment(VOID)
{
/* Raise the Right Status */
RtlRaiseStatus(STATUS_DATATYPE_MISALIGNMENT);
}
/*++
* @name ExSystemExceptionFilter
* @implemented
*
* TODO: Add description
*
* @return FILLME
*
* @remarks None
*
*--*/
LONG
NTAPI
ExSystemExceptionFilter(VOID)
{
return KeGetPreviousMode() != KernelMode ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH;
}
/*++
* @name ExRaiseHardError
* @implemented
*
* See NtRaiseHardError and ExpRaiseHardError.
*
* @param ErrorStatus
* Error Code
*
* @param NumberOfParameters
* Number of optional parameters in Parameters array
*
* @param UnicodeStringParameterMask
* Optional string parameter (can be only one per error code)
*
* @param Parameters
* Array of ULONG parameters for use in error message string
*
* @param ValidResponseOptions
* See HARDERROR_RESPONSE_OPTION for possible values description
*
* @param Response
* Pointer to HARDERROR_RESPONSE enumeration
*
* @return Status
*
*--*/
NTSTATUS
NTAPI
ExRaiseHardError(IN NTSTATUS ErrorStatus,
IN ULONG NumberOfParameters,
IN ULONG UnicodeStringParameterMask,
IN PULONG_PTR Parameters,
IN ULONG ValidResponseOptions,
OUT PULONG Response)
{
NTSTATUS Status;
SIZE_T Size;
UNICODE_STRING CapturedParams[MAXIMUM_HARDERROR_PARAMETERS];
ULONG i;
PVOID UserData = NULL;
PHARDERROR_USER_PARAMETERS UserParams;
PWSTR BufferBase;
ULONG SafeResponse = ResponseNotHandled;
PAGED_CODE();
/* Check if we have parameters */
if (Parameters)
{
/* Check if we have strings */
if (UnicodeStringParameterMask)
{
/* Calculate the required size */
Size = FIELD_OFFSET(HARDERROR_USER_PARAMETERS, Buffer[0]);
/* Loop each parameter */
for (i = 0; i < NumberOfParameters; i++)
{
/* Check if it's part of the mask */
if (UnicodeStringParameterMask & (1 << i))
{
/* Copy it */
RtlMoveMemory(&CapturedParams[i],
(PVOID)Parameters[i],
sizeof(UNICODE_STRING));
/* Increase the size */
Size += CapturedParams[i].MaximumLength;
}
}
/* Allocate the user data region */
Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
&UserData,
0,
&Size,
MEM_COMMIT,
PAGE_READWRITE);
if (!NT_SUCCESS(Status))
{
/* Return failure */
*Response = ResponseNotHandled;
return Status;
}
/* Set the pointers to our data */
UserParams = UserData;
BufferBase = UserParams->Buffer;
/* Enter SEH block as we are writing to user-mode space */
_SEH2_TRY
{
/* Loop parameters again */
for (i = 0; i < NumberOfParameters; i++)
{
/* Check if we are in the mask */
if (UnicodeStringParameterMask & (1 << i))
{
/* Update the base */
UserParams->Parameters[i] = (ULONG_PTR)&UserParams->Strings[i];
/* Copy the string buffer */
RtlMoveMemory(BufferBase,
CapturedParams[i].Buffer,
CapturedParams[i].MaximumLength);
/* Set buffer */
CapturedParams[i].Buffer = BufferBase;
/* Copy the string structure */
UserParams->Strings[i] = CapturedParams[i];
/* Update the pointer */
BufferBase += CapturedParams[i].MaximumLength;
}
else
{
/* No need to copy any strings */
UserParams->Parameters[i] = Parameters[i];
}
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
Status = _SEH2_GetExceptionCode();
DPRINT1("ExRaiseHardError - Exception when writing data to user-mode, Status 0x%08lx\n", Status);
}
_SEH2_END;
}
else
{
/* Just keep the data as is */
UserData = Parameters;
}
}
/* Now call the worker function */
Status = ExpRaiseHardError(ErrorStatus,
NumberOfParameters,
UnicodeStringParameterMask,
UserData,
ValidResponseOptions,
&SafeResponse);
/* Check if we had done user-mode allocation */
if ((UserData) && (UserData != Parameters))
{
/* We did! Delete it */
Size = 0;
ZwFreeVirtualMemory(NtCurrentProcess(),
&UserData,
&Size,
MEM_RELEASE);
}
/* Return status and the response */
*Response = SafeResponse;
return Status;
}
/*++
* @name NtRaiseHardError
* @implemented
*
* This function sends HARDERROR_MSG LPC message to a hard-error listener,
* typically CSRSS.EXE. See NtSetDefaultHardErrorPort for more information.
* See also: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Error/NtRaiseHardError.html
*
* @param ErrorStatus
* Error Code
*
* @param NumberOfParameters
* Number of optional parameters in Parameters array
*
* @param UnicodeStringParameterMask
* Optional string parameter (can be only one per error code)
*
* @param Parameters
* Array of ULONG_PTR parameters for use in error message string
*
* @param ValidResponseOptions
* See HARDERROR_RESPONSE_OPTION for possible values description
*
* @param Response
* Pointer to HARDERROR_RESPONSE enumeration
*
* @return Status
*
* @remarks NtRaiseHardError constitutes an easy way to display messages
* in GUI without loading any Win32 API libraries.
*
*--*/
NTSTATUS
NTAPI
NtRaiseHardError(IN NTSTATUS ErrorStatus,
IN ULONG NumberOfParameters,
IN ULONG UnicodeStringParameterMask,
IN PULONG_PTR Parameters,
IN ULONG ValidResponseOptions,
OUT PULONG Response)
{
NTSTATUS Status = STATUS_SUCCESS;
PULONG_PTR SafeParams = NULL;
ULONG SafeResponse = ResponseNotHandled;
UNICODE_STRING SafeString;
ULONG i;
ULONG ParamSize = 0;
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
PAGED_CODE();
/* Validate parameter count */
if (NumberOfParameters > MAXIMUM_HARDERROR_PARAMETERS)
{
/* Fail */
return STATUS_INVALID_PARAMETER_2;
}
/* Make sure we have some at least */
if ((Parameters != NULL) && (NumberOfParameters == 0))
{
/* Fail */
return STATUS_INVALID_PARAMETER_2;
}
/* Check if we were called from user-mode */
if (PreviousMode != KernelMode)
{
/* First validate the responses */
switch (ValidResponseOptions)
{
/* Check all valid cases */
case OptionAbortRetryIgnore:
case OptionOk:
case OptionOkCancel:
case OptionRetryCancel:
case OptionYesNo:
case OptionYesNoCancel:
case OptionShutdownSystem:
case OptionOkNoWait:
case OptionCancelTryContinue:
break;
/* Anything else is invalid */
default:
return STATUS_INVALID_PARAMETER_4;
}
/* Check if we have parameters */
if (Parameters)
{
/* Calculate size of the parameters */
ParamSize = sizeof(ULONG_PTR) * NumberOfParameters;
/* Allocate a safe buffer */
SafeParams = ExAllocatePoolWithTag(PagedPool, ParamSize, TAG_ERR);
if (!SafeParams)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
}
/* Enter SEH Block */
_SEH2_TRY
{
/* Validate the response pointer */
ProbeForWriteUlong(Response);
/* Check if we have parameters */
if (Parameters)
{
/* Validate the parameter pointers */
ProbeForRead(Parameters, ParamSize, sizeof(ULONG_PTR));
/* Copy them */
RtlCopyMemory(SafeParams, Parameters, ParamSize);
/* Now check if there's strings in it */
if (UnicodeStringParameterMask)
{
/* Loop every string */
for (i = 0; i < NumberOfParameters; i++)
{
/* Check if this parameter is a string */
if (UnicodeStringParameterMask & (1 << i))
{
/* Probe the structure */
ProbeForRead((PVOID)SafeParams[i],
sizeof(UNICODE_STRING),
sizeof(ULONG_PTR));
/* Capture it */
RtlCopyMemory(&SafeString,
(PVOID)SafeParams[i],
sizeof(UNICODE_STRING));
/* Probe the buffer */
ProbeForRead(SafeString.Buffer,
SafeString.MaximumLength,
sizeof(UCHAR));
}
}
}
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Free captured buffer */
if (SafeParams) ExFreePoolWithTag(SafeParams, TAG_ERR);
/* Return the exception code */
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
/* Call the system function directly, because we probed */
Status = ExpRaiseHardError(ErrorStatus,
NumberOfParameters,
UnicodeStringParameterMask,
SafeParams,
ValidResponseOptions,
&SafeResponse);
/* Free captured buffer */
if (SafeParams) ExFreePoolWithTag(SafeParams, TAG_ERR);
/* Enter SEH Block to return the response */
_SEH2_TRY
{
/* Return the response */
*Response = SafeResponse;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Get the exception code */
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
}
else
{
/* Reuse variable */
SafeParams = Parameters;
/*
* Call the Executive Function. It will probe
* and copy pointers to user-mode.
*/
Status = ExRaiseHardError(ErrorStatus,
NumberOfParameters,
UnicodeStringParameterMask,
SafeParams,
ValidResponseOptions,
&SafeResponse);
/* Return the response */
*Response = SafeResponse;
}
/* Return status */
return Status;
}
/*++
* @name NtSetDefaultHardErrorPort
* @implemented
*
* NtSetDefaultHardErrorPort is typically called only once. After the call,
* the kernel sets a BOOLEAN flag named ExReadyForErrors to TRUE, and all other
* attempts to change the default port fail with STATUS_UNSUCCESSFUL error code.
* See: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Error/NtSetDefaultHardErrorPort.html
* https://web.archive.org/web/20070716133753/http://www.windowsitlibrary.com/Content/356/08/2.html
*
* @param PortHandle
* Handle to named port object
*
* @return Status
*
* @remarks Privileges: SE_TCB_PRIVILEGE
*
*--*/
NTSTATUS
NTAPI
NtSetDefaultHardErrorPort(IN HANDLE PortHandle)
{
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PAGED_CODE();
/* Check if we have the privileges */
if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
{
DPRINT1("NtSetDefaultHardErrorPort: Caller requires "
"the SeTcbPrivilege privilege!\n");
return STATUS_PRIVILEGE_NOT_HELD;
}
/* Only called once during bootup, make sure we weren't called yet */
if (!ExReadyForErrors)
{
/* Reference the hard-error port */
Status = ObReferenceObjectByHandle(PortHandle,
0,
LpcPortObjectType,
PreviousMode,
(PVOID*)&ExpDefaultErrorPort,
NULL);
if (NT_SUCCESS(Status))
{
/* Keep also a reference to the process handling the hard errors */
ExpDefaultErrorPortProcess = PsGetCurrentProcess();
ObReferenceObject(ExpDefaultErrorPortProcess);
ExReadyForErrors = TRUE;
Status = STATUS_SUCCESS;
}
}
/* Return status to caller */
return Status;
}
VOID
__cdecl
_purecall(VOID)
{
/* Not supported in Kernel Mode */
RtlRaiseStatus(STATUS_NOT_IMPLEMENTED);
}
/* EOF */