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
,
108 PEPROCESS Process
= PsGetCurrentProcess();
109 PETHREAD Thread
= PsGetCurrentThread();
110 UCHAR Buffer
[PORT_MAXIMUM_MESSAGE_LENGTH
];
111 PHARDERROR_MSG Message
= (PHARDERROR_MSG
)Buffer
;
114 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
117 /* Check if this error will shutdown the system */
118 if (ValidResponseOptions
== OptionShutdownSystem
)
121 * Check if we have the privileges.
123 * NOTE: In addition to the Shutdown privilege we also check whether
124 * the caller has the Tcb privilege. The purpose is to allow only
125 * SYSTEM processes to "shutdown" the system on hard errors (BSOD)
126 * while forbidding regular processes to do so. This behaviour differs
127 * from Windows, where any user-mode process, as soon as it has the
128 * Shutdown privilege, can trigger a hard-error BSOD.
130 if (!SeSinglePrivilegeCheck(SeTcbPrivilege
, PreviousMode
) ||
131 !SeSinglePrivilegeCheck(SeShutdownPrivilege
, PreviousMode
))
134 *Response
= ResponseNotHandled
;
135 return STATUS_PRIVILEGE_NOT_HELD
;
138 /* Don't handle any new hard errors */
139 ExReadyForErrors
= FALSE
;
142 /* Check if hard errors are not disabled */
143 if (!Thread
->HardErrorsAreDisabled
)
145 /* Check if we can't do errors anymore, and this is serious */
146 if (!ExReadyForErrors
&& NT_ERROR(ErrorStatus
))
148 /* Use the system handler */
149 ExpSystemErrorHandler(ErrorStatus
,
151 UnicodeStringParameterMask
,
153 (PreviousMode
!= KernelMode
) ? TRUE
: FALSE
);
158 * Enable hard error processing if it is enabled for the process
159 * or if the exception status forces it.
161 if ((Process
->DefaultHardErrorProcessing
& SEM_FAILCRITICALERRORS
) ||
162 (ErrorStatus
& HARDERROR_OVERRIDE_ERRORMODE
))
164 /* Check if we have an exception port */
165 if (Process
->ExceptionPort
)
168 PortHandle
= Process
->ExceptionPort
;
172 /* Use our default system port */
173 PortHandle
= ExpDefaultErrorPort
;
178 /* Don't process the error */
182 /* If hard errors are disabled, do nothing */
183 if (Thread
->HardErrorsAreDisabled
) PortHandle
= NULL
;
185 /* Now check if we have a port */
186 if (PortHandle
== NULL
)
188 /* Just return to caller */
189 *Response
= ResponseReturnToCaller
;
190 return STATUS_SUCCESS
;
193 /* Check if this is the default process */
194 if (Process
== ExpDefaultErrorPortProcess
)
196 /* We can't handle the error, check if this is critical */
197 if (NT_ERROR(ErrorStatus
))
199 /* It is, invoke the system handler */
200 ExpSystemErrorHandler(ErrorStatus
,
202 UnicodeStringParameterMask
,
204 (PreviousMode
!= KernelMode
) ? TRUE
: FALSE
);
206 /* If we survived, return to caller */
207 *Response
= ResponseReturnToCaller
;
208 return STATUS_SUCCESS
;
212 /* Setup the LPC Message */
213 Message
->h
.u1
.Length
= (sizeof(HARDERROR_MSG
) << 16) |
214 (sizeof(HARDERROR_MSG
) - sizeof(PORT_MESSAGE
));
215 Message
->h
.u2
.ZeroInit
= 0;
216 Message
->h
.u2
.s2
.Type
= LPC_ERROR_EVENT
;
217 Message
->Status
= ErrorStatus
& ~HARDERROR_OVERRIDE_ERRORMODE
;
218 Message
->ValidResponseOptions
= ValidResponseOptions
;
219 Message
->UnicodeStringParameterMask
= UnicodeStringParameterMask
;
220 Message
->NumberOfParameters
= NumberOfParameters
;
221 KeQuerySystemTime(&Message
->ErrorTime
);
223 /* Copy the parameters */
224 if (Parameters
) RtlMoveMemory(&Message
->Parameters
,
226 sizeof(ULONG_PTR
) * NumberOfParameters
);
228 /* Send the LPC Message */
229 Status
= LpcRequestWaitReplyPort(PortHandle
,
230 (PPORT_MESSAGE
)Message
,
231 (PPORT_MESSAGE
)Message
);
232 if (NT_SUCCESS(Status
))
234 /* Check what kind of response we got */
235 if ((Message
->Response
!= ResponseReturnToCaller
) &&
236 (Message
->Response
!= ResponseNotHandled
) &&
237 (Message
->Response
!= ResponseAbort
) &&
238 (Message
->Response
!= ResponseCancel
) &&
239 (Message
->Response
!= ResponseIgnore
) &&
240 (Message
->Response
!= ResponseNo
) &&
241 (Message
->Response
!= ResponseOk
) &&
242 (Message
->Response
!= ResponseRetry
) &&
243 (Message
->Response
!= ResponseYes
) &&
244 (Message
->Response
!= ResponseTryAgain
) &&
245 (Message
->Response
!= ResponseContinue
))
247 /* Reset to a default one */
248 Message
->Response
= ResponseReturnToCaller
;
251 /* Set the response */
252 *Response
= Message
->Response
;
256 /* Set the response */
257 *Response
= ResponseReturnToCaller
;
265 * @name ExRaiseAccessViolation
268 * The ExRaiseAccessViolation routine can be used with structured exception
269 * handling to throw a driver-determined exception for a memory access
270 * violation that occurs when a driver processes I/O requests.
271 * See: http://msdn.microsoft.com/library/en-us/Kernel_r/hh/Kernel_r/k102_71b4c053-599c-4a6d-8a59-08aae6bdc534.xml.asp?frame=true
272 * http://www.osronline.com/ddkx/kmarch/k102_814i.htm
281 ExRaiseAccessViolation(VOID
)
283 /* Raise the Right Status */
284 RtlRaiseStatus(STATUS_ACCESS_VIOLATION
);
288 * @name ExRaiseDatatypeMisalignment
291 * ExRaiseDatatypeMisalignment raises an exception with the exception
292 * code set to STATUS_DATATYPE_MISALIGNMENT
294 * http://www.osronline.com/ddkx/kmarch/k102_814i.htm
303 ExRaiseDatatypeMisalignment(VOID
)
305 /* Raise the Right Status */
306 RtlRaiseStatus(STATUS_DATATYPE_MISALIGNMENT
);
310 * @name ExSystemExceptionFilter
313 * TODO: Add description
322 ExSystemExceptionFilter(VOID
)
324 return KeGetPreviousMode() != KernelMode
?
325 EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
;
329 * @name ExRaiseHardError
332 * See NtRaiseHardError and ExpRaiseHardError.
337 * @param NumberOfParameters
338 * Number of optional parameters in Parameters array
340 * @param UnicodeStringParameterMask
341 * Optional string parameter (can be only one per error code)
344 * Array of ULONG parameters for use in error message string
346 * @param ValidResponseOptions
347 * See HARDERROR_RESPONSE_OPTION for possible values description
350 * Pointer to HARDERROR_RESPONSE enumeration
357 ExRaiseHardError(IN NTSTATUS ErrorStatus
,
358 IN ULONG NumberOfParameters
,
359 IN ULONG UnicodeStringParameterMask
,
360 IN PULONG_PTR Parameters
,
361 IN ULONG ValidResponseOptions
,
365 UNICODE_STRING CapturedParams
[MAXIMUM_HARDERROR_PARAMETERS
];
367 PVOID UserData
= NULL
;
368 PHARDERROR_USER_PARAMETERS UserParams
;
374 /* Check if we have parameters */
377 /* Check if we have strings */
378 if (UnicodeStringParameterMask
)
380 /* Calculate the required size */
381 Size
= FIELD_OFFSET(HARDERROR_USER_PARAMETERS
, Buffer
[0]);
383 /* Loop each parameter */
384 for (i
= 0; i
< NumberOfParameters
; i
++)
386 /* Check if it's part of the mask */
387 if (UnicodeStringParameterMask
& (1 << i
))
390 RtlMoveMemory(&CapturedParams
[i
],
391 (PVOID
)Parameters
[i
],
392 sizeof(UNICODE_STRING
));
394 /* Increase the size */
395 Size
+= CapturedParams
[i
].MaximumLength
;
399 /* Allocate the user data region */
400 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
406 if (!NT_SUCCESS(Status
)) return Status
;
408 /* Set the pointers to our data */
409 UserParams
= UserData
;
410 BufferBase
= UserParams
->Buffer
;
412 /* Loop parameters again */
413 for (i
= 0; i
< NumberOfParameters
; i
++)
415 /* Check if we're in the mask */
416 if (UnicodeStringParameterMask
& (1 << i
))
418 /* Update the base */
419 UserParams
->Parameters
[i
] = (ULONG_PTR
)&UserParams
->Strings
[i
];
421 /* Copy the string buffer */
422 RtlMoveMemory(BufferBase
,
423 CapturedParams
[i
].Buffer
,
424 CapturedParams
[i
].MaximumLength
);
427 CapturedParams
[i
].Buffer
= BufferBase
;
429 /* Copy the string structure */
430 UserParams
->Strings
[i
] = CapturedParams
[i
];
432 /* Update the pointer */
433 BufferBase
+= CapturedParams
[i
].MaximumLength
;
437 /* No need to copy any strings */
438 UserParams
->Parameters
[i
] = Parameters
[i
];
444 /* Just keep the data as is */
445 UserData
= Parameters
;
449 /* Now call the worker function */
450 Status
= ExpRaiseHardError(ErrorStatus
,
452 UnicodeStringParameterMask
,
454 ValidResponseOptions
,
457 /* Check if we had done user-mode allocation */
458 if ((UserData
) && (UserData
!= Parameters
))
460 /* We did! Delete it */
462 ZwFreeVirtualMemory(NtCurrentProcess(),
468 /* Return status and the response */
469 *Response
= SafeResponse
;
474 * @name NtRaiseHardError
477 * This function sends HARDERROR_MSG LPC message to a hard-error listener,
478 * typically CSRSS.EXE. See NtSetDefaultHardErrorPort for more information.
479 * See also: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Error/NtRaiseHardError.html
484 * @param NumberOfParameters
485 * Number of optional parameters in Parameters array
487 * @param UnicodeStringParameterMask
488 * Optional string parameter (can be only one per error code)
491 * Array of ULONG_PTR parameters for use in error message string
493 * @param ValidResponseOptions
494 * See HARDERROR_RESPONSE_OPTION for possible values description
497 * Pointer to HARDERROR_RESPONSE enumeration
501 * @remarks NtRaiseHardError constitutes an easy way to display messages
502 * in GUI without loading any Win32 API libraries.
507 NtRaiseHardError(IN NTSTATUS ErrorStatus
,
508 IN ULONG NumberOfParameters
,
509 IN ULONG UnicodeStringParameterMask
,
510 IN PULONG_PTR Parameters
,
511 IN ULONG ValidResponseOptions
,
514 NTSTATUS Status
= STATUS_SUCCESS
;
515 PULONG_PTR SafeParams
= NULL
;
517 UNICODE_STRING SafeString
;
520 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
522 /* Validate parameter count */
523 if (NumberOfParameters
> MAXIMUM_HARDERROR_PARAMETERS
)
526 return STATUS_INVALID_PARAMETER_2
;
529 /* Make sure we have some at least */
530 if ((Parameters
!= NULL
) && (NumberOfParameters
== 0))
533 return STATUS_INVALID_PARAMETER_2
;
536 /* Check if we were called from user-mode */
537 if (PreviousMode
!= KernelMode
)
539 /* First validate the responses */
540 switch (ValidResponseOptions
)
542 /* Check all valid cases */
543 case OptionAbortRetryIgnore
:
546 case OptionRetryCancel
:
548 case OptionYesNoCancel
:
549 case OptionShutdownSystem
:
551 case OptionCancelTryContinue
:
554 /* Anything else is invalid */
556 return STATUS_INVALID_PARAMETER_4
;
559 /* Check if we have parameters */
562 /* Calculate size of the parameters */
563 ParamSize
= sizeof(ULONG_PTR
) * NumberOfParameters
;
565 /* Allocate a safe buffer */
566 SafeParams
= ExAllocatePoolWithTag(PagedPool
, ParamSize
, TAG_ERR
);
569 return STATUS_INSUFFICIENT_RESOURCES
;
573 /* Enter SEH Block */
576 /* Validate the response pointer */
577 ProbeForWriteUlong(Response
);
579 /* Check if we have parameters */
582 /* Validate the parameter pointers */
583 ProbeForRead(Parameters
, ParamSize
, sizeof(ULONG_PTR
));
586 RtlCopyMemory(SafeParams
, Parameters
, ParamSize
);
588 /* Now check if there's strings in it */
589 if (UnicodeStringParameterMask
)
591 /* Loop every string */
592 for (i
= 0; i
< NumberOfParameters
; i
++)
594 /* Check if this parameter is a string */
595 if (UnicodeStringParameterMask
& (1 << i
))
597 /* Probe the structure */
598 ProbeForRead((PVOID
)SafeParams
[i
],
599 sizeof(UNICODE_STRING
),
603 RtlCopyMemory(&SafeString
,
604 (PVOID
)SafeParams
[i
],
605 sizeof(UNICODE_STRING
));
607 /* Probe the buffer */
608 ProbeForRead(SafeString
.Buffer
,
609 SafeString
.MaximumLength
,
616 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
618 /* Free captured buffer */
619 if (SafeParams
) ExFreePoolWithTag(SafeParams
, TAG_ERR
);
621 /* Return the exception code */
622 _SEH2_YIELD(return _SEH2_GetExceptionCode());
626 /* Call the system function directly, because we probed */
627 ExpRaiseHardError(ErrorStatus
,
629 UnicodeStringParameterMask
,
631 ValidResponseOptions
,
637 SafeParams
= Parameters
;
640 * Call the Executive Function. It will probe and copy pointers to
643 ExRaiseHardError(ErrorStatus
,
645 UnicodeStringParameterMask
,
647 ValidResponseOptions
,
651 /* Check if we were called in user-mode */
652 if (PreviousMode
!= KernelMode
)
654 /* That means we have a buffer to free */
655 if (SafeParams
) ExFreePoolWithTag(SafeParams
, TAG_ERR
);
657 /* Enter SEH Block for return */
660 /* Return the response */
661 *Response
= SafeResponse
;
663 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
665 /* Get the exception code */
666 Status
= _SEH2_GetExceptionCode();
672 /* Return the response */
673 *Response
= SafeResponse
;
681 * @name NtSetDefaultHardErrorPort
684 * NtSetDefaultHardErrorPort is typically called only once. After the call,
685 * the kernel sets a BOOLEAN flag named ExReadyForErrors to TRUE, and all other
686 * attempts to change the default port fail with STATUS_UNSUCCESSFUL error code.
687 * See: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Error/NtSetDefaultHardErrorPort.html
688 * https://web.archive.org/web/20070716133753/http://www.windowsitlibrary.com/Content/356/08/2.html
691 * Handle to named port object
695 * @remarks Privileges: SE_TCB_PRIVILEGE
700 NtSetDefaultHardErrorPort(IN HANDLE PortHandle
)
702 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
703 NTSTATUS Status
= STATUS_UNSUCCESSFUL
;
705 /* Check if we have the privileges */
706 if (!SeSinglePrivilegeCheck(SeTcbPrivilege
, PreviousMode
))
708 DPRINT1("NtSetDefaultHardErrorPort: Caller requires "
709 "the SeTcbPrivilege privilege!\n");
710 return STATUS_PRIVILEGE_NOT_HELD
;
713 /* Only called once during bootup, make sure we weren't called yet */
714 if (!ExReadyForErrors
)
716 /* Reference the hard-error port */
717 Status
= ObReferenceObjectByHandle(PortHandle
,
721 (PVOID
*)&ExpDefaultErrorPort
,
723 if (NT_SUCCESS(Status
))
725 /* Keep also a reference to the process handling the hard errors */
726 ExpDefaultErrorPortProcess
= PsGetCurrentProcess();
727 ObReferenceObject(ExpDefaultErrorPortProcess
);
728 ExReadyForErrors
= TRUE
;
729 Status
= STATUS_SUCCESS
;
733 /* Return status to caller */
741 /* Not supported in Kernel Mode */
742 RtlRaiseStatus(STATUS_NOT_IMPLEMENTED
);