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
& HARDERROR_OVERRIDE_ERRORMODE
))
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 */
189 if (PortHandle
== NULL
)
191 /* Just return to caller */
192 *Response
= ResponseReturnToCaller
;
193 return STATUS_SUCCESS
;
196 /* Check if this is the default process */
197 if (Process
== ExpDefaultErrorPortProcess
)
199 /* We can't handle the error, check if this is critical */
200 if (NT_ERROR(ErrorStatus
))
202 /* It is, invoke the system handler */
203 ExpSystemErrorHandler(ErrorStatus
,
205 UnicodeStringParameterMask
,
207 (PreviousMode
!= KernelMode
) ? TRUE
: FALSE
);
209 /* If we survived, return to caller */
210 *Response
= ResponseReturnToCaller
;
211 return STATUS_SUCCESS
;
215 /* Setup the LPC Message */
216 Message
->h
.u1
.Length
= (sizeof(HARDERROR_MSG
) << 16) |
217 (sizeof(HARDERROR_MSG
) - sizeof(PORT_MESSAGE
));
218 Message
->h
.u2
.ZeroInit
= 0;
219 Message
->h
.u2
.s2
.Type
= LPC_ERROR_EVENT
;
220 Message
->Status
= ErrorStatus
& ~HARDERROR_OVERRIDE_ERRORMODE
;
221 Message
->ValidResponseOptions
= ValidResponseOptions
;
222 Message
->UnicodeStringParameterMask
= UnicodeStringParameterMask
;
223 Message
->NumberOfParameters
= NumberOfParameters
;
224 KeQuerySystemTime(&Message
->ErrorTime
);
226 /* Copy the parameters */
227 if (Parameters
) RtlMoveMemory(&Message
->Parameters
,
229 sizeof(ULONG_PTR
) * NumberOfParameters
);
231 /* Send the LPC Message */
232 Status
= LpcRequestWaitReplyPort(PortHandle
,
233 (PPORT_MESSAGE
)Message
,
234 (PPORT_MESSAGE
)Message
);
235 if (NT_SUCCESS(Status
))
237 /* Check what kind of response we got */
238 if ((Message
->Response
!= ResponseReturnToCaller
) &&
239 (Message
->Response
!= ResponseNotHandled
) &&
240 (Message
->Response
!= ResponseAbort
) &&
241 (Message
->Response
!= ResponseCancel
) &&
242 (Message
->Response
!= ResponseIgnore
) &&
243 (Message
->Response
!= ResponseNo
) &&
244 (Message
->Response
!= ResponseOk
) &&
245 (Message
->Response
!= ResponseRetry
) &&
246 (Message
->Response
!= ResponseYes
) &&
247 (Message
->Response
!= ResponseTryAgain
) &&
248 (Message
->Response
!= ResponseContinue
))
250 /* Reset to a default one */
251 Message
->Response
= ResponseReturnToCaller
;
254 /* Set the response */
255 *Response
= Message
->Response
;
259 /* Set the response */
260 *Response
= ResponseReturnToCaller
;
268 * @name ExRaiseAccessViolation
271 * The ExRaiseAccessViolation routine can be used with structured exception
272 * handling to throw a driver-determined exception for a memory access
273 * violation that occurs when a driver processes I/O requests.
274 * See: http://msdn.microsoft.com/library/en-us/Kernel_r/hh/Kernel_r/k102_71b4c053-599c-4a6d-8a59-08aae6bdc534.xml.asp?frame=true
275 * http://www.osronline.com/ddkx/kmarch/k102_814i.htm
284 ExRaiseAccessViolation(VOID
)
286 /* Raise the Right Status */
287 RtlRaiseStatus(STATUS_ACCESS_VIOLATION
);
291 * @name ExRaiseDatatypeMisalignment
294 * ExRaiseDatatypeMisalignment raises an exception with the exception
295 * code set to STATUS_DATATYPE_MISALIGNMENT
297 * http://www.osronline.com/ddkx/kmarch/k102_814i.htm
306 ExRaiseDatatypeMisalignment(VOID
)
308 /* Raise the Right Status */
309 RtlRaiseStatus(STATUS_DATATYPE_MISALIGNMENT
);
313 * @name ExSystemExceptionFilter
316 * TODO: Add description
325 ExSystemExceptionFilter(VOID
)
327 return KeGetPreviousMode() != KernelMode
?
328 EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
;
332 * @name ExRaiseHardError
335 * See NtRaiseHardError
340 * @param NumberOfParameters
341 * Number of optional parameters in Parameters array
343 * @param UnicodeStringParameterMask
344 * Optional string parameter (can be only one per error code)
347 * Array of ULONG parameters for use in error message string
349 * @param ValidResponseOptions
350 * See HARDERROR_RESPONSE_OPTION for possible values description
353 * Pointer to HARDERROR_RESPONSE enumeration
362 ExRaiseHardError(IN NTSTATUS ErrorStatus
,
363 IN ULONG NumberOfParameters
,
364 IN ULONG UnicodeStringParameterMask
,
365 IN PULONG_PTR Parameters
,
366 IN ULONG ValidResponseOptions
,
370 UNICODE_STRING CapturedParams
[MAXIMUM_HARDERROR_PARAMETERS
];
372 PVOID UserData
= NULL
;
373 PHARDERROR_USER_PARAMETERS UserParams
;
379 /* Check if we have parameters */
382 /* Check if we have strings */
383 if (UnicodeStringParameterMask
)
385 /* Calculate the required size */
386 Size
= FIELD_OFFSET(HARDERROR_USER_PARAMETERS
, Buffer
[0]);
388 /* Loop each parameter */
389 for (i
= 0; i
< NumberOfParameters
; i
++)
391 /* Check if it's part of the mask */
392 if (UnicodeStringParameterMask
& (1 << i
))
395 RtlMoveMemory(&CapturedParams
[i
],
396 (PVOID
)Parameters
[i
],
397 sizeof(UNICODE_STRING
));
399 /* Increase the size */
400 Size
+= CapturedParams
[i
].MaximumLength
;
404 /* Allocate the user data region */
405 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
411 if (!NT_SUCCESS(Status
)) return Status
;
413 /* Set the pointers to our data */
414 UserParams
= UserData
;
415 BufferBase
= UserParams
->Buffer
;
417 /* Loop parameters again */
418 for (i
= 0; i
< NumberOfParameters
; i
++)
420 /* Check if we're in the mask */
421 if (UnicodeStringParameterMask
& (1 << i
))
423 /* Update the base */
424 UserParams
->Parameters
[i
] = (ULONG_PTR
)&UserParams
->Strings
[i
];
426 /* Copy the string buffer */
427 RtlMoveMemory(BufferBase
,
428 CapturedParams
[i
].Buffer
,
429 CapturedParams
[i
].MaximumLength
);
432 CapturedParams
[i
].Buffer
= BufferBase
;
434 /* Copy the string structure */
435 UserParams
->Strings
[i
] = CapturedParams
[i
];
437 /* Update the pointer */
438 BufferBase
+= CapturedParams
[i
].MaximumLength
;
442 /* No need to copy any strings */
443 UserParams
->Parameters
[i
] = Parameters
[i
];
449 /* Just keep the data as is */
450 UserData
= Parameters
;
454 /* Now call the worker function */
455 Status
= ExpRaiseHardError(ErrorStatus
,
457 UnicodeStringParameterMask
,
459 ValidResponseOptions
,
462 /* Check if we had done user-mode allocation */
463 if ((UserData
) && (UserData
!= Parameters
))
465 /* We did! Delete it */
467 ZwFreeVirtualMemory(NtCurrentProcess(),
473 /* Return status and the response */
474 *Response
= SafeResponse
;
479 * @name NtRaiseHardError
482 * This function sends HARDERROR_MSG LPC message to listener
483 * (typically CSRSS.EXE). See NtSetDefaultHardErrorPort for more information
484 * See: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Error/NtRaiseHardError.html
489 * @param NumberOfParameters
490 * Number of optional parameters in Parameters array
492 * @param UnicodeStringParameterMask
493 * Optional string parameter (can be only one per error code)
496 * Array of ULONG_PTR parameters for use in error message string
498 * @param ValidResponseOptions
499 * See HARDERROR_RESPONSE_OPTION for possible values description
502 * Pointer to HARDERROR_RESPONSE enumeration
506 * @remarks NtRaiseHardError is easy way to display message in GUI
507 * without loading Win32 API libraries
512 NtRaiseHardError(IN NTSTATUS ErrorStatus
,
513 IN ULONG NumberOfParameters
,
514 IN ULONG UnicodeStringParameterMask
,
515 IN PULONG_PTR Parameters
,
516 IN ULONG ValidResponseOptions
,
519 NTSTATUS Status
= STATUS_SUCCESS
;
520 PULONG_PTR SafeParams
= NULL
;
522 UNICODE_STRING SafeString
;
525 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
527 /* Validate parameter count */
528 if (NumberOfParameters
> MAXIMUM_HARDERROR_PARAMETERS
)
531 return STATUS_INVALID_PARAMETER_2
;
534 /* Make sure we have some at least */
535 if ((Parameters
!= NULL
) && (NumberOfParameters
== 0))
538 return STATUS_INVALID_PARAMETER_2
;
541 /* Check if we were called from user-mode */
542 if (PreviousMode
!= KernelMode
)
544 /* First validate the responses */
545 switch (ValidResponseOptions
)
547 /* Check all valid cases */
548 case OptionAbortRetryIgnore
:
551 case OptionRetryCancel
:
553 case OptionYesNoCancel
:
554 case OptionShutdownSystem
:
556 case OptionCancelTryContinue
:
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
);