2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ex/error.c
5 * PURPOSE: Error Functions and Status/Exception Dispatching/Raising
6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
9 /* INCLUDES *****************************************************************/
15 #define TAG_ERR ' rrE'
17 /* GLOBALS ****************************************************************/
19 BOOLEAN ExReadyForErrors
= FALSE
;
20 PVOID ExpDefaultErrorPort
= NULL
;
21 PEPROCESS ExpDefaultErrorPortProcess
= NULL
;
23 /* FUNCTIONS ****************************************************************/
26 * @name ExpSystemErrorHandler
33 * @param NumberOfParameters
36 * @param UnicodeStringParameterMask
42 * @param ValidResponseOptions
55 ExpSystemErrorHandler(IN NTSTATUS ErrorStatus
,
56 IN ULONG NumberOfParameters
,
57 IN ULONG UnicodeStringParameterMask
,
58 IN PULONG_PTR Parameters
,
61 ULONG_PTR Dummy
[4] = {0, 0, 0, 0};
64 KeBugCheckEx(FATAL_UNHANDLED_HARD_ERROR
,
69 return STATUS_SUCCESS
;
73 * @name ExpRaiseHardError
80 * @param NumberOfParameters
83 * @param UnicodeStringParameterMask
89 * @param ValidResponseOptions
102 ExpRaiseHardError(IN NTSTATUS ErrorStatus
,
103 IN ULONG NumberOfParameters
,
104 IN ULONG UnicodeStringParameterMask
,
105 IN PULONG_PTR Parameters
,
106 IN ULONG ValidResponseOptions
,
109 PEPROCESS Process
= PsGetCurrentProcess();
110 PETHREAD Thread
= PsGetCurrentThread();
111 UCHAR Buffer
[PORT_MAXIMUM_MESSAGE_LENGTH
];
112 PHARDERROR_MSG Message
= (PHARDERROR_MSG
)Buffer
;
115 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
118 /* Check if this error will shutdown the system */
119 if (ValidResponseOptions
== OptionShutdownSystem
)
121 /* Check for privilege */
122 if (!SeSinglePrivilegeCheck(SeShutdownPrivilege
, PreviousMode
))
125 return STATUS_PRIVILEGE_NOT_HELD
;
128 /* Don't handle any new hard errors */
129 ExReadyForErrors
= FALSE
;
132 /* Check if hard errors are not disabled */
133 if (!Thread
->HardErrorsAreDisabled
)
135 /* Check if we can't do errors anymore, and this is serious */
136 if ((!ExReadyForErrors
) && (NT_ERROR(ErrorStatus
)))
138 /* Use the system handler */
139 ExpSystemErrorHandler(ErrorStatus
,
141 UnicodeStringParameterMask
,
143 (PreviousMode
!= KernelMode
) ? TRUE
: FALSE
);
147 /* Check if we have an exception port */
148 if (Process
->ExceptionPort
)
150 /* Check if hard errors should be processed */
151 if (Process
->DefaultHardErrorProcessing
& 1)
154 PortHandle
= Process
->ExceptionPort
;
158 /* It's disabled, check if the error overrides it */
159 if (ErrorStatus
& 0x10000000)
161 /* Use the port anyway */
162 PortHandle
= Process
->ExceptionPort
;
173 /* Check if hard errors are enabled */
174 if (Process
->DefaultHardErrorProcessing
& 1)
176 /* Use our default system port */
177 PortHandle
= ExpDefaultErrorPort
;
181 /* It's disabled, check if the error overrides it */
182 if (ErrorStatus
& 0x10000000)
184 /* Use the port anyway */
185 PortHandle
= ExpDefaultErrorPort
;
195 /* If hard errors are disabled, do nothing */
196 if (Thread
->HardErrorsAreDisabled
) PortHandle
= NULL
;
198 /* Now check if we have a port */
201 /* Check if this is the default process */
202 if (Process
== ExpDefaultErrorPortProcess
)
204 /* We can't handle the error, check if this is critical */
205 if (NT_ERROR(ErrorStatus
))
207 /* It is, invoke the system handler */
208 ExpSystemErrorHandler(ErrorStatus
,
210 UnicodeStringParameterMask
,
212 (PreviousMode
!= KernelMode
) ? TRUE
: FALSE
);
214 /* If we survived, return to caller */
215 *Response
= ResponseReturnToCaller
;
216 return STATUS_SUCCESS
;
220 /* Setup the LPC Message */
221 Message
->h
.u1
.Length
= (sizeof(HARDERROR_MSG
) << 16) |
222 (sizeof(HARDERROR_MSG
) - sizeof(PORT_MESSAGE
));
223 Message
->h
.u2
.ZeroInit
= LPC_ERROR_EVENT
;
224 Message
->Status
= ErrorStatus
&~ 0x10000000;
225 Message
->ValidResponseOptions
= ValidResponseOptions
;
226 Message
->UnicodeStringParameterMask
= UnicodeStringParameterMask
;
227 Message
->NumberOfParameters
= NumberOfParameters
;
228 KeQuerySystemTime(&Message
->ErrorTime
);
230 /* Copy the parameters */
231 if (Parameters
) RtlMoveMemory(&Message
->Parameters
,
233 sizeof(ULONG_PTR
) * NumberOfParameters
);
235 /* Send the LPC Message */
236 Status
= LpcRequestWaitReplyPort(PortHandle
,
239 if (NT_SUCCESS(Status
))
241 /* Check what kind of response we got */
242 if ((Message
->Response
!= ResponseReturnToCaller
) &&
243 (Message
->Response
!= ResponseNotHandled
) &&
244 (Message
->Response
!= ResponseAbort
) &&
245 (Message
->Response
!= ResponseCancel
) &&
246 (Message
->Response
!= ResponseIgnore
) &&
247 (Message
->Response
!= ResponseNo
) &&
248 (Message
->Response
!= ResponseOk
) &&
249 (Message
->Response
!= ResponseRetry
) &&
250 (Message
->Response
!= ResponseYes
) &&
251 (Message
->Response
!= ResponseTryAgain
) &&
252 (Message
->Response
!= ResponseContinue
))
254 /* Reset to a default one */
255 Message
->Response
= ResponseReturnToCaller
;
258 /* Set the response */
259 *Response
= Message
->Response
;
265 *Response
= ResponseReturnToCaller
;
266 Status
= STATUS_SUCCESS
;
274 * @name ExRaiseAccessViolation
277 * The ExRaiseAccessViolation routine can be used with structured exception
278 * handling to throw a driver-determined exception for a memory access
279 * violation that occurs when a driver processes I/O requests.
280 * See: http://msdn.microsoft.com/library/en-us/Kernel_r/hh/Kernel_r/k102_71b4c053-599c-4a6d-8a59-08aae6bdc534.xml.asp?frame=true
281 * http://www.osronline.com/ddkx/kmarch/k102_814i.htm
290 ExRaiseAccessViolation(VOID
)
292 /* Raise the Right Status */
293 RtlRaiseStatus(STATUS_ACCESS_VIOLATION
);
297 * @name ExRaiseDatatypeMisalignment
300 * ExRaiseDatatypeMisalignment raises an exception with the exception
301 * code set to STATUS_DATATYPE_MISALIGNMENT
303 * http://www.osronline.com/ddkx/kmarch/k102_814i.htm
312 ExRaiseDatatypeMisalignment(VOID
)
314 /* Raise the Right Status */
315 RtlRaiseStatus(STATUS_DATATYPE_MISALIGNMENT
);
319 * @name ExSystemExceptionFilter
322 * TODO: Add description
331 ExSystemExceptionFilter(VOID
)
333 return KeGetPreviousMode() != KernelMode
?
334 EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
;
338 * @name ExRaiseHardError
341 * See NtRaiseHardError
346 * @param NumberOfParameters
347 * Number of optional parameters in Parameters array
349 * @param UnicodeStringParameterMask
350 * Optional string parameter (can be only one per error code)
353 * Array of ULONG parameters for use in error message string
355 * @param ValidResponseOptions
356 * See HARDERROR_RESPONSE_OPTION for possible values description
359 * Pointer to HARDERROR_RESPONSE enumeration
368 ExRaiseHardError(IN NTSTATUS ErrorStatus
,
369 IN ULONG NumberOfParameters
,
370 IN ULONG UnicodeStringParameterMask
,
371 IN PULONG_PTR Parameters
,
372 IN ULONG ValidResponseOptions
,
376 UNICODE_STRING CapturedParams
[MAXIMUM_HARDERROR_PARAMETERS
];
378 PULONG_PTR UserData
= NULL
, ParameterBase
;
379 PUNICODE_STRING StringBase
;
385 /* Check if we have parameters */
388 /* Check if we have strings */
389 if (UnicodeStringParameterMask
)
391 /* Add the maximum possible size */
392 Size
= (sizeof(ULONG_PTR
) + sizeof(UNICODE_STRING
)) *
393 MAXIMUM_HARDERROR_PARAMETERS
+ sizeof(UNICODE_STRING
);
395 /* Loop each parameter */
396 for (i
= 0; i
< NumberOfParameters
; i
++)
398 /* Check if it's part of the mask */
399 if (UnicodeStringParameterMask
& (1 << i
))
402 RtlMoveMemory(&CapturedParams
[i
],
404 sizeof(UNICODE_STRING
));
406 /* Increase the size */
407 Size
+= CapturedParams
[i
].MaximumLength
;
411 /* Allocate the user data region */
412 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
418 if (!NT_SUCCESS(Status
)) return Status
;
420 /* Set the pointers to our various data */
421 ParameterBase
= UserData
;
422 StringBase
= (PVOID
)((ULONG_PTR
)UserData
+
424 MAXIMUM_HARDERROR_PARAMETERS
);
425 BufferBase
= (PVOID
)((ULONG_PTR
)StringBase
+
426 sizeof(UNICODE_STRING
) *
427 MAXIMUM_HARDERROR_PARAMETERS
);
429 /* Loop parameters again */
430 for (i
= 0; i
< NumberOfParameters
; i
++)
432 /* Check if we're in the mask */
433 if (UnicodeStringParameterMask
& (1 << i
))
435 /* Update the base */
436 ParameterBase
[i
] = (ULONG_PTR
)&StringBase
[i
];
438 /* Copy the string buffer */
439 RtlMoveMemory(BufferBase
,
440 CapturedParams
[i
].Buffer
,
441 CapturedParams
[i
].MaximumLength
);
444 CapturedParams
[i
].Buffer
= BufferBase
;
446 /* Copy the string structure */
447 RtlMoveMemory(&StringBase
[i
],
449 sizeof(UNICODE_STRING
));
451 /* Update the pointer */
452 BufferBase
+= CapturedParams
[i
].MaximumLength
;
456 /* No need to copy any strings */
457 ParameterBase
[i
] = Parameters
[i
];
463 /* Just keep the data as is */
464 UserData
= Parameters
;
468 /* Now call the worker function */
469 Status
= ExpRaiseHardError(ErrorStatus
,
471 UnicodeStringParameterMask
,
473 ValidResponseOptions
,
476 /* Check if we had done user-mode allocation */
477 if ((UserData
) && (UserData
!= Parameters
))
479 /* We did! Delete it */
481 ZwFreeVirtualMemory(NtCurrentProcess(),
487 /* Return status and the response */
488 *Response
= SafeResponse
;
493 * @name NtRaiseHardError
496 * This function sends HARDERROR_MSG LPC message to listener
497 * (typically CSRSS.EXE). See NtSetDefaultHardErrorPort for more information
498 * See: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Error/NtRaiseHardError.html
503 * @param NumberOfParameters
504 * Number of optional parameters in Parameters array
506 * @param UnicodeStringParameterMask
507 * Optional string parameter (can be only one per error code)
510 * Array of ULONG parameters for use in error message string
512 * @param ValidResponseOptions
513 * See HARDERROR_RESPONSE_OPTION for possible values description
516 * Pointer to HARDERROR_RESPONSE enumeration
520 * @remarks NtRaiseHardError is easy way to display message in GUI
521 * without loading Win32 API libraries
526 NtRaiseHardError(IN NTSTATUS ErrorStatus
,
527 IN ULONG NumberOfParameters
,
528 IN ULONG UnicodeStringParameterMask
,
529 IN PULONG_PTR Parameters
,
530 IN ULONG ValidResponseOptions
,
533 NTSTATUS Status
= STATUS_SUCCESS
;
534 PULONG_PTR SafeParams
= NULL
;
536 UNICODE_STRING SafeString
;
539 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
541 /* Validate parameter count */
542 if (NumberOfParameters
> MAXIMUM_HARDERROR_PARAMETERS
)
545 return STATUS_INVALID_PARAMETER_2
;
548 /* Make sure we have some at least */
549 if ((Parameters
) && !(NumberOfParameters
))
552 return STATUS_INVALID_PARAMETER_2
;
555 /* Check if we were called from user-mode */
556 if (PreviousMode
!= KernelMode
)
558 /* First validate the responses */
559 switch (ValidResponseOptions
)
561 /* Check all valid cases */
562 case OptionAbortRetryIgnore
:
565 case OptionRetryCancel
:
567 case OptionYesNoCancel
:
568 case OptionShutdownSystem
:
571 /* Anything else is invalid */
573 return STATUS_INVALID_PARAMETER_4
;
576 /* Enter SEH Block */
579 /* Validate the response pointer */
580 ProbeForWriteUlong(Response
);
582 /* Check if we have parameters */
585 /* Validate the parameter pointers */
586 ParamSize
= sizeof(ULONG_PTR
) * NumberOfParameters
;
587 ProbeForRead(Parameters
, ParamSize
, sizeof(ULONG_PTR
));
589 /* Allocate a safe buffer */
590 SafeParams
= ExAllocatePoolWithTag(PagedPool
,
595 RtlCopyMemory(SafeParams
, Parameters
, ParamSize
);
597 /* Nowo check if there's strings in it */
598 if (UnicodeStringParameterMask
)
600 /* Loop every string */
601 for (i
= 0; i
< NumberOfParameters
; i
++)
603 /* Check if this parameter is a string */
604 if (UnicodeStringParameterMask
& (1 << i
))
606 /* Probe the structure */
607 ProbeForRead((PVOID
)SafeParams
[i
],
608 sizeof(UNICODE_STRING
),
612 RtlCopyMemory(&SafeString
,
613 (PVOID
)SafeParams
[i
],
614 sizeof(UNICODE_STRING
));
616 /* Probe the buffer */
617 ProbeForRead(SafeString
.Buffer
,
618 SafeString
.MaximumLength
,
625 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
627 /* Free captured buffer */
628 if (SafeParams
) ExFreePool(SafeParams
);
630 /* Return the exception code */
631 _SEH2_YIELD(return _SEH2_GetExceptionCode());
635 /* Call the system function directly, because we probed */
636 ExpRaiseHardError(ErrorStatus
,
638 UnicodeStringParameterMask
,
640 ValidResponseOptions
,
646 SafeParams
= Parameters
;
649 * Call the Executive Function. It will probe and copy pointers to
652 ExRaiseHardError(ErrorStatus
,
654 UnicodeStringParameterMask
,
656 ValidResponseOptions
,
660 /* Check if we were called in user-mode */
661 if (PreviousMode
!= KernelMode
)
663 /* That means we have a buffer to free */
664 if (SafeParams
) ExFreePool(SafeParams
);
666 /* Enter SEH Block for return */
669 /* Return the response */
670 *Response
= SafeResponse
;
672 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
674 /* Get the exception code */
675 Status
= _SEH2_GetExceptionCode();
681 /* Return the response */
682 *Response
= SafeResponse
;
690 * @name NtSetDefaultHardErrorPort
693 * NtSetDefaultHardErrorPort is typically called only once. After call,
694 * kernel set BOOLEAN flag named _ExReadyForErrors to TRUE, and all other
695 * tries to change default port are broken with STATUS_UNSUCCESSFUL error code
696 * See: http://www.windowsitlibrary.com/Content/356/08/2.html
697 * http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Error/NtSetDefaultHardErrorPort.html
700 * Handle to named port object
704 * @remarks Privileges: SE_TCB_PRIVILEGE
709 NtSetDefaultHardErrorPort(IN HANDLE PortHandle
)
711 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
712 NTSTATUS Status
= STATUS_UNSUCCESSFUL
;
714 /* Check if we have the Privilege */
715 if (!SeSinglePrivilegeCheck(SeTcbPrivilege
, PreviousMode
))
717 DPRINT1("NtSetDefaultHardErrorPort: Caller requires "
718 "the SeTcbPrivilege privilege!\n");
719 return STATUS_PRIVILEGE_NOT_HELD
;
722 /* Only called once during bootup, make sure we weren't called yet */
723 if (!ExReadyForErrors
)
725 /* Reference the port */
726 Status
= ObReferenceObjectByHandle(PortHandle
,
730 (PVOID
*)&ExpDefaultErrorPort
,
732 if (NT_SUCCESS(Status
))
735 ExpDefaultErrorPortProcess
= PsGetCurrentProcess();
736 ExReadyForErrors
= TRUE
;
740 /* Return status to caller */
748 /* Not supported in Kernel Mode */
749 RtlRaiseStatus(STATUS_NOT_IMPLEMENTED
);