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 return STATUS_PRIVILEGE_NOT_HELD
;
142 /* Don't handle any new hard errors */
143 ExReadyForErrors
= FALSE
;
146 /* Check if hard errors are not disabled */
147 if (!Thread
->HardErrorsAreDisabled
)
149 /* Check if we can't do errors anymore, and this is serious */
150 if ((!ExReadyForErrors
) && (NT_ERROR(ErrorStatus
)))
152 /* Use the system handler */
153 ExpSystemErrorHandler(ErrorStatus
,
155 UnicodeStringParameterMask
,
157 (PreviousMode
!= KernelMode
) ? TRUE
: FALSE
);
161 /* Enable hard error processing if it is enabled for the process
162 * or if the exception status forces it */
163 if ((Process
->DefaultHardErrorProcessing
& 1) ||
164 (ErrorStatus
& 0x10000000))
166 /* Check if we have an exception port */
167 if (Process
->ExceptionPort
)
170 PortHandle
= Process
->ExceptionPort
;
174 /* Use our default system port */
175 PortHandle
= ExpDefaultErrorPort
;
180 /* Don't process the error */
184 /* If hard errors are disabled, do nothing */
185 if (Thread
->HardErrorsAreDisabled
) PortHandle
= NULL
;
187 /* Now check if we have a port */
190 /* Check if this is the default process */
191 if (Process
== ExpDefaultErrorPortProcess
)
193 /* We can't handle the error, check if this is critical */
194 if (NT_ERROR(ErrorStatus
))
196 /* It is, invoke the system handler */
197 ExpSystemErrorHandler(ErrorStatus
,
199 UnicodeStringParameterMask
,
201 (PreviousMode
!= KernelMode
) ? TRUE
: FALSE
);
203 /* If we survived, return to caller */
204 *Response
= ResponseReturnToCaller
;
205 return STATUS_SUCCESS
;
209 /* Setup the LPC Message */
210 Message
->h
.u1
.Length
= (sizeof(HARDERROR_MSG
) << 16) |
211 (sizeof(HARDERROR_MSG
) - sizeof(PORT_MESSAGE
));
212 Message
->h
.u2
.ZeroInit
= 0;
213 Message
->h
.u2
.s2
.Type
= LPC_ERROR_EVENT
;
214 Message
->Status
= ErrorStatus
&~ 0x10000000;
215 Message
->ValidResponseOptions
= ValidResponseOptions
;
216 Message
->UnicodeStringParameterMask
= UnicodeStringParameterMask
;
217 Message
->NumberOfParameters
= NumberOfParameters
;
218 KeQuerySystemTime(&Message
->ErrorTime
);
220 /* Copy the parameters */
221 if (Parameters
) RtlMoveMemory(&Message
->Parameters
,
223 sizeof(ULONG_PTR
) * NumberOfParameters
);
225 /* Send the LPC Message */
226 Status
= LpcRequestWaitReplyPort(PortHandle
,
229 if (NT_SUCCESS(Status
))
231 /* Check what kind of response we got */
232 if ((Message
->Response
!= ResponseReturnToCaller
) &&
233 (Message
->Response
!= ResponseNotHandled
) &&
234 (Message
->Response
!= ResponseAbort
) &&
235 (Message
->Response
!= ResponseCancel
) &&
236 (Message
->Response
!= ResponseIgnore
) &&
237 (Message
->Response
!= ResponseNo
) &&
238 (Message
->Response
!= ResponseOk
) &&
239 (Message
->Response
!= ResponseRetry
) &&
240 (Message
->Response
!= ResponseYes
) &&
241 (Message
->Response
!= ResponseTryAgain
) &&
242 (Message
->Response
!= ResponseContinue
))
244 /* Reset to a default one */
245 Message
->Response
= ResponseReturnToCaller
;
248 /* Set the response */
249 *Response
= Message
->Response
;
255 *Response
= ResponseReturnToCaller
;
256 Status
= STATUS_SUCCESS
;
264 * @name ExRaiseAccessViolation
267 * The ExRaiseAccessViolation routine can be used with structured exception
268 * handling to throw a driver-determined exception for a memory access
269 * violation that occurs when a driver processes I/O requests.
270 * See: http://msdn.microsoft.com/library/en-us/Kernel_r/hh/Kernel_r/k102_71b4c053-599c-4a6d-8a59-08aae6bdc534.xml.asp?frame=true
271 * http://www.osronline.com/ddkx/kmarch/k102_814i.htm
280 ExRaiseAccessViolation(VOID
)
282 /* Raise the Right Status */
283 RtlRaiseStatus(STATUS_ACCESS_VIOLATION
);
287 * @name ExRaiseDatatypeMisalignment
290 * ExRaiseDatatypeMisalignment raises an exception with the exception
291 * code set to STATUS_DATATYPE_MISALIGNMENT
293 * http://www.osronline.com/ddkx/kmarch/k102_814i.htm
302 ExRaiseDatatypeMisalignment(VOID
)
304 /* Raise the Right Status */
305 RtlRaiseStatus(STATUS_DATATYPE_MISALIGNMENT
);
309 * @name ExSystemExceptionFilter
312 * TODO: Add description
321 ExSystemExceptionFilter(VOID
)
323 return KeGetPreviousMode() != KernelMode
?
324 EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
;
328 * @name ExRaiseHardError
331 * See NtRaiseHardError
336 * @param NumberOfParameters
337 * Number of optional parameters in Parameters array
339 * @param UnicodeStringParameterMask
340 * Optional string parameter (can be only one per error code)
343 * Array of ULONG parameters for use in error message string
345 * @param ValidResponseOptions
346 * See HARDERROR_RESPONSE_OPTION for possible values description
349 * Pointer to HARDERROR_RESPONSE enumeration
358 ExRaiseHardError(IN NTSTATUS ErrorStatus
,
359 IN ULONG NumberOfParameters
,
360 IN ULONG UnicodeStringParameterMask
,
361 IN PULONG_PTR Parameters
,
362 IN ULONG ValidResponseOptions
,
366 UNICODE_STRING CapturedParams
[MAXIMUM_HARDERROR_PARAMETERS
];
368 PVOID UserData
= NULL
;
369 PHARDERROR_USER_PARAMETERS UserParams
;
375 /* Check if we have parameters */
378 /* Check if we have strings */
379 if (UnicodeStringParameterMask
)
381 /* Calculate the required size */
382 Size
= FIELD_OFFSET(HARDERROR_USER_PARAMETERS
, Buffer
[0]);
384 /* Loop each parameter */
385 for (i
= 0; i
< NumberOfParameters
; i
++)
387 /* Check if it's part of the mask */
388 if (UnicodeStringParameterMask
& (1 << i
))
391 RtlMoveMemory(&CapturedParams
[i
],
392 (PVOID
)Parameters
[i
],
393 sizeof(UNICODE_STRING
));
395 /* Increase the size */
396 Size
+= CapturedParams
[i
].MaximumLength
;
400 /* Allocate the user data region */
401 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
407 if (!NT_SUCCESS(Status
)) return Status
;
409 /* Set the pointers to our data */
410 UserParams
= UserData
;
411 BufferBase
= UserParams
->Buffer
;
413 /* Loop parameters again */
414 for (i
= 0; i
< NumberOfParameters
; i
++)
416 /* Check if we're in the mask */
417 if (UnicodeStringParameterMask
& (1 << i
))
419 /* Update the base */
420 UserParams
->Parameters
[i
] = (ULONG_PTR
)&UserParams
->Strings
[i
];
422 /* Copy the string buffer */
423 RtlMoveMemory(BufferBase
,
424 CapturedParams
[i
].Buffer
,
425 CapturedParams
[i
].MaximumLength
);
428 CapturedParams
[i
].Buffer
= BufferBase
;
430 /* Copy the string structure */
431 UserParams
->Strings
[i
] = CapturedParams
[i
];
433 /* Update the pointer */
434 BufferBase
+= CapturedParams
[i
].MaximumLength
;
438 /* No need to copy any strings */
439 UserParams
->Parameters
[i
] = Parameters
[i
];
445 /* Just keep the data as is */
446 UserData
= Parameters
;
450 /* Now call the worker function */
451 Status
= ExpRaiseHardError(ErrorStatus
,
453 UnicodeStringParameterMask
,
455 ValidResponseOptions
,
458 /* Check if we had done user-mode allocation */
459 if ((UserData
) && (UserData
!= Parameters
))
461 /* We did! Delete it */
463 ZwFreeVirtualMemory(NtCurrentProcess(),
469 /* Return status and the response */
470 *Response
= SafeResponse
;
475 * @name NtRaiseHardError
478 * This function sends HARDERROR_MSG LPC message to listener
479 * (typically CSRSS.EXE). See NtSetDefaultHardErrorPort for more information
480 * See: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Error/NtRaiseHardError.html
485 * @param NumberOfParameters
486 * Number of optional parameters in Parameters array
488 * @param UnicodeStringParameterMask
489 * Optional string parameter (can be only one per error code)
492 * Array of ULONG_PTR parameters for use in error message string
494 * @param ValidResponseOptions
495 * See HARDERROR_RESPONSE_OPTION for possible values description
498 * Pointer to HARDERROR_RESPONSE enumeration
502 * @remarks NtRaiseHardError is easy way to display message in GUI
503 * without loading Win32 API libraries
508 NtRaiseHardError(IN NTSTATUS ErrorStatus
,
509 IN ULONG NumberOfParameters
,
510 IN ULONG UnicodeStringParameterMask
,
511 IN PULONG_PTR Parameters
,
512 IN ULONG ValidResponseOptions
,
515 NTSTATUS Status
= STATUS_SUCCESS
;
516 PULONG_PTR SafeParams
= NULL
;
518 UNICODE_STRING SafeString
;
521 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
523 /* Validate parameter count */
524 if (NumberOfParameters
> MAXIMUM_HARDERROR_PARAMETERS
)
527 return STATUS_INVALID_PARAMETER_2
;
530 /* Make sure we have some at least */
531 if ((Parameters
) && !(NumberOfParameters
))
534 return STATUS_INVALID_PARAMETER_2
;
537 /* Check if we were called from user-mode */
538 if (PreviousMode
!= KernelMode
)
540 /* First validate the responses */
541 switch (ValidResponseOptions
)
543 /* Check all valid cases */
544 case OptionAbortRetryIgnore
:
547 case OptionRetryCancel
:
549 case OptionYesNoCancel
:
550 case OptionShutdownSystem
:
553 /* Anything else is invalid */
555 return STATUS_INVALID_PARAMETER_4
;
558 /* Enter SEH Block */
561 /* Validate the response pointer */
562 ProbeForWriteUlong(Response
);
564 /* Check if we have parameters */
567 /* Validate the parameter pointers */
568 ParamSize
= sizeof(ULONG_PTR
) * NumberOfParameters
;
569 ProbeForRead(Parameters
, ParamSize
, sizeof(ULONG_PTR
));
571 /* Allocate a safe buffer */
572 SafeParams
= ExAllocatePoolWithTag(PagedPool
,
577 RtlCopyMemory(SafeParams
, Parameters
, ParamSize
);
579 /* Now check if there's strings in it */
580 if (UnicodeStringParameterMask
)
582 /* Loop every string */
583 for (i
= 0; i
< NumberOfParameters
; i
++)
585 /* Check if this parameter is a string */
586 if (UnicodeStringParameterMask
& (1 << i
))
588 /* Probe the structure */
589 ProbeForRead((PVOID
)SafeParams
[i
],
590 sizeof(UNICODE_STRING
),
594 RtlCopyMemory(&SafeString
,
595 (PVOID
)SafeParams
[i
],
596 sizeof(UNICODE_STRING
));
598 /* Probe the buffer */
599 ProbeForRead(SafeString
.Buffer
,
600 SafeString
.MaximumLength
,
607 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
609 /* Free captured buffer */
610 if (SafeParams
) ExFreePoolWithTag(SafeParams
, TAG_ERR
);
612 /* Return the exception code */
613 _SEH2_YIELD(return _SEH2_GetExceptionCode());
617 /* Call the system function directly, because we probed */
618 ExpRaiseHardError(ErrorStatus
,
620 UnicodeStringParameterMask
,
622 ValidResponseOptions
,
628 SafeParams
= Parameters
;
631 * Call the Executive Function. It will probe and copy pointers to
634 ExRaiseHardError(ErrorStatus
,
636 UnicodeStringParameterMask
,
638 ValidResponseOptions
,
642 /* Check if we were called in user-mode */
643 if (PreviousMode
!= KernelMode
)
645 /* That means we have a buffer to free */
646 if (SafeParams
) ExFreePoolWithTag(SafeParams
, TAG_ERR
);
648 /* Enter SEH Block for return */
651 /* Return the response */
652 *Response
= SafeResponse
;
654 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
656 /* Get the exception code */
657 Status
= _SEH2_GetExceptionCode();
663 /* Return the response */
664 *Response
= SafeResponse
;
672 * @name NtSetDefaultHardErrorPort
675 * NtSetDefaultHardErrorPort is typically called only once. After call,
676 * kernel set BOOLEAN flag named _ExReadyForErrors to TRUE, and all other
677 * tries to change default port are broken with STATUS_UNSUCCESSFUL error code
678 * See: http://www.windowsitlibrary.com/Content/356/08/2.html
679 * http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Error/NtSetDefaultHardErrorPort.html
682 * Handle to named port object
686 * @remarks Privileges: SE_TCB_PRIVILEGE
691 NtSetDefaultHardErrorPort(IN HANDLE PortHandle
)
693 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
694 NTSTATUS Status
= STATUS_UNSUCCESSFUL
;
696 /* Check if we have the Privilege */
697 if (!SeSinglePrivilegeCheck(SeTcbPrivilege
, PreviousMode
))
699 DPRINT1("NtSetDefaultHardErrorPort: Caller requires "
700 "the SeTcbPrivilege privilege!\n");
701 return STATUS_PRIVILEGE_NOT_HELD
;
704 /* Only called once during bootup, make sure we weren't called yet */
705 if (!ExReadyForErrors
)
707 /* Reference the port */
708 Status
= ObReferenceObjectByHandle(PortHandle
,
712 (PVOID
*)&ExpDefaultErrorPort
,
714 if (NT_SUCCESS(Status
))
717 ExpDefaultErrorPortProcess
= PsGetCurrentProcess();
718 ExReadyForErrors
= TRUE
;
722 /* Return status to caller */
730 /* Not supported in Kernel Mode */
731 RtlRaiseStatus(STATUS_NOT_IMPLEMENTED
);