[NTOSKRNL] Update Doxygen descriptions for NtRaiseHardError, ExRaiseHardError, ExpRai...
[reactos.git] / ntoskrnl / ex / harderr.c
1 /*
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)
7 */
8
9 /* INCLUDES *****************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 #define TAG_ERR ' rrE'
16
17 /* GLOBALS ****************************************************************/
18
19 BOOLEAN ExReadyForErrors = FALSE;
20 PVOID ExpDefaultErrorPort = NULL;
21 PEPROCESS ExpDefaultErrorPortProcess = NULL;
22
23 /* FUNCTIONS ****************************************************************/
24
25 /*++
26 * @name ExpSystemErrorHandler
27 *
28 * For now it's a stub
29 *
30 * @param ErrorStatus
31 * FILLME
32 *
33 * @param NumberOfParameters
34 * FILLME
35 *
36 * @param UnicodeStringParameterMask
37 * FILLME
38 *
39 * @param Parameters
40 * FILLME
41 *
42 * @param ValidResponseOptions
43 * FILLME
44 *
45 * @param Response
46 * FILLME
47 *
48 * @return None
49 *
50 * @remarks None
51 *
52 *--*/
53 NTSTATUS
54 NTAPI
55 ExpSystemErrorHandler(IN NTSTATUS ErrorStatus,
56 IN ULONG NumberOfParameters,
57 IN ULONG UnicodeStringParameterMask,
58 IN PULONG_PTR Parameters,
59 IN BOOLEAN Shutdown)
60 {
61 ULONG_PTR BugCheckParameters[MAXIMUM_HARDERROR_PARAMETERS] = {0, 0, 0, 0};
62 ULONG i;
63
64 /* Sanity check */
65 ASSERT(NumberOfParameters <= MAXIMUM_HARDERROR_PARAMETERS);
66
67 /*
68 * KeBugCheck expects MAXIMUM_HARDERROR_PARAMETERS parameters,
69 * but we might get called with less, so use a local buffer here.
70 */
71 for (i = 0; i < NumberOfParameters; i++)
72 {
73 /* Copy them over */
74 BugCheckParameters[i] = Parameters[i];
75 }
76
77 /* FIXME: STUB */
78 KeBugCheckEx(FATAL_UNHANDLED_HARD_ERROR,
79 ErrorStatus,
80 (ULONG_PTR)BugCheckParameters,
81 0,
82 0);
83 return STATUS_SUCCESS;
84 }
85
86 /*++
87 * @name ExpRaiseHardError
88 * @implemented
89 *
90 * See ExRaiseHardError and NtRaiseHardError, same parameters.
91 *
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.
97 *
98 *--*/
99 NTSTATUS
100 NTAPI
101 ExpRaiseHardError(IN NTSTATUS ErrorStatus,
102 IN ULONG NumberOfParameters,
103 IN ULONG UnicodeStringParameterMask,
104 IN PULONG_PTR Parameters,
105 IN ULONG ValidResponseOptions,
106 OUT PULONG Response)
107 {
108 PEPROCESS Process = PsGetCurrentProcess();
109 PETHREAD Thread = PsGetCurrentThread();
110 UCHAR Buffer[PORT_MAXIMUM_MESSAGE_LENGTH];
111 PHARDERROR_MSG Message = (PHARDERROR_MSG)Buffer;
112 NTSTATUS Status;
113 HANDLE PortHandle;
114 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
115 PAGED_CODE();
116
117 /* Check if this error will shutdown the system */
118 if (ValidResponseOptions == OptionShutdownSystem)
119 {
120 /*
121 * Check if we have the privileges.
122 *
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.
129 */
130 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode) ||
131 !SeSinglePrivilegeCheck(SeShutdownPrivilege, PreviousMode))
132 {
133 /* No rights */
134 *Response = ResponseNotHandled;
135 return STATUS_PRIVILEGE_NOT_HELD;
136 }
137
138 /* Don't handle any new hard errors */
139 ExReadyForErrors = FALSE;
140 }
141
142 /* Check if hard errors are not disabled */
143 if (!Thread->HardErrorsAreDisabled)
144 {
145 /* Check if we can't do errors anymore, and this is serious */
146 if (!ExReadyForErrors && NT_ERROR(ErrorStatus))
147 {
148 /* Use the system handler */
149 ExpSystemErrorHandler(ErrorStatus,
150 NumberOfParameters,
151 UnicodeStringParameterMask,
152 Parameters,
153 (PreviousMode != KernelMode) ? TRUE: FALSE);
154 }
155 }
156
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))
161 {
162 /* Check if we have an exception port */
163 if (Process->ExceptionPort)
164 {
165 /* Use the port */
166 PortHandle = Process->ExceptionPort;
167 }
168 else
169 {
170 /* Use our default system port */
171 PortHandle = ExpDefaultErrorPort;
172 }
173 }
174 else
175 {
176 /* Don't process the error */
177 PortHandle = NULL;
178 }
179
180 /* If hard errors are disabled, do nothing */
181 if (Thread->HardErrorsAreDisabled) PortHandle = NULL;
182
183 /* Now check if we have a port */
184 if (PortHandle == NULL)
185 {
186 /* Just return to caller */
187 *Response = ResponseReturnToCaller;
188 return STATUS_SUCCESS;
189 }
190
191 /* Check if this is the default process */
192 if (Process == ExpDefaultErrorPortProcess)
193 {
194 /* We can't handle the error, check if this is critical */
195 if (NT_ERROR(ErrorStatus))
196 {
197 /* It is, invoke the system handler */
198 ExpSystemErrorHandler(ErrorStatus,
199 NumberOfParameters,
200 UnicodeStringParameterMask,
201 Parameters,
202 (PreviousMode != KernelMode) ? TRUE: FALSE);
203
204 /* If we survived, return to caller */
205 *Response = ResponseReturnToCaller;
206 return STATUS_SUCCESS;
207 }
208 }
209
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);
220
221 /* Copy the parameters */
222 if (Parameters) RtlMoveMemory(&Message->Parameters,
223 Parameters,
224 sizeof(ULONG_PTR) * NumberOfParameters);
225
226 /* Send the LPC Message */
227 Status = LpcRequestWaitReplyPort(PortHandle,
228 (PPORT_MESSAGE)Message,
229 (PPORT_MESSAGE)Message);
230 if (NT_SUCCESS(Status))
231 {
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))
244 {
245 /* Reset to a default one */
246 Message->Response = ResponseReturnToCaller;
247 }
248
249 /* Set the response */
250 *Response = Message->Response;
251 }
252 else
253 {
254 /* Set the response */
255 *Response = ResponseReturnToCaller;
256 }
257
258 /* Return status */
259 return Status;
260 }
261
262 /*++
263 * @name ExRaiseAccessViolation
264 * @implemented
265 *
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
271 *
272 * @return None
273 *
274 * @remarks None
275 *
276 *--*/
277 VOID
278 NTAPI
279 ExRaiseAccessViolation(VOID)
280 {
281 /* Raise the Right Status */
282 RtlRaiseStatus(STATUS_ACCESS_VIOLATION);
283 }
284
285 /*++
286 * @name ExRaiseDatatypeMisalignment
287 * @implemented
288 *
289 * ExRaiseDatatypeMisalignment raises an exception with the exception
290 * code set to STATUS_DATATYPE_MISALIGNMENT
291 * See: MSDN / DDK
292 * http://www.osronline.com/ddkx/kmarch/k102_814i.htm
293 *
294 * @return None
295 *
296 * @remarks None
297 *
298 *--*/
299 VOID
300 NTAPI
301 ExRaiseDatatypeMisalignment(VOID)
302 {
303 /* Raise the Right Status */
304 RtlRaiseStatus(STATUS_DATATYPE_MISALIGNMENT);
305 }
306
307 /*++
308 * @name ExSystemExceptionFilter
309 * @implemented
310 *
311 * TODO: Add description
312 *
313 * @return FILLME
314 *
315 * @remarks None
316 *
317 *--*/
318 LONG
319 NTAPI
320 ExSystemExceptionFilter(VOID)
321 {
322 return KeGetPreviousMode() != KernelMode ?
323 EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH;
324 }
325
326 /*++
327 * @name ExRaiseHardError
328 * @implemented
329 *
330 * See NtRaiseHardError and ExpRaiseHardError.
331 *
332 * @param ErrorStatus
333 * Error Code
334 *
335 * @param NumberOfParameters
336 * Number of optional parameters in Parameters array
337 *
338 * @param UnicodeStringParameterMask
339 * Optional string parameter (can be only one per error code)
340 *
341 * @param Parameters
342 * Array of ULONG parameters for use in error message string
343 *
344 * @param ValidResponseOptions
345 * See HARDERROR_RESPONSE_OPTION for possible values description
346 *
347 * @param Response
348 * Pointer to HARDERROR_RESPONSE enumeration
349 *
350 * @return Status
351 *
352 *--*/
353 NTSTATUS
354 NTAPI
355 ExRaiseHardError(IN NTSTATUS ErrorStatus,
356 IN ULONG NumberOfParameters,
357 IN ULONG UnicodeStringParameterMask,
358 IN PULONG_PTR Parameters,
359 IN ULONG ValidResponseOptions,
360 OUT PULONG Response)
361 {
362 SIZE_T Size;
363 UNICODE_STRING CapturedParams[MAXIMUM_HARDERROR_PARAMETERS];
364 ULONG i;
365 PVOID UserData = NULL;
366 PHARDERROR_USER_PARAMETERS UserParams;
367 PWSTR BufferBase;
368 ULONG SafeResponse;
369 NTSTATUS Status;
370 PAGED_CODE();
371
372 /* Check if we have parameters */
373 if (Parameters)
374 {
375 /* Check if we have strings */
376 if (UnicodeStringParameterMask)
377 {
378 /* Calculate the required size */
379 Size = FIELD_OFFSET(HARDERROR_USER_PARAMETERS, Buffer[0]);
380
381 /* Loop each parameter */
382 for (i = 0; i < NumberOfParameters; i++)
383 {
384 /* Check if it's part of the mask */
385 if (UnicodeStringParameterMask & (1 << i))
386 {
387 /* Copy it */
388 RtlMoveMemory(&CapturedParams[i],
389 (PVOID)Parameters[i],
390 sizeof(UNICODE_STRING));
391
392 /* Increase the size */
393 Size += CapturedParams[i].MaximumLength;
394 }
395 }
396
397 /* Allocate the user data region */
398 Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
399 &UserData,
400 0,
401 &Size,
402 MEM_COMMIT,
403 PAGE_READWRITE);
404 if (!NT_SUCCESS(Status)) return Status;
405
406 /* Set the pointers to our data */
407 UserParams = UserData;
408 BufferBase = UserParams->Buffer;
409
410 /* Loop parameters again */
411 for (i = 0; i < NumberOfParameters; i++)
412 {
413 /* Check if we're in the mask */
414 if (UnicodeStringParameterMask & (1 << i))
415 {
416 /* Update the base */
417 UserParams->Parameters[i] = (ULONG_PTR)&UserParams->Strings[i];
418
419 /* Copy the string buffer */
420 RtlMoveMemory(BufferBase,
421 CapturedParams[i].Buffer,
422 CapturedParams[i].MaximumLength);
423
424 /* Set buffer */
425 CapturedParams[i].Buffer = BufferBase;
426
427 /* Copy the string structure */
428 UserParams->Strings[i] = CapturedParams[i];
429
430 /* Update the pointer */
431 BufferBase += CapturedParams[i].MaximumLength;
432 }
433 else
434 {
435 /* No need to copy any strings */
436 UserParams->Parameters[i] = Parameters[i];
437 }
438 }
439 }
440 else
441 {
442 /* Just keep the data as is */
443 UserData = Parameters;
444 }
445 }
446
447 /* Now call the worker function */
448 Status = ExpRaiseHardError(ErrorStatus,
449 NumberOfParameters,
450 UnicodeStringParameterMask,
451 UserData,
452 ValidResponseOptions,
453 &SafeResponse);
454
455 /* Check if we had done user-mode allocation */
456 if ((UserData) && (UserData != Parameters))
457 {
458 /* We did! Delete it */
459 Size = 0;
460 ZwFreeVirtualMemory(NtCurrentProcess(),
461 &UserData,
462 &Size,
463 MEM_RELEASE);
464 }
465
466 /* Return status and the response */
467 *Response = SafeResponse;
468 return Status;
469 }
470
471 /*++
472 * @name NtRaiseHardError
473 * @implemented
474 *
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
478 *
479 * @param ErrorStatus
480 * Error Code
481 *
482 * @param NumberOfParameters
483 * Number of optional parameters in Parameters array
484 *
485 * @param UnicodeStringParameterMask
486 * Optional string parameter (can be only one per error code)
487 *
488 * @param Parameters
489 * Array of ULONG_PTR parameters for use in error message string
490 *
491 * @param ValidResponseOptions
492 * See HARDERROR_RESPONSE_OPTION for possible values description
493 *
494 * @param Response
495 * Pointer to HARDERROR_RESPONSE enumeration
496 *
497 * @return Status
498 *
499 * @remarks NtRaiseHardError constitutes an easy way to display messages
500 * in GUI without loading any Win32 API libraries.
501 *
502 *--*/
503 NTSTATUS
504 NTAPI
505 NtRaiseHardError(IN NTSTATUS ErrorStatus,
506 IN ULONG NumberOfParameters,
507 IN ULONG UnicodeStringParameterMask,
508 IN PULONG_PTR Parameters,
509 IN ULONG ValidResponseOptions,
510 OUT PULONG Response)
511 {
512 NTSTATUS Status = STATUS_SUCCESS;
513 PULONG_PTR SafeParams = NULL;
514 ULONG SafeResponse;
515 UNICODE_STRING SafeString;
516 ULONG i;
517 ULONG ParamSize = 0;
518 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
519
520 /* Validate parameter count */
521 if (NumberOfParameters > MAXIMUM_HARDERROR_PARAMETERS)
522 {
523 /* Fail */
524 return STATUS_INVALID_PARAMETER_2;
525 }
526
527 /* Make sure we have some at least */
528 if ((Parameters != NULL) && (NumberOfParameters == 0))
529 {
530 /* Fail */
531 return STATUS_INVALID_PARAMETER_2;
532 }
533
534 /* Check if we were called from user-mode */
535 if (PreviousMode != KernelMode)
536 {
537 /* First validate the responses */
538 switch (ValidResponseOptions)
539 {
540 /* Check all valid cases */
541 case OptionAbortRetryIgnore:
542 case OptionOk:
543 case OptionOkCancel:
544 case OptionRetryCancel:
545 case OptionYesNo:
546 case OptionYesNoCancel:
547 case OptionShutdownSystem:
548 case OptionOkNoWait:
549 case OptionCancelTryContinue:
550 break;
551
552 /* Anything else is invalid */
553 default:
554 return STATUS_INVALID_PARAMETER_4;
555 }
556
557 /* Check if we have parameters */
558 if (Parameters)
559 {
560 /* Calculate size of the parameters */
561 ParamSize = sizeof(ULONG_PTR) * NumberOfParameters;
562
563 /* Allocate a safe buffer */
564 SafeParams = ExAllocatePoolWithTag(PagedPool, ParamSize, TAG_ERR);
565 if (!SafeParams)
566 {
567 return STATUS_INSUFFICIENT_RESOURCES;
568 }
569 }
570
571 /* Enter SEH Block */
572 _SEH2_TRY
573 {
574 /* Validate the response pointer */
575 ProbeForWriteUlong(Response);
576
577 /* Check if we have parameters */
578 if (Parameters)
579 {
580 /* Validate the parameter pointers */
581 ProbeForRead(Parameters, ParamSize, sizeof(ULONG_PTR));
582
583 /* Copy them */
584 RtlCopyMemory(SafeParams, Parameters, ParamSize);
585
586 /* Now check if there's strings in it */
587 if (UnicodeStringParameterMask)
588 {
589 /* Loop every string */
590 for (i = 0; i < NumberOfParameters; i++)
591 {
592 /* Check if this parameter is a string */
593 if (UnicodeStringParameterMask & (1 << i))
594 {
595 /* Probe the structure */
596 ProbeForRead((PVOID)SafeParams[i],
597 sizeof(UNICODE_STRING),
598 sizeof(ULONG_PTR));
599
600 /* Capture it */
601 RtlCopyMemory(&SafeString,
602 (PVOID)SafeParams[i],
603 sizeof(UNICODE_STRING));
604
605 /* Probe the buffer */
606 ProbeForRead(SafeString.Buffer,
607 SafeString.MaximumLength,
608 sizeof(UCHAR));
609 }
610 }
611 }
612 }
613 }
614 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
615 {
616 /* Free captured buffer */
617 if (SafeParams) ExFreePoolWithTag(SafeParams, TAG_ERR);
618
619 /* Return the exception code */
620 _SEH2_YIELD(return _SEH2_GetExceptionCode());
621 }
622 _SEH2_END;
623
624 /* Call the system function directly, because we probed */
625 ExpRaiseHardError(ErrorStatus,
626 NumberOfParameters,
627 UnicodeStringParameterMask,
628 SafeParams,
629 ValidResponseOptions,
630 &SafeResponse);
631 }
632 else
633 {
634 /* Reuse variable */
635 SafeParams = Parameters;
636
637 /*
638 * Call the Executive Function. It will probe and copy pointers to
639 * user-mode
640 */
641 ExRaiseHardError(ErrorStatus,
642 NumberOfParameters,
643 UnicodeStringParameterMask,
644 SafeParams,
645 ValidResponseOptions,
646 &SafeResponse);
647 }
648
649 /* Check if we were called in user-mode */
650 if (PreviousMode != KernelMode)
651 {
652 /* That means we have a buffer to free */
653 if (SafeParams) ExFreePoolWithTag(SafeParams, TAG_ERR);
654
655 /* Enter SEH Block for return */
656 _SEH2_TRY
657 {
658 /* Return the response */
659 *Response = SafeResponse;
660 }
661 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
662 {
663 /* Get the exception code */
664 Status = _SEH2_GetExceptionCode();
665 }
666 _SEH2_END;
667 }
668 else
669 {
670 /* Return the response */
671 *Response = SafeResponse;
672 }
673
674 /* Return status */
675 return Status;
676 }
677
678 /*++
679 * @name NtSetDefaultHardErrorPort
680 * @implemented
681 *
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
687 *
688 * @param PortHandle
689 * Handle to named port object
690 *
691 * @return Status
692 *
693 * @remarks Privileges: SE_TCB_PRIVILEGE
694 *
695 *--*/
696 NTSTATUS
697 NTAPI
698 NtSetDefaultHardErrorPort(IN HANDLE PortHandle)
699 {
700 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
701 NTSTATUS Status = STATUS_UNSUCCESSFUL;
702
703 /* Check if we have the privileges */
704 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
705 {
706 DPRINT1("NtSetDefaultHardErrorPort: Caller requires "
707 "the SeTcbPrivilege privilege!\n");
708 return STATUS_PRIVILEGE_NOT_HELD;
709 }
710
711 /* Only called once during bootup, make sure we weren't called yet */
712 if (!ExReadyForErrors)
713 {
714 /* Reference the hard-error port */
715 Status = ObReferenceObjectByHandle(PortHandle,
716 0,
717 LpcPortObjectType,
718 PreviousMode,
719 (PVOID*)&ExpDefaultErrorPort,
720 NULL);
721 if (NT_SUCCESS(Status))
722 {
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;
728 }
729 }
730
731 /* Return status to caller */
732 return Status;
733 }
734
735 VOID
736 __cdecl
737 _purecall(VOID)
738 {
739 /* Not supported in Kernel Mode */
740 RtlRaiseStatus(STATUS_NOT_IMPLEMENTED);
741 }
742
743 /* EOF */