2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ex/error.c
5 * PURPOSE: Error Functions and Status/Exception Dispatching/Raising
6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
9 /* INCLUDES *****************************************************************/
15 #define TAG_ERR TAG('E', 'r', 'r', ' ')
17 /* GLOBALS ****************************************************************/
19 BOOLEAN ExReadyForErrors
= FALSE
;
20 PVOID ExpDefaultErrorPort
= NULL
;
21 PEPROCESS ExpDefaultErrorPortProcess
= NULL
;
23 /* FUNCTIONS ****************************************************************/
26 * @name ExpRaiseHardError
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
,
62 KeBugCheckEx(FATAL_UNHANDLED_HARD_ERROR
,
67 return STATUS_SUCCESS
;
71 * @name ExpRaiseHardError
78 * @param NumberOfParameters
81 * @param UnicodeStringParameterMask
87 * @param ValidResponseOptions
100 ExpRaiseHardError(IN NTSTATUS ErrorStatus
,
101 IN ULONG NumberOfParameters
,
102 IN ULONG UnicodeStringParameterMask
,
103 IN PULONG_PTR Parameters
,
104 IN ULONG ValidResponseOptions
,
107 PEPROCESS Process
= PsGetCurrentProcess();
108 PETHREAD Thread
= PsGetCurrentThread();
109 UCHAR Buffer
[PORT_MAXIMUM_MESSAGE_LENGTH
];
110 PHARDERROR_MSG Message
= (PHARDERROR_MSG
)Buffer
;
113 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
116 /* Check if this error will shutdown the system */
117 if (ValidResponseOptions
== OptionShutdownSystem
)
119 /* Check for privilege */
120 if (!SeSinglePrivilegeCheck(SeShutdownPrivilege
, PreviousMode
))
123 return STATUS_PRIVILEGE_NOT_HELD
;
126 /* Don't handle any new hard errors */
127 ExReadyForErrors
= FALSE
;
130 /* Check if hard errors are not disabled */
131 if (!Thread
->HardErrorsAreDisabled
)
133 /* Check if we can't do errors anymore, and this is serious */
134 if ((!ExReadyForErrors
) && (NT_ERROR(ErrorStatus
)))
136 /* Use the system handler */
137 ExpSystemErrorHandler(ErrorStatus
,
139 UnicodeStringParameterMask
,
141 (PreviousMode
!= KernelMode
) ? TRUE
: FALSE
);
145 /* Check if we have an exception port */
146 if (Process
->ExceptionPort
)
148 /* Check if hard errors should be processed */
149 if (Process
->DefaultHardErrorProcessing
& 1)
152 PortHandle
= Process
->ExceptionPort
;
156 /* It's disabled, check if the error overrides it */
157 if (ErrorStatus
& 0x10000000)
159 /* Use the port anyway */
160 PortHandle
= Process
->ExceptionPort
;
171 /* Check if hard errors are enabled */
172 if (Process
->DefaultHardErrorProcessing
& 1)
174 /* Use our default system port */
175 PortHandle
= ExpDefaultErrorPort
;
179 /* It's disabled, check if the error overrides it */
180 if (ErrorStatus
& 0x10000000)
182 /* Use the port anyway */
183 PortHandle
= ExpDefaultErrorPort
;
193 /* If hard errors are disabled, do nothing */
194 if (Thread
->HardErrorsAreDisabled
) PortHandle
= NULL
;
196 /* Now check if we have a port */
199 /* Check if this is the default process */
200 if (Process
== ExpDefaultErrorPortProcess
)
202 /* We can't handle the error, check if this is critical */
203 if (NT_ERROR(ErrorStatus
))
205 /* It is, invoke the system handler */
206 ExpSystemErrorHandler(ErrorStatus
,
208 UnicodeStringParameterMask
,
210 (PreviousMode
!= KernelMode
) ? TRUE
: FALSE
);
212 /* If we survived, return to caller */
213 *Response
= ResponseReturnToCaller
;
214 return STATUS_SUCCESS
;
218 /* Setup the LPC Message */
219 Message
->h
.u1
.Length
= (sizeof(HARDERROR_MSG
) << 16) |
220 (sizeof(HARDERROR_MSG
) - sizeof(PORT_MESSAGE
));
221 Message
->h
.u2
.ZeroInit
= LPC_ERROR_EVENT
;
222 Message
->Status
= ErrorStatus
&~ 0x10000000;
223 Message
->ValidResponseOptions
= ValidResponseOptions
;
224 Message
->UnicodeStringParameterMask
= UnicodeStringParameterMask
;
225 Message
->NumberOfParameters
= NumberOfParameters
;
226 KeQuerySystemTime(&Message
->ErrorTime
);
228 /* Copy the parameters */
229 if (Parameters
) RtlMoveMemory(&Message
->Parameters
,
231 sizeof(ULONG_PTR
) * NumberOfParameters
);
233 /* Send the LPC Message */
234 Status
= LpcRequestWaitReplyPort(PortHandle
,
237 if (NT_SUCCESS(Status
))
239 /* Check what kind of response we got */
240 if ((Message
->Response
!= ResponseReturnToCaller
) &&
241 (Message
->Response
!= ResponseNotHandled
) &&
242 (Message
->Response
!= ResponseAbort
) &&
243 (Message
->Response
!= ResponseCancel
) &&
244 (Message
->Response
!= ResponseIgnore
) &&
245 (Message
->Response
!= ResponseNo
) &&
246 (Message
->Response
!= ResponseOk
) &&
247 (Message
->Response
!= ResponseRetry
) &&
248 (Message
->Response
!= ResponseYes
) &&
249 (Message
->Response
!= ResponseTryAgain
) &&
250 (Message
->Response
!= ResponseContinue
))
252 /* Reset to a default one */
253 Message
->Response
= ResponseReturnToCaller
;
256 /* Set the response */
257 *Response
= Message
->Response
;
263 *Response
= ResponseReturnToCaller
;
264 Status
= STATUS_SUCCESS
;
272 * @name ExRaiseAccessViolation
275 * The ExRaiseAccessViolation routine can be used with structured exception
276 * handling to throw a driver-determined exception for a memory access
277 * violation that occurs when a driver processes I/O requests.
278 * See: http://msdn.microsoft.com/library/en-us/Kernel_r/hh/Kernel_r/k102_71b4c053-599c-4a6d-8a59-08aae6bdc534.xml.asp?frame=true
279 * http://www.osronline.com/ddkx/kmarch/k102_814i.htm
288 ExRaiseAccessViolation(VOID
)
290 /* Raise the Right Status */
291 RtlRaiseStatus(STATUS_ACCESS_VIOLATION
);
295 * @name ExRaiseDatatypeMisalignment
298 * ExRaiseDatatypeMisalignment raises an exception with the exception
299 * code set to STATUS_DATATYPE_MISALIGNMENT
301 * http://www.osronline.com/ddkx/kmarch/k102_814i.htm
310 ExRaiseDatatypeMisalignment(VOID
)
312 /* Raise the Right Status */
313 RtlRaiseStatus(STATUS_DATATYPE_MISALIGNMENT
);
317 * @name ExSystemExceptionFilter
320 * TODO: Add description
329 ExSystemExceptionFilter(VOID
)
331 return KeGetPreviousMode() != KernelMode
?
332 EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
;
336 * @name ExRaiseHardError
339 * See NtRaiseHardError
344 * @param NumberOfParameters
345 * Number of optional parameters in Parameters array
347 * @param UnicodeStringParameterMask
348 * Optional string parameter (can be only one per error code)
351 * Array of ULONG parameters for use in error message string
353 * @param ValidResponseOptions
354 * See HARDERROR_RESPONSE_OPTION for possible values description
357 * Pointer to HARDERROR_RESPONSE enumeration
366 ExRaiseHardError(IN NTSTATUS ErrorStatus
,
367 IN ULONG NumberOfParameters
,
368 IN ULONG UnicodeStringParameterMask
,
369 IN PULONG_PTR Parameters
,
370 IN ULONG ValidResponseOptions
,
374 UNICODE_STRING CapturedParams
[MAXIMUM_HARDERROR_PARAMETERS
];
376 PULONG_PTR UserData
= NULL
, ParameterBase
;
377 PUNICODE_STRING StringBase
;
383 /* Check if we have parameters */
386 /* Check if we have strings */
387 if (UnicodeStringParameterMask
)
389 /* Add the maximum possible size */
390 Size
= (sizeof(ULONG_PTR
) + sizeof(UNICODE_STRING
)) *
391 MAXIMUM_HARDERROR_PARAMETERS
+ sizeof(UNICODE_STRING
);
393 /* Loop each parameter */
394 for (i
= 0; i
< NumberOfParameters
; i
++)
396 /* Check if it's part of the mask */
397 if (UnicodeStringParameterMask
& (1 << i
))
400 RtlMoveMemory(&CapturedParams
[i
],
402 sizeof(UNICODE_STRING
));
404 /* Increase the size */
405 Size
+= CapturedParams
[i
].MaximumLength
;
409 /* Allocate the user data region */
410 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
416 if (!NT_SUCCESS(Status
)) return Status
;
418 /* Set the pointers to our various data */
419 ParameterBase
= UserData
;
420 StringBase
= (PVOID
)((ULONG_PTR
)UserData
+
422 MAXIMUM_HARDERROR_PARAMETERS
);
423 BufferBase
= (PVOID
)((ULONG_PTR
)StringBase
+
424 sizeof(UNICODE_STRING
) *
425 MAXIMUM_HARDERROR_PARAMETERS
);
427 /* Loop parameters again */
428 for (i
= 0; i
< NumberOfParameters
; i
++)
430 /* Check if we're in the mask */
431 if (UnicodeStringParameterMask
& (1 << i
))
433 /* Update the base */
434 ParameterBase
[i
] = (ULONG_PTR
)&StringBase
[i
];
436 /* Copy the string buffer */
437 RtlMoveMemory(BufferBase
,
438 CapturedParams
[i
].Buffer
,
439 CapturedParams
[i
].MaximumLength
);
442 CapturedParams
[i
].Buffer
= BufferBase
;
444 /* Copy the string structure */
445 RtlMoveMemory(&StringBase
[i
],
447 sizeof(UNICODE_STRING
));
449 /* Update the pointer */
450 BufferBase
+= CapturedParams
[i
].MaximumLength
;
454 /* No need to copy any strings */
455 ParameterBase
[i
] = Parameters
[i
];
461 /* Just keep the data as is */
462 UserData
= Parameters
;
466 /* Now call the worker function */
467 Status
= ExpRaiseHardError(ErrorStatus
,
469 UnicodeStringParameterMask
,
471 ValidResponseOptions
,
474 /* Check if we had done user-mode allocation */
475 if ((UserData
) && (UserData
!= Parameters
))
477 /* We did! Delete it */
479 ZwFreeVirtualMemory(NtCurrentProcess(),
485 /* Return status and the response */
486 *Response
= SafeResponse
;
491 * @name NtRaiseHardError
494 * This function sends HARDERROR_MSG LPC message to listener
495 * (typically CSRSS.EXE). See NtSetDefaultHardErrorPort for more information
496 * See: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Error/NtRaiseHardError.html
501 * @param NumberOfParameters
502 * Number of optional parameters in Parameters array
504 * @param UnicodeStringParameterMask
505 * Optional string parameter (can be only one per error code)
508 * Array of ULONG parameters for use in error message string
510 * @param ValidResponseOptions
511 * See HARDERROR_RESPONSE_OPTION for possible values description
514 * Pointer to HARDERROR_RESPONSE enumeration
518 * @remarks NtRaiseHardError is easy way to display message in GUI
519 * without loading Win32 API libraries
524 NtRaiseHardError(IN NTSTATUS ErrorStatus
,
525 IN ULONG NumberOfParameters
,
526 IN ULONG UnicodeStringParameterMask
,
527 IN PULONG_PTR Parameters
,
528 IN ULONG ValidResponseOptions
,
531 NTSTATUS Status
= STATUS_SUCCESS
;
532 PULONG_PTR SafeParams
= NULL
;
534 UNICODE_STRING SafeString
;
537 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
539 /* Validate parameter count */
540 if (NumberOfParameters
> MAXIMUM_HARDERROR_PARAMETERS
)
543 return STATUS_INVALID_PARAMETER_2
;
546 /* Make sure we have some at least */
547 if ((Parameters
) && !(NumberOfParameters
))
550 return STATUS_INVALID_PARAMETER_2
;
553 /* Check if we were called from user-mode */
554 if (PreviousMode
!= KernelMode
)
556 /* First validate the responses */
557 switch (ValidResponseOptions
)
559 /* Check all valid cases */
560 case OptionAbortRetryIgnore
:
563 case OptionRetryCancel
:
565 case OptionYesNoCancel
:
566 case OptionShutdownSystem
:
569 /* Anything else is invalid */
571 return STATUS_INVALID_PARAMETER_4
;
574 /* Enter SEH Block */
577 /* Validate the response pointer */
578 ProbeForWriteUlong(Response
);
580 /* Check if we have parameters */
583 /* Validate the parameter pointers */
584 ParamSize
= sizeof(ULONG_PTR
) * NumberOfParameters
;
585 ProbeForRead(Parameters
, ParamSize
, sizeof(ULONG_PTR
));
587 /* Allocate a safe buffer */
588 SafeParams
= ExAllocatePoolWithTag(PagedPool
,
593 RtlCopyMemory(SafeParams
, Parameters
, ParamSize
);
595 /* Nowo check if there's strings in it */
596 if (UnicodeStringParameterMask
)
598 /* Loop every string */
599 for (i
= 0; i
< NumberOfParameters
; i
++)
601 /* Check if this parameter is a string */
602 if (UnicodeStringParameterMask
& (1 << i
))
604 /* Probe the structure */
605 ProbeForRead((PVOID
)SafeParams
[i
],
606 sizeof(UNICODE_STRING
),
610 RtlCopyMemory(&SafeString
,
611 (PVOID
)SafeParams
[i
],
612 sizeof(UNICODE_STRING
));
614 /* Probe the buffer */
615 ProbeForRead(SafeString
.Buffer
,
616 SafeString
.MaximumLength
,
623 _SEH2_EXCEPT(ExSystemExceptionFilter())
625 /* Free captured buffer */
626 if (SafeParams
) ExFreePool(SafeParams
);
627 Status
= _SEH2_GetExceptionCode();
631 /* If we failed to capture/probe, bail out */
632 if (!NT_SUCCESS(Status
)) return Status
;
634 /* Call the system function directly, because we probed */
635 ExpRaiseHardError(ErrorStatus
,
637 UnicodeStringParameterMask
,
639 ValidResponseOptions
,
645 SafeParams
= Parameters
;
648 * Call the Executive Function. It will probe and copy pointers to
651 ExRaiseHardError(ErrorStatus
,
653 UnicodeStringParameterMask
,
655 ValidResponseOptions
,
659 /* Check if we were called in user-mode */
660 if (PreviousMode
!= KernelMode
)
662 /* That means we have a buffer to free */
663 if (SafeParams
) ExFreePool(SafeParams
);
665 /* Enter SEH Block for return */
668 /* Return the response */
669 *Response
= SafeResponse
;
671 _SEH2_EXCEPT(ExSystemExceptionFilter())
673 Status
= _SEH2_GetExceptionCode();
679 /* Return the response */
680 *Response
= SafeResponse
;
688 * @name NtSetDefaultHardErrorPort
691 * NtSetDefaultHardErrorPort is typically called only once. After call,
692 * kernel set BOOLEAN flag named _ExReadyForErrors to TRUE, and all other
693 * tries to change default port are broken with STATUS_UNSUCCESSFUL error code
694 * See: http://www.windowsitlibrary.com/Content/356/08/2.html
695 * http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Error/NtSetDefaultHardErrorPort.html
698 * Handle to named port object
702 * @remarks Privileges: SE_TCB_PRIVILEGE
707 NtSetDefaultHardErrorPort(IN HANDLE PortHandle
)
709 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
710 NTSTATUS Status
= STATUS_UNSUCCESSFUL
;
712 /* Check if we have the Privilege */
713 if (!SeSinglePrivilegeCheck(SeTcbPrivilege
, PreviousMode
))
715 DPRINT1("NtSetDefaultHardErrorPort: Caller requires "
716 "the SeTcbPrivilege privilege!\n");
717 return STATUS_PRIVILEGE_NOT_HELD
;
720 /* Only called once during bootup, make sure we weren't called yet */
721 if (!ExReadyForErrors
)
723 /* Reference the port */
724 Status
= ObReferenceObjectByHandle(PortHandle
,
728 (PVOID
*)&ExpDefaultErrorPort
,
730 if (NT_SUCCESS(Status
))
733 ExpDefaultErrorPortProcess
= PsGetCurrentProcess();
734 ExReadyForErrors
= TRUE
;
738 /* Return status to caller */
746 /* Not supported in Kernel Mode */
747 RtlRaiseStatus(STATUS_NOT_IMPLEMENTED
);