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
94 * @param NumberOfParameters
97 * @param UnicodeStringParameterMask
103 * @param ValidResponseOptions
116 ExpRaiseHardError(IN NTSTATUS ErrorStatus
,
117 IN ULONG NumberOfParameters
,
118 IN ULONG UnicodeStringParameterMask
,
119 IN PULONG_PTR Parameters
,
120 IN ULONG ValidResponseOptions
,
123 PEPROCESS Process
= PsGetCurrentProcess();
124 PETHREAD Thread
= PsGetCurrentThread();
125 UCHAR Buffer
[PORT_MAXIMUM_MESSAGE_LENGTH
];
126 PHARDERROR_MSG Message
= (PHARDERROR_MSG
)Buffer
;
129 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
132 /* Check if this error will shutdown the system */
133 if (ValidResponseOptions
== OptionShutdownSystem
)
135 /* Check for privilege */
136 if (!SeSinglePrivilegeCheck(SeShutdownPrivilege
, PreviousMode
))
139 *Response
= ResponseNotHandled
;
140 return STATUS_PRIVILEGE_NOT_HELD
;
143 /* Don't handle any new hard errors */
144 ExReadyForErrors
= FALSE
;
147 /* Check if hard errors are not disabled */
148 if (!Thread
->HardErrorsAreDisabled
)
150 /* Check if we can't do errors anymore, and this is serious */
151 if ((!ExReadyForErrors
) && (NT_ERROR(ErrorStatus
)))
153 /* Use the system handler */
154 ExpSystemErrorHandler(ErrorStatus
,
156 UnicodeStringParameterMask
,
158 (PreviousMode
!= KernelMode
) ? TRUE
: FALSE
);
162 /* Enable hard error processing if it is enabled for the process
163 * or if the exception status forces it */
164 if ((Process
->DefaultHardErrorProcessing
& 1) ||
165 (ErrorStatus
& 0x10000000))
167 /* Check if we have an exception port */
168 if (Process
->ExceptionPort
)
171 PortHandle
= Process
->ExceptionPort
;
175 /* Use our default system port */
176 PortHandle
= ExpDefaultErrorPort
;
181 /* Don't process the error */
185 /* If hard errors are disabled, do nothing */
186 if (Thread
->HardErrorsAreDisabled
) PortHandle
= NULL
;
188 /* Now check if we have a port */
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
&~ 0x10000000;
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
,
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
;
261 *Response
= ResponseReturnToCaller
;
262 Status
= STATUS_SUCCESS
;
270 * @name ExRaiseAccessViolation
273 * The ExRaiseAccessViolation routine can be used with structured exception
274 * handling to throw a driver-determined exception for a memory access
275 * violation that occurs when a driver processes I/O requests.
276 * See: http://msdn.microsoft.com/library/en-us/Kernel_r/hh/Kernel_r/k102_71b4c053-599c-4a6d-8a59-08aae6bdc534.xml.asp?frame=true
277 * http://www.osronline.com/ddkx/kmarch/k102_814i.htm
286 ExRaiseAccessViolation(VOID
)
288 /* Raise the Right Status */
289 RtlRaiseStatus(STATUS_ACCESS_VIOLATION
);
293 * @name ExRaiseDatatypeMisalignment
296 * ExRaiseDatatypeMisalignment raises an exception with the exception
297 * code set to STATUS_DATATYPE_MISALIGNMENT
299 * http://www.osronline.com/ddkx/kmarch/k102_814i.htm
308 ExRaiseDatatypeMisalignment(VOID
)
310 /* Raise the Right Status */
311 RtlRaiseStatus(STATUS_DATATYPE_MISALIGNMENT
);
315 * @name ExSystemExceptionFilter
318 * TODO: Add description
327 ExSystemExceptionFilter(VOID
)
329 return KeGetPreviousMode() != KernelMode
?
330 EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
;
334 * @name ExRaiseHardError
337 * See NtRaiseHardError
342 * @param NumberOfParameters
343 * Number of optional parameters in Parameters array
345 * @param UnicodeStringParameterMask
346 * Optional string parameter (can be only one per error code)
349 * Array of ULONG parameters for use in error message string
351 * @param ValidResponseOptions
352 * See HARDERROR_RESPONSE_OPTION for possible values description
355 * Pointer to HARDERROR_RESPONSE enumeration
364 ExRaiseHardError(IN NTSTATUS ErrorStatus
,
365 IN ULONG NumberOfParameters
,
366 IN ULONG UnicodeStringParameterMask
,
367 IN PULONG_PTR Parameters
,
368 IN ULONG ValidResponseOptions
,
372 UNICODE_STRING CapturedParams
[MAXIMUM_HARDERROR_PARAMETERS
];
374 PVOID UserData
= NULL
;
375 PHARDERROR_USER_PARAMETERS UserParams
;
381 /* Check if we have parameters */
384 /* Check if we have strings */
385 if (UnicodeStringParameterMask
)
387 /* Calculate the required size */
388 Size
= FIELD_OFFSET(HARDERROR_USER_PARAMETERS
, Buffer
[0]);
390 /* Loop each parameter */
391 for (i
= 0; i
< NumberOfParameters
; i
++)
393 /* Check if it's part of the mask */
394 if (UnicodeStringParameterMask
& (1 << i
))
397 RtlMoveMemory(&CapturedParams
[i
],
398 (PVOID
)Parameters
[i
],
399 sizeof(UNICODE_STRING
));
401 /* Increase the size */
402 Size
+= CapturedParams
[i
].MaximumLength
;
406 /* Allocate the user data region */
407 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
413 if (!NT_SUCCESS(Status
)) return Status
;
415 /* Set the pointers to our data */
416 UserParams
= UserData
;
417 BufferBase
= UserParams
->Buffer
;
419 /* Loop parameters again */
420 for (i
= 0; i
< NumberOfParameters
; i
++)
422 /* Check if we're in the mask */
423 if (UnicodeStringParameterMask
& (1 << i
))
425 /* Update the base */
426 UserParams
->Parameters
[i
] = (ULONG_PTR
)&UserParams
->Strings
[i
];
428 /* Copy the string buffer */
429 RtlMoveMemory(BufferBase
,
430 CapturedParams
[i
].Buffer
,
431 CapturedParams
[i
].MaximumLength
);
434 CapturedParams
[i
].Buffer
= BufferBase
;
436 /* Copy the string structure */
437 UserParams
->Strings
[i
] = CapturedParams
[i
];
439 /* Update the pointer */
440 BufferBase
+= CapturedParams
[i
].MaximumLength
;
444 /* No need to copy any strings */
445 UserParams
->Parameters
[i
] = Parameters
[i
];
451 /* Just keep the data as is */
452 UserData
= Parameters
;
456 /* Now call the worker function */
457 Status
= ExpRaiseHardError(ErrorStatus
,
459 UnicodeStringParameterMask
,
461 ValidResponseOptions
,
464 /* Check if we had done user-mode allocation */
465 if ((UserData
) && (UserData
!= Parameters
))
467 /* We did! Delete it */
469 ZwFreeVirtualMemory(NtCurrentProcess(),
475 /* Return status and the response */
476 *Response
= SafeResponse
;
481 * @name NtRaiseHardError
484 * This function sends HARDERROR_MSG LPC message to listener
485 * (typically CSRSS.EXE). See NtSetDefaultHardErrorPort for more information
486 * See: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Error/NtRaiseHardError.html
491 * @param NumberOfParameters
492 * Number of optional parameters in Parameters array
494 * @param UnicodeStringParameterMask
495 * Optional string parameter (can be only one per error code)
498 * Array of ULONG_PTR parameters for use in error message string
500 * @param ValidResponseOptions
501 * See HARDERROR_RESPONSE_OPTION for possible values description
504 * Pointer to HARDERROR_RESPONSE enumeration
508 * @remarks NtRaiseHardError is easy way to display message in GUI
509 * without loading Win32 API libraries
514 NtRaiseHardError(IN NTSTATUS ErrorStatus
,
515 IN ULONG NumberOfParameters
,
516 IN ULONG UnicodeStringParameterMask
,
517 IN PULONG_PTR Parameters
,
518 IN ULONG ValidResponseOptions
,
521 NTSTATUS Status
= STATUS_SUCCESS
;
522 PULONG_PTR SafeParams
= NULL
;
524 UNICODE_STRING SafeString
;
527 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
529 /* Validate parameter count */
530 if (NumberOfParameters
> MAXIMUM_HARDERROR_PARAMETERS
)
533 return STATUS_INVALID_PARAMETER_2
;
536 /* Make sure we have some at least */
537 if ((Parameters
!= NULL
) && (NumberOfParameters
== 0))
540 return STATUS_INVALID_PARAMETER_2
;
543 /* Check if we were called from user-mode */
544 if (PreviousMode
!= KernelMode
)
546 /* First validate the responses */
547 switch (ValidResponseOptions
)
549 /* Check all valid cases */
550 case OptionAbortRetryIgnore
:
553 case OptionRetryCancel
:
555 case OptionYesNoCancel
:
556 case OptionShutdownSystem
:
559 /* Anything else is invalid */
561 return STATUS_INVALID_PARAMETER_4
;
564 /* Check if we have parameters */
567 /* Calculate size of the parameters */
568 ParamSize
= sizeof(ULONG_PTR
) * NumberOfParameters
;
570 /* Allocate a safe buffer */
571 SafeParams
= ExAllocatePoolWithTag(PagedPool
, ParamSize
, TAG_ERR
);
574 return STATUS_INSUFFICIENT_RESOURCES
;
578 /* Enter SEH Block */
581 /* Validate the response pointer */
582 ProbeForWriteUlong(Response
);
584 /* Check if we have parameters */
587 /* Validate the parameter pointers */
588 ProbeForRead(Parameters
, ParamSize
, sizeof(ULONG_PTR
));
591 RtlCopyMemory(SafeParams
, Parameters
, ParamSize
);
593 /* Now check if there's strings in it */
594 if (UnicodeStringParameterMask
)
596 /* Loop every string */
597 for (i
= 0; i
< NumberOfParameters
; i
++)
599 /* Check if this parameter is a string */
600 if (UnicodeStringParameterMask
& (1 << i
))
602 /* Probe the structure */
603 ProbeForRead((PVOID
)SafeParams
[i
],
604 sizeof(UNICODE_STRING
),
608 RtlCopyMemory(&SafeString
,
609 (PVOID
)SafeParams
[i
],
610 sizeof(UNICODE_STRING
));
612 /* Probe the buffer */
613 ProbeForRead(SafeString
.Buffer
,
614 SafeString
.MaximumLength
,
621 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
623 /* Free captured buffer */
624 if (SafeParams
) ExFreePoolWithTag(SafeParams
, TAG_ERR
);
626 /* Return the exception code */
627 _SEH2_YIELD(return _SEH2_GetExceptionCode());
631 /* Call the system function directly, because we probed */
632 ExpRaiseHardError(ErrorStatus
,
634 UnicodeStringParameterMask
,
636 ValidResponseOptions
,
642 SafeParams
= Parameters
;
645 * Call the Executive Function. It will probe and copy pointers to
648 ExRaiseHardError(ErrorStatus
,
650 UnicodeStringParameterMask
,
652 ValidResponseOptions
,
656 /* Check if we were called in user-mode */
657 if (PreviousMode
!= KernelMode
)
659 /* That means we have a buffer to free */
660 if (SafeParams
) ExFreePoolWithTag(SafeParams
, TAG_ERR
);
662 /* Enter SEH Block for return */
665 /* Return the response */
666 *Response
= SafeResponse
;
668 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
670 /* Get the exception code */
671 Status
= _SEH2_GetExceptionCode();
677 /* Return the response */
678 *Response
= SafeResponse
;
686 * @name NtSetDefaultHardErrorPort
689 * NtSetDefaultHardErrorPort is typically called only once. After call,
690 * kernel set BOOLEAN flag named _ExReadyForErrors to TRUE, and all other
691 * tries to change default port are broken with STATUS_UNSUCCESSFUL error code
692 * See: http://www.windowsitlibrary.com/Content/356/08/2.html
693 * http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Error/NtSetDefaultHardErrorPort.html
696 * Handle to named port object
700 * @remarks Privileges: SE_TCB_PRIVILEGE
705 NtSetDefaultHardErrorPort(IN HANDLE PortHandle
)
707 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
708 NTSTATUS Status
= STATUS_UNSUCCESSFUL
;
710 /* Check if we have the Privilege */
711 if (!SeSinglePrivilegeCheck(SeTcbPrivilege
, PreviousMode
))
713 DPRINT1("NtSetDefaultHardErrorPort: Caller requires "
714 "the SeTcbPrivilege privilege!\n");
715 return STATUS_PRIVILEGE_NOT_HELD
;
718 /* Only called once during bootup, make sure we weren't called yet */
719 if (!ExReadyForErrors
)
721 /* Reference the port */
722 Status
= ObReferenceObjectByHandle(PortHandle
,
726 (PVOID
*)&ExpDefaultErrorPort
,
728 if (NT_SUCCESS(Status
))
731 ExpDefaultErrorPortProcess
= PsGetCurrentProcess();
732 ExReadyForErrors
= TRUE
;
736 /* Return status to caller */
744 /* Not supported in Kernel Mode */
745 RtlRaiseStatus(STATUS_NOT_IMPLEMENTED
);