2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ex/harderr.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 BugCheckParameters
[MAXIMUM_HARDERROR_PARAMETERS
] = {0, 0, 0, 0};
65 ASSERT(NumberOfParameters
<= MAXIMUM_HARDERROR_PARAMETERS
);
68 * KeBugCheck expects MAXIMUM_HARDERROR_PARAMETERS parameters,
69 * but we might get called with less, so use a local buffer here.
71 for (i
= 0; i
< NumberOfParameters
; i
++)
74 BugCheckParameters
[i
] = Parameters
[i
];
78 KeBugCheckEx(FATAL_UNHANDLED_HARD_ERROR
,
80 (ULONG_PTR
)BugCheckParameters
,
83 return STATUS_SUCCESS
;
87 * @name ExpRaiseHardError
90 * See ExRaiseHardError and NtRaiseHardError, same parameters.
92 * This function performs the central work for both ExRaiseHardError
93 * and NtRaiseHardError. ExRaiseHardError is the service for kernel-mode
94 * that copies the parameters to user-mode, and NtRaiseHardError is the
95 * service for both kernel-mode and user-mode that performs parameters
96 * validation and capture if necessary.
101 ExpRaiseHardError(IN NTSTATUS ErrorStatus
,
102 IN ULONG NumberOfParameters
,
103 IN ULONG UnicodeStringParameterMask
,
104 IN PULONG_PTR Parameters
,
105 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
;
114 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
118 /* Check if this error will shutdown the system */
119 if (ValidResponseOptions
== OptionShutdownSystem
)
122 * Check if we have the privileges.
124 * NOTE: In addition to the Shutdown privilege we also check whether
125 * the caller has the Tcb privilege. The purpose is to allow only
126 * SYSTEM processes to "shutdown" the system on hard errors (BSOD)
127 * while forbidding regular processes to do so. This behaviour differs
128 * from Windows, where any user-mode process, as soon as it has the
129 * Shutdown privilege, can trigger a hard-error BSOD.
131 if (!SeSinglePrivilegeCheck(SeTcbPrivilege
, PreviousMode
) ||
132 !SeSinglePrivilegeCheck(SeShutdownPrivilege
, PreviousMode
))
135 *Response
= ResponseNotHandled
;
136 return STATUS_PRIVILEGE_NOT_HELD
;
139 /* Don't handle any new hard errors */
140 ExReadyForErrors
= FALSE
;
143 /* Check if hard errors are not disabled */
144 if (!Thread
->HardErrorsAreDisabled
)
146 /* Check if we can't do errors anymore, and this is serious */
147 if (!ExReadyForErrors
&& NT_ERROR(ErrorStatus
))
149 /* Use the system handler */
150 ExpSystemErrorHandler(ErrorStatus
,
152 UnicodeStringParameterMask
,
154 (PreviousMode
!= KernelMode
) ? TRUE
: FALSE
);
159 * Enable hard error processing if it is enabled for the process
160 * or if the exception status forces it.
162 if ((Process
->DefaultHardErrorProcessing
& SEM_FAILCRITICALERRORS
) ||
163 (ErrorStatus
& HARDERROR_OVERRIDE_ERRORMODE
))
165 /* Check if we have an exception port */
166 if (Process
->ExceptionPort
)
169 PortHandle
= Process
->ExceptionPort
;
173 /* Use our default system port */
174 PortHandle
= ExpDefaultErrorPort
;
179 /* Don't process the error */
183 /* If hard errors are disabled, do nothing */
184 if (Thread
->HardErrorsAreDisabled
) PortHandle
= NULL
;
187 * If this is not the system thread, check whether hard errors are
188 * disabled for this thread on user-mode side, and if so, do nothing.
190 if (!Thread
->SystemThread
&& (PortHandle
!= NULL
))
192 /* Check if we have a TEB */
193 PTEB Teb
= PsGetCurrentThread()->Tcb
.Teb
;
198 if (Teb
->HardErrorMode
& RTL_SEM_FAILCRITICALERRORS
)
203 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
211 /* Now check if we have a port */
212 if (PortHandle
== NULL
)
214 /* Just return to caller */
215 *Response
= ResponseReturnToCaller
;
216 return STATUS_SUCCESS
;
219 /* Check if this is the default process */
220 if (Process
== ExpDefaultErrorPortProcess
)
222 /* We can't handle the error, check if this is critical */
223 if (NT_ERROR(ErrorStatus
))
225 /* It is, invoke the system handler */
226 ExpSystemErrorHandler(ErrorStatus
,
228 UnicodeStringParameterMask
,
230 (PreviousMode
!= KernelMode
) ? TRUE
: FALSE
);
232 /* If we survived, return to caller */
233 *Response
= ResponseReturnToCaller
;
234 return STATUS_SUCCESS
;
238 /* Setup the LPC Message */
239 Message
->h
.u1
.Length
= (sizeof(HARDERROR_MSG
) << 16) |
240 (sizeof(HARDERROR_MSG
) - sizeof(PORT_MESSAGE
));
241 Message
->h
.u2
.ZeroInit
= 0;
242 Message
->h
.u2
.s2
.Type
= LPC_ERROR_EVENT
;
243 Message
->Status
= ErrorStatus
& ~HARDERROR_OVERRIDE_ERRORMODE
;
244 Message
->ValidResponseOptions
= ValidResponseOptions
;
245 Message
->UnicodeStringParameterMask
= UnicodeStringParameterMask
;
246 Message
->NumberOfParameters
= NumberOfParameters
;
247 KeQuerySystemTime(&Message
->ErrorTime
);
249 /* Copy the parameters */
252 RtlMoveMemory(&Message
->Parameters
,
254 sizeof(ULONG_PTR
) * NumberOfParameters
);
257 /* Send the LPC Message */
258 Status
= LpcRequestWaitReplyPort(PortHandle
,
259 (PPORT_MESSAGE
)Message
,
260 (PPORT_MESSAGE
)Message
);
261 if (NT_SUCCESS(Status
))
263 /* Check what kind of response we got */
264 if ((Message
->Response
!= ResponseReturnToCaller
) &&
265 (Message
->Response
!= ResponseNotHandled
) &&
266 (Message
->Response
!= ResponseAbort
) &&
267 (Message
->Response
!= ResponseCancel
) &&
268 (Message
->Response
!= ResponseIgnore
) &&
269 (Message
->Response
!= ResponseNo
) &&
270 (Message
->Response
!= ResponseOk
) &&
271 (Message
->Response
!= ResponseRetry
) &&
272 (Message
->Response
!= ResponseYes
) &&
273 (Message
->Response
!= ResponseTryAgain
) &&
274 (Message
->Response
!= ResponseContinue
))
276 /* Reset to a default one */
277 Message
->Response
= ResponseReturnToCaller
;
280 /* Set the response */
281 *Response
= Message
->Response
;
285 /* Set the response */
286 *Response
= ResponseReturnToCaller
;
294 * @name ExRaiseAccessViolation
297 * The ExRaiseAccessViolation routine can be used with structured exception
298 * handling to throw a driver-determined exception for a memory access
299 * violation that occurs when a driver processes I/O requests.
300 * See: http://msdn.microsoft.com/library/en-us/Kernel_r/hh/Kernel_r/k102_71b4c053-599c-4a6d-8a59-08aae6bdc534.xml.asp?frame=true
301 * http://www.osronline.com/ddkx/kmarch/k102_814i.htm
310 ExRaiseAccessViolation(VOID
)
312 /* Raise the Right Status */
313 RtlRaiseStatus(STATUS_ACCESS_VIOLATION
);
317 * @name ExRaiseDatatypeMisalignment
320 * ExRaiseDatatypeMisalignment raises an exception with the exception
321 * code set to STATUS_DATATYPE_MISALIGNMENT
323 * http://www.osronline.com/ddkx/kmarch/k102_814i.htm
332 ExRaiseDatatypeMisalignment(VOID
)
334 /* Raise the Right Status */
335 RtlRaiseStatus(STATUS_DATATYPE_MISALIGNMENT
);
339 * @name ExSystemExceptionFilter
342 * TODO: Add description
351 ExSystemExceptionFilter(VOID
)
353 return KeGetPreviousMode() != KernelMode
?
354 EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
;
358 * @name ExRaiseHardError
361 * See NtRaiseHardError and ExpRaiseHardError.
366 * @param NumberOfParameters
367 * Number of optional parameters in Parameters array
369 * @param UnicodeStringParameterMask
370 * Optional string parameter (can be only one per error code)
373 * Array of ULONG parameters for use in error message string
375 * @param ValidResponseOptions
376 * See HARDERROR_RESPONSE_OPTION for possible values description
379 * Pointer to HARDERROR_RESPONSE enumeration
386 ExRaiseHardError(IN NTSTATUS ErrorStatus
,
387 IN ULONG NumberOfParameters
,
388 IN ULONG UnicodeStringParameterMask
,
389 IN PULONG_PTR Parameters
,
390 IN ULONG ValidResponseOptions
,
395 UNICODE_STRING CapturedParams
[MAXIMUM_HARDERROR_PARAMETERS
];
397 PVOID UserData
= NULL
;
398 PHARDERROR_USER_PARAMETERS UserParams
;
400 ULONG SafeResponse
= ResponseNotHandled
;
404 /* Check if we have parameters */
407 /* Check if we have strings */
408 if (UnicodeStringParameterMask
)
410 /* Calculate the required size */
411 Size
= FIELD_OFFSET(HARDERROR_USER_PARAMETERS
, Buffer
[0]);
413 /* Loop each parameter */
414 for (i
= 0; i
< NumberOfParameters
; i
++)
416 /* Check if it's part of the mask */
417 if (UnicodeStringParameterMask
& (1 << i
))
420 RtlMoveMemory(&CapturedParams
[i
],
421 (PVOID
)Parameters
[i
],
422 sizeof(UNICODE_STRING
));
424 /* Increase the size */
425 Size
+= CapturedParams
[i
].MaximumLength
;
429 /* Allocate the user data region */
430 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
436 if (!NT_SUCCESS(Status
))
439 *Response
= ResponseNotHandled
;
443 /* Set the pointers to our data */
444 UserParams
= UserData
;
445 BufferBase
= UserParams
->Buffer
;
447 /* Enter SEH block as we are writing to user-mode space */
450 /* Loop parameters again */
451 for (i
= 0; i
< NumberOfParameters
; i
++)
453 /* Check if we are in the mask */
454 if (UnicodeStringParameterMask
& (1 << i
))
456 /* Update the base */
457 UserParams
->Parameters
[i
] = (ULONG_PTR
)&UserParams
->Strings
[i
];
459 /* Copy the string buffer */
460 RtlMoveMemory(BufferBase
,
461 CapturedParams
[i
].Buffer
,
462 CapturedParams
[i
].MaximumLength
);
465 CapturedParams
[i
].Buffer
= BufferBase
;
467 /* Copy the string structure */
468 UserParams
->Strings
[i
] = CapturedParams
[i
];
470 /* Update the pointer */
471 BufferBase
+= CapturedParams
[i
].MaximumLength
;
475 /* No need to copy any strings */
476 UserParams
->Parameters
[i
] = Parameters
[i
];
480 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
482 /* Return the exception code */
483 Status
= _SEH2_GetExceptionCode();
484 DPRINT1("ExRaiseHardError - Exception when writing data to user-mode, Status 0x%08lx\n", Status
);
490 /* Just keep the data as is */
491 UserData
= Parameters
;
495 /* Now call the worker function */
496 Status
= ExpRaiseHardError(ErrorStatus
,
498 UnicodeStringParameterMask
,
500 ValidResponseOptions
,
503 /* Check if we had done user-mode allocation */
504 if ((UserData
) && (UserData
!= Parameters
))
506 /* We did! Delete it */
508 ZwFreeVirtualMemory(NtCurrentProcess(),
514 /* Return status and the response */
515 *Response
= SafeResponse
;
520 * @name NtRaiseHardError
523 * This function sends HARDERROR_MSG LPC message to a hard-error listener,
524 * typically CSRSS.EXE. See NtSetDefaultHardErrorPort for more information.
525 * See also: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Error/NtRaiseHardError.html
530 * @param NumberOfParameters
531 * Number of optional parameters in Parameters array
533 * @param UnicodeStringParameterMask
534 * Optional string parameter (can be only one per error code)
537 * Array of ULONG_PTR parameters for use in error message string
539 * @param ValidResponseOptions
540 * See HARDERROR_RESPONSE_OPTION for possible values description
543 * Pointer to HARDERROR_RESPONSE enumeration
547 * @remarks NtRaiseHardError constitutes an easy way to display messages
548 * in GUI without loading any Win32 API libraries.
553 NtRaiseHardError(IN NTSTATUS ErrorStatus
,
554 IN ULONG NumberOfParameters
,
555 IN ULONG UnicodeStringParameterMask
,
556 IN PULONG_PTR Parameters
,
557 IN ULONG ValidResponseOptions
,
560 NTSTATUS Status
= STATUS_SUCCESS
;
561 PULONG_PTR SafeParams
= NULL
;
562 ULONG SafeResponse
= ResponseNotHandled
;
563 UNICODE_STRING SafeString
;
566 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
570 /* Validate parameter count */
571 if (NumberOfParameters
> MAXIMUM_HARDERROR_PARAMETERS
)
574 return STATUS_INVALID_PARAMETER_2
;
577 /* Make sure we have some at least */
578 if ((Parameters
!= NULL
) && (NumberOfParameters
== 0))
581 return STATUS_INVALID_PARAMETER_2
;
584 /* Check if we were called from user-mode */
585 if (PreviousMode
!= KernelMode
)
587 /* First validate the responses */
588 switch (ValidResponseOptions
)
590 /* Check all valid cases */
591 case OptionAbortRetryIgnore
:
594 case OptionRetryCancel
:
596 case OptionYesNoCancel
:
597 case OptionShutdownSystem
:
599 case OptionCancelTryContinue
:
602 /* Anything else is invalid */
604 return STATUS_INVALID_PARAMETER_4
;
607 /* Check if we have parameters */
610 /* Calculate size of the parameters */
611 ParamSize
= sizeof(ULONG_PTR
) * NumberOfParameters
;
613 /* Allocate a safe buffer */
614 SafeParams
= ExAllocatePoolWithTag(PagedPool
, ParamSize
, TAG_ERR
);
617 return STATUS_INSUFFICIENT_RESOURCES
;
621 /* Enter SEH Block */
624 /* Validate the response pointer */
625 ProbeForWriteUlong(Response
);
627 /* Check if we have parameters */
630 /* Validate the parameter pointers */
631 ProbeForRead(Parameters
, ParamSize
, sizeof(ULONG_PTR
));
634 RtlCopyMemory(SafeParams
, Parameters
, ParamSize
);
636 /* Now check if there's strings in it */
637 if (UnicodeStringParameterMask
)
639 /* Loop every string */
640 for (i
= 0; i
< NumberOfParameters
; i
++)
642 /* Check if this parameter is a string */
643 if (UnicodeStringParameterMask
& (1 << i
))
645 /* Probe the structure */
646 ProbeForRead((PVOID
)SafeParams
[i
],
647 sizeof(UNICODE_STRING
),
651 RtlCopyMemory(&SafeString
,
652 (PVOID
)SafeParams
[i
],
653 sizeof(UNICODE_STRING
));
655 /* Probe the buffer */
656 ProbeForRead(SafeString
.Buffer
,
657 SafeString
.MaximumLength
,
664 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
666 /* Free captured buffer */
667 if (SafeParams
) ExFreePoolWithTag(SafeParams
, TAG_ERR
);
669 /* Return the exception code */
670 _SEH2_YIELD(return _SEH2_GetExceptionCode());
674 /* Call the system function directly, because we probed */
675 Status
= ExpRaiseHardError(ErrorStatus
,
677 UnicodeStringParameterMask
,
679 ValidResponseOptions
,
682 /* Free captured buffer */
683 if (SafeParams
) ExFreePoolWithTag(SafeParams
, TAG_ERR
);
685 /* Enter SEH Block to return the response */
688 /* Return the response */
689 *Response
= SafeResponse
;
691 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
693 /* Get the exception code */
694 Status
= _SEH2_GetExceptionCode();
701 SafeParams
= Parameters
;
704 * Call the Executive Function. It will probe
705 * and copy pointers to user-mode.
707 Status
= ExRaiseHardError(ErrorStatus
,
709 UnicodeStringParameterMask
,
711 ValidResponseOptions
,
714 /* Return the response */
715 *Response
= SafeResponse
;
723 * @name NtSetDefaultHardErrorPort
726 * NtSetDefaultHardErrorPort is typically called only once. After the call,
727 * the kernel sets a BOOLEAN flag named ExReadyForErrors to TRUE, and all other
728 * attempts to change the default port fail with STATUS_UNSUCCESSFUL error code.
729 * See: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Error/NtSetDefaultHardErrorPort.html
730 * https://web.archive.org/web/20070716133753/http://www.windowsitlibrary.com/Content/356/08/2.html
733 * Handle to named port object
737 * @remarks Privileges: SE_TCB_PRIVILEGE
742 NtSetDefaultHardErrorPort(IN HANDLE PortHandle
)
744 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
745 NTSTATUS Status
= STATUS_UNSUCCESSFUL
;
749 /* Check if we have the privileges */
750 if (!SeSinglePrivilegeCheck(SeTcbPrivilege
, PreviousMode
))
752 DPRINT1("NtSetDefaultHardErrorPort: Caller requires "
753 "the SeTcbPrivilege privilege!\n");
754 return STATUS_PRIVILEGE_NOT_HELD
;
757 /* Only called once during bootup, make sure we weren't called yet */
758 if (!ExReadyForErrors
)
760 /* Reference the hard-error port */
761 Status
= ObReferenceObjectByHandle(PortHandle
,
765 (PVOID
*)&ExpDefaultErrorPort
,
767 if (NT_SUCCESS(Status
))
769 /* Keep also a reference to the process handling the hard errors */
770 ExpDefaultErrorPortProcess
= PsGetCurrentProcess();
771 ObReferenceObject(ExpDefaultErrorPortProcess
);
772 ExReadyForErrors
= TRUE
;
773 Status
= STATUS_SUCCESS
;
777 /* Return status to caller */
785 /* Not supported in Kernel Mode */
786 RtlRaiseStatus(STATUS_NOT_IMPLEMENTED
);