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
);
157 /* Enable hard error processing if it is enabled for the process
158 * or if the exception status forces it */
159 if ((Process
->DefaultHardErrorProcessing
& 1) ||
160 (ErrorStatus
& HARDERROR_OVERRIDE_ERRORMODE
))
162 /* Check if we have an exception port */
163 if (Process
->ExceptionPort
)
166 PortHandle
= Process
->ExceptionPort
;
170 /* Use our default system port */
171 PortHandle
= ExpDefaultErrorPort
;
176 /* Don't process the error */
180 /* If hard errors are disabled, do nothing */
181 if (Thread
->HardErrorsAreDisabled
) PortHandle
= NULL
;
183 /* Now check if we have a port */
184 if (PortHandle
== NULL
)
186 /* Just return to caller */
187 *Response
= ResponseReturnToCaller
;
188 return STATUS_SUCCESS
;
191 /* Check if this is the default process */
192 if (Process
== ExpDefaultErrorPortProcess
)
194 /* We can't handle the error, check if this is critical */
195 if (NT_ERROR(ErrorStatus
))
197 /* It is, invoke the system handler */
198 ExpSystemErrorHandler(ErrorStatus
,
200 UnicodeStringParameterMask
,
202 (PreviousMode
!= KernelMode
) ? TRUE
: FALSE
);
204 /* If we survived, return to caller */
205 *Response
= ResponseReturnToCaller
;
206 return STATUS_SUCCESS
;
210 /* Setup the LPC Message */
211 Message
->h
.u1
.Length
= (sizeof(HARDERROR_MSG
) << 16) |
212 (sizeof(HARDERROR_MSG
) - sizeof(PORT_MESSAGE
));
213 Message
->h
.u2
.ZeroInit
= 0;
214 Message
->h
.u2
.s2
.Type
= LPC_ERROR_EVENT
;
215 Message
->Status
= ErrorStatus
& ~HARDERROR_OVERRIDE_ERRORMODE
;
216 Message
->ValidResponseOptions
= ValidResponseOptions
;
217 Message
->UnicodeStringParameterMask
= UnicodeStringParameterMask
;
218 Message
->NumberOfParameters
= NumberOfParameters
;
219 KeQuerySystemTime(&Message
->ErrorTime
);
221 /* Copy the parameters */
222 if (Parameters
) RtlMoveMemory(&Message
->Parameters
,
224 sizeof(ULONG_PTR
) * NumberOfParameters
);
226 /* Send the LPC Message */
227 Status
= LpcRequestWaitReplyPort(PortHandle
,
228 (PPORT_MESSAGE
)Message
,
229 (PPORT_MESSAGE
)Message
);
230 if (NT_SUCCESS(Status
))
232 /* Check what kind of response we got */
233 if ((Message
->Response
!= ResponseReturnToCaller
) &&
234 (Message
->Response
!= ResponseNotHandled
) &&
235 (Message
->Response
!= ResponseAbort
) &&
236 (Message
->Response
!= ResponseCancel
) &&
237 (Message
->Response
!= ResponseIgnore
) &&
238 (Message
->Response
!= ResponseNo
) &&
239 (Message
->Response
!= ResponseOk
) &&
240 (Message
->Response
!= ResponseRetry
) &&
241 (Message
->Response
!= ResponseYes
) &&
242 (Message
->Response
!= ResponseTryAgain
) &&
243 (Message
->Response
!= ResponseContinue
))
245 /* Reset to a default one */
246 Message
->Response
= ResponseReturnToCaller
;
249 /* Set the response */
250 *Response
= Message
->Response
;
254 /* Set the response */
255 *Response
= ResponseReturnToCaller
;
263 * @name ExRaiseAccessViolation
266 * The ExRaiseAccessViolation routine can be used with structured exception
267 * handling to throw a driver-determined exception for a memory access
268 * violation that occurs when a driver processes I/O requests.
269 * See: http://msdn.microsoft.com/library/en-us/Kernel_r/hh/Kernel_r/k102_71b4c053-599c-4a6d-8a59-08aae6bdc534.xml.asp?frame=true
270 * http://www.osronline.com/ddkx/kmarch/k102_814i.htm
279 ExRaiseAccessViolation(VOID
)
281 /* Raise the Right Status */
282 RtlRaiseStatus(STATUS_ACCESS_VIOLATION
);
286 * @name ExRaiseDatatypeMisalignment
289 * ExRaiseDatatypeMisalignment raises an exception with the exception
290 * code set to STATUS_DATATYPE_MISALIGNMENT
292 * http://www.osronline.com/ddkx/kmarch/k102_814i.htm
301 ExRaiseDatatypeMisalignment(VOID
)
303 /* Raise the Right Status */
304 RtlRaiseStatus(STATUS_DATATYPE_MISALIGNMENT
);
308 * @name ExSystemExceptionFilter
311 * TODO: Add description
320 ExSystemExceptionFilter(VOID
)
322 return KeGetPreviousMode() != KernelMode
?
323 EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
;
327 * @name ExRaiseHardError
330 * See NtRaiseHardError and ExpRaiseHardError.
335 * @param NumberOfParameters
336 * Number of optional parameters in Parameters array
338 * @param UnicodeStringParameterMask
339 * Optional string parameter (can be only one per error code)
342 * Array of ULONG parameters for use in error message string
344 * @param ValidResponseOptions
345 * See HARDERROR_RESPONSE_OPTION for possible values description
348 * Pointer to HARDERROR_RESPONSE enumeration
355 ExRaiseHardError(IN NTSTATUS ErrorStatus
,
356 IN ULONG NumberOfParameters
,
357 IN ULONG UnicodeStringParameterMask
,
358 IN PULONG_PTR Parameters
,
359 IN ULONG ValidResponseOptions
,
363 UNICODE_STRING CapturedParams
[MAXIMUM_HARDERROR_PARAMETERS
];
365 PVOID UserData
= NULL
;
366 PHARDERROR_USER_PARAMETERS UserParams
;
372 /* Check if we have parameters */
375 /* Check if we have strings */
376 if (UnicodeStringParameterMask
)
378 /* Calculate the required size */
379 Size
= FIELD_OFFSET(HARDERROR_USER_PARAMETERS
, Buffer
[0]);
381 /* Loop each parameter */
382 for (i
= 0; i
< NumberOfParameters
; i
++)
384 /* Check if it's part of the mask */
385 if (UnicodeStringParameterMask
& (1 << i
))
388 RtlMoveMemory(&CapturedParams
[i
],
389 (PVOID
)Parameters
[i
],
390 sizeof(UNICODE_STRING
));
392 /* Increase the size */
393 Size
+= CapturedParams
[i
].MaximumLength
;
397 /* Allocate the user data region */
398 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
404 if (!NT_SUCCESS(Status
)) return Status
;
406 /* Set the pointers to our data */
407 UserParams
= UserData
;
408 BufferBase
= UserParams
->Buffer
;
410 /* Loop parameters again */
411 for (i
= 0; i
< NumberOfParameters
; i
++)
413 /* Check if we're in the mask */
414 if (UnicodeStringParameterMask
& (1 << i
))
416 /* Update the base */
417 UserParams
->Parameters
[i
] = (ULONG_PTR
)&UserParams
->Strings
[i
];
419 /* Copy the string buffer */
420 RtlMoveMemory(BufferBase
,
421 CapturedParams
[i
].Buffer
,
422 CapturedParams
[i
].MaximumLength
);
425 CapturedParams
[i
].Buffer
= BufferBase
;
427 /* Copy the string structure */
428 UserParams
->Strings
[i
] = CapturedParams
[i
];
430 /* Update the pointer */
431 BufferBase
+= CapturedParams
[i
].MaximumLength
;
435 /* No need to copy any strings */
436 UserParams
->Parameters
[i
] = Parameters
[i
];
442 /* Just keep the data as is */
443 UserData
= Parameters
;
447 /* Now call the worker function */
448 Status
= ExpRaiseHardError(ErrorStatus
,
450 UnicodeStringParameterMask
,
452 ValidResponseOptions
,
455 /* Check if we had done user-mode allocation */
456 if ((UserData
) && (UserData
!= Parameters
))
458 /* We did! Delete it */
460 ZwFreeVirtualMemory(NtCurrentProcess(),
466 /* Return status and the response */
467 *Response
= SafeResponse
;
472 * @name NtRaiseHardError
475 * This function sends HARDERROR_MSG LPC message to a hard-error listener,
476 * typically CSRSS.EXE. See NtSetDefaultHardErrorPort for more information.
477 * See also: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Error/NtRaiseHardError.html
482 * @param NumberOfParameters
483 * Number of optional parameters in Parameters array
485 * @param UnicodeStringParameterMask
486 * Optional string parameter (can be only one per error code)
489 * Array of ULONG_PTR parameters for use in error message string
491 * @param ValidResponseOptions
492 * See HARDERROR_RESPONSE_OPTION for possible values description
495 * Pointer to HARDERROR_RESPONSE enumeration
499 * @remarks NtRaiseHardError constitutes an easy way to display messages
500 * in GUI without loading any Win32 API libraries.
505 NtRaiseHardError(IN NTSTATUS ErrorStatus
,
506 IN ULONG NumberOfParameters
,
507 IN ULONG UnicodeStringParameterMask
,
508 IN PULONG_PTR Parameters
,
509 IN ULONG ValidResponseOptions
,
512 NTSTATUS Status
= STATUS_SUCCESS
;
513 PULONG_PTR SafeParams
= NULL
;
515 UNICODE_STRING SafeString
;
518 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
520 /* Validate parameter count */
521 if (NumberOfParameters
> MAXIMUM_HARDERROR_PARAMETERS
)
524 return STATUS_INVALID_PARAMETER_2
;
527 /* Make sure we have some at least */
528 if ((Parameters
!= NULL
) && (NumberOfParameters
== 0))
531 return STATUS_INVALID_PARAMETER_2
;
534 /* Check if we were called from user-mode */
535 if (PreviousMode
!= KernelMode
)
537 /* First validate the responses */
538 switch (ValidResponseOptions
)
540 /* Check all valid cases */
541 case OptionAbortRetryIgnore
:
544 case OptionRetryCancel
:
546 case OptionYesNoCancel
:
547 case OptionShutdownSystem
:
549 case OptionCancelTryContinue
:
552 /* Anything else is invalid */
554 return STATUS_INVALID_PARAMETER_4
;
557 /* Check if we have parameters */
560 /* Calculate size of the parameters */
561 ParamSize
= sizeof(ULONG_PTR
) * NumberOfParameters
;
563 /* Allocate a safe buffer */
564 SafeParams
= ExAllocatePoolWithTag(PagedPool
, ParamSize
, TAG_ERR
);
567 return STATUS_INSUFFICIENT_RESOURCES
;
571 /* Enter SEH Block */
574 /* Validate the response pointer */
575 ProbeForWriteUlong(Response
);
577 /* Check if we have parameters */
580 /* Validate the parameter pointers */
581 ProbeForRead(Parameters
, ParamSize
, sizeof(ULONG_PTR
));
584 RtlCopyMemory(SafeParams
, Parameters
, ParamSize
);
586 /* Now check if there's strings in it */
587 if (UnicodeStringParameterMask
)
589 /* Loop every string */
590 for (i
= 0; i
< NumberOfParameters
; i
++)
592 /* Check if this parameter is a string */
593 if (UnicodeStringParameterMask
& (1 << i
))
595 /* Probe the structure */
596 ProbeForRead((PVOID
)SafeParams
[i
],
597 sizeof(UNICODE_STRING
),
601 RtlCopyMemory(&SafeString
,
602 (PVOID
)SafeParams
[i
],
603 sizeof(UNICODE_STRING
));
605 /* Probe the buffer */
606 ProbeForRead(SafeString
.Buffer
,
607 SafeString
.MaximumLength
,
614 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
616 /* Free captured buffer */
617 if (SafeParams
) ExFreePoolWithTag(SafeParams
, TAG_ERR
);
619 /* Return the exception code */
620 _SEH2_YIELD(return _SEH2_GetExceptionCode());
624 /* Call the system function directly, because we probed */
625 ExpRaiseHardError(ErrorStatus
,
627 UnicodeStringParameterMask
,
629 ValidResponseOptions
,
635 SafeParams
= Parameters
;
638 * Call the Executive Function. It will probe and copy pointers to
641 ExRaiseHardError(ErrorStatus
,
643 UnicodeStringParameterMask
,
645 ValidResponseOptions
,
649 /* Check if we were called in user-mode */
650 if (PreviousMode
!= KernelMode
)
652 /* That means we have a buffer to free */
653 if (SafeParams
) ExFreePoolWithTag(SafeParams
, TAG_ERR
);
655 /* Enter SEH Block for return */
658 /* Return the response */
659 *Response
= SafeResponse
;
661 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
663 /* Get the exception code */
664 Status
= _SEH2_GetExceptionCode();
670 /* Return the response */
671 *Response
= SafeResponse
;
679 * @name NtSetDefaultHardErrorPort
682 * NtSetDefaultHardErrorPort is typically called only once. After the call,
683 * the kernel sets a BOOLEAN flag named ExReadyForErrors to TRUE, and all other
684 * attempts to change the default port fail with STATUS_UNSUCCESSFUL error code.
685 * See: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Error/NtSetDefaultHardErrorPort.html
686 * https://web.archive.org/web/20070716133753/http://www.windowsitlibrary.com/Content/356/08/2.html
689 * Handle to named port object
693 * @remarks Privileges: SE_TCB_PRIVILEGE
698 NtSetDefaultHardErrorPort(IN HANDLE PortHandle
)
700 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
701 NTSTATUS Status
= STATUS_UNSUCCESSFUL
;
703 /* Check if we have the privileges */
704 if (!SeSinglePrivilegeCheck(SeTcbPrivilege
, PreviousMode
))
706 DPRINT1("NtSetDefaultHardErrorPort: Caller requires "
707 "the SeTcbPrivilege privilege!\n");
708 return STATUS_PRIVILEGE_NOT_HELD
;
711 /* Only called once during bootup, make sure we weren't called yet */
712 if (!ExReadyForErrors
)
714 /* Reference the hard-error port */
715 Status
= ObReferenceObjectByHandle(PortHandle
,
719 (PVOID
*)&ExpDefaultErrorPort
,
721 if (NT_SUCCESS(Status
))
723 /* Keep also a reference to the process handling the hard errors */
724 ExpDefaultErrorPortProcess
= PsGetCurrentProcess();
725 ObReferenceObject(ExpDefaultErrorPortProcess
);
726 ExReadyForErrors
= TRUE
;
727 Status
= STATUS_SUCCESS
;
731 /* Return status to caller */
739 /* Not supported in Kernel Mode */
740 RtlRaiseStatus(STATUS_NOT_IMPLEMENTED
);