82d22f49263980ed539006e523848f83af1c426b
[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 /*
158 * Enable hard error processing if it is enabled for the process
159 * or if the exception status forces it.
160 */
161 if ((Process->DefaultHardErrorProcessing & SEM_FAILCRITICALERRORS) ||
162 (ErrorStatus & HARDERROR_OVERRIDE_ERRORMODE))
163 {
164 /* Check if we have an exception port */
165 if (Process->ExceptionPort)
166 {
167 /* Use the port */
168 PortHandle = Process->ExceptionPort;
169 }
170 else
171 {
172 /* Use our default system port */
173 PortHandle = ExpDefaultErrorPort;
174 }
175 }
176 else
177 {
178 /* Don't process the error */
179 PortHandle = NULL;
180 }
181
182 /* If hard errors are disabled, do nothing */
183 if (Thread->HardErrorsAreDisabled) PortHandle = NULL;
184
185 /* Now check if we have a port */
186 if (PortHandle == NULL)
187 {
188 /* Just return to caller */
189 *Response = ResponseReturnToCaller;
190 return STATUS_SUCCESS;
191 }
192
193 /* Check if this is the default process */
194 if (Process == ExpDefaultErrorPortProcess)
195 {
196 /* We can't handle the error, check if this is critical */
197 if (NT_ERROR(ErrorStatus))
198 {
199 /* It is, invoke the system handler */
200 ExpSystemErrorHandler(ErrorStatus,
201 NumberOfParameters,
202 UnicodeStringParameterMask,
203 Parameters,
204 (PreviousMode != KernelMode) ? TRUE : FALSE);
205
206 /* If we survived, return to caller */
207 *Response = ResponseReturnToCaller;
208 return STATUS_SUCCESS;
209 }
210 }
211
212 /* Setup the LPC Message */
213 Message->h.u1.Length = (sizeof(HARDERROR_MSG) << 16) |
214 (sizeof(HARDERROR_MSG) - sizeof(PORT_MESSAGE));
215 Message->h.u2.ZeroInit = 0;
216 Message->h.u2.s2.Type = LPC_ERROR_EVENT;
217 Message->Status = ErrorStatus & ~HARDERROR_OVERRIDE_ERRORMODE;
218 Message->ValidResponseOptions = ValidResponseOptions;
219 Message->UnicodeStringParameterMask = UnicodeStringParameterMask;
220 Message->NumberOfParameters = NumberOfParameters;
221 KeQuerySystemTime(&Message->ErrorTime);
222
223 /* Copy the parameters */
224 if (Parameters) RtlMoveMemory(&Message->Parameters,
225 Parameters,
226 sizeof(ULONG_PTR) * NumberOfParameters);
227
228 /* Send the LPC Message */
229 Status = LpcRequestWaitReplyPort(PortHandle,
230 (PPORT_MESSAGE)Message,
231 (PPORT_MESSAGE)Message);
232 if (NT_SUCCESS(Status))
233 {
234 /* Check what kind of response we got */
235 if ((Message->Response != ResponseReturnToCaller) &&
236 (Message->Response != ResponseNotHandled) &&
237 (Message->Response != ResponseAbort) &&
238 (Message->Response != ResponseCancel) &&
239 (Message->Response != ResponseIgnore) &&
240 (Message->Response != ResponseNo) &&
241 (Message->Response != ResponseOk) &&
242 (Message->Response != ResponseRetry) &&
243 (Message->Response != ResponseYes) &&
244 (Message->Response != ResponseTryAgain) &&
245 (Message->Response != ResponseContinue))
246 {
247 /* Reset to a default one */
248 Message->Response = ResponseReturnToCaller;
249 }
250
251 /* Set the response */
252 *Response = Message->Response;
253 }
254 else
255 {
256 /* Set the response */
257 *Response = ResponseReturnToCaller;
258 }
259
260 /* Return status */
261 return Status;
262 }
263
264 /*++
265 * @name ExRaiseAccessViolation
266 * @implemented
267 *
268 * The ExRaiseAccessViolation routine can be used with structured exception
269 * handling to throw a driver-determined exception for a memory access
270 * violation that occurs when a driver processes I/O requests.
271 * See: http://msdn.microsoft.com/library/en-us/Kernel_r/hh/Kernel_r/k102_71b4c053-599c-4a6d-8a59-08aae6bdc534.xml.asp?frame=true
272 * http://www.osronline.com/ddkx/kmarch/k102_814i.htm
273 *
274 * @return None
275 *
276 * @remarks None
277 *
278 *--*/
279 VOID
280 NTAPI
281 ExRaiseAccessViolation(VOID)
282 {
283 /* Raise the Right Status */
284 RtlRaiseStatus(STATUS_ACCESS_VIOLATION);
285 }
286
287 /*++
288 * @name ExRaiseDatatypeMisalignment
289 * @implemented
290 *
291 * ExRaiseDatatypeMisalignment raises an exception with the exception
292 * code set to STATUS_DATATYPE_MISALIGNMENT
293 * See: MSDN / DDK
294 * http://www.osronline.com/ddkx/kmarch/k102_814i.htm
295 *
296 * @return None
297 *
298 * @remarks None
299 *
300 *--*/
301 VOID
302 NTAPI
303 ExRaiseDatatypeMisalignment(VOID)
304 {
305 /* Raise the Right Status */
306 RtlRaiseStatus(STATUS_DATATYPE_MISALIGNMENT);
307 }
308
309 /*++
310 * @name ExSystemExceptionFilter
311 * @implemented
312 *
313 * TODO: Add description
314 *
315 * @return FILLME
316 *
317 * @remarks None
318 *
319 *--*/
320 LONG
321 NTAPI
322 ExSystemExceptionFilter(VOID)
323 {
324 return KeGetPreviousMode() != KernelMode ?
325 EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH;
326 }
327
328 /*++
329 * @name ExRaiseHardError
330 * @implemented
331 *
332 * See NtRaiseHardError and ExpRaiseHardError.
333 *
334 * @param ErrorStatus
335 * Error Code
336 *
337 * @param NumberOfParameters
338 * Number of optional parameters in Parameters array
339 *
340 * @param UnicodeStringParameterMask
341 * Optional string parameter (can be only one per error code)
342 *
343 * @param Parameters
344 * Array of ULONG parameters for use in error message string
345 *
346 * @param ValidResponseOptions
347 * See HARDERROR_RESPONSE_OPTION for possible values description
348 *
349 * @param Response
350 * Pointer to HARDERROR_RESPONSE enumeration
351 *
352 * @return Status
353 *
354 *--*/
355 NTSTATUS
356 NTAPI
357 ExRaiseHardError(IN NTSTATUS ErrorStatus,
358 IN ULONG NumberOfParameters,
359 IN ULONG UnicodeStringParameterMask,
360 IN PULONG_PTR Parameters,
361 IN ULONG ValidResponseOptions,
362 OUT PULONG Response)
363 {
364 SIZE_T Size;
365 UNICODE_STRING CapturedParams[MAXIMUM_HARDERROR_PARAMETERS];
366 ULONG i;
367 PVOID UserData = NULL;
368 PHARDERROR_USER_PARAMETERS UserParams;
369 PWSTR BufferBase;
370 ULONG SafeResponse;
371 NTSTATUS Status;
372 PAGED_CODE();
373
374 /* Check if we have parameters */
375 if (Parameters)
376 {
377 /* Check if we have strings */
378 if (UnicodeStringParameterMask)
379 {
380 /* Calculate the required size */
381 Size = FIELD_OFFSET(HARDERROR_USER_PARAMETERS, Buffer[0]);
382
383 /* Loop each parameter */
384 for (i = 0; i < NumberOfParameters; i++)
385 {
386 /* Check if it's part of the mask */
387 if (UnicodeStringParameterMask & (1 << i))
388 {
389 /* Copy it */
390 RtlMoveMemory(&CapturedParams[i],
391 (PVOID)Parameters[i],
392 sizeof(UNICODE_STRING));
393
394 /* Increase the size */
395 Size += CapturedParams[i].MaximumLength;
396 }
397 }
398
399 /* Allocate the user data region */
400 Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
401 &UserData,
402 0,
403 &Size,
404 MEM_COMMIT,
405 PAGE_READWRITE);
406 if (!NT_SUCCESS(Status)) return Status;
407
408 /* Set the pointers to our data */
409 UserParams = UserData;
410 BufferBase = UserParams->Buffer;
411
412 /* Loop parameters again */
413 for (i = 0; i < NumberOfParameters; i++)
414 {
415 /* Check if we're in the mask */
416 if (UnicodeStringParameterMask & (1 << i))
417 {
418 /* Update the base */
419 UserParams->Parameters[i] = (ULONG_PTR)&UserParams->Strings[i];
420
421 /* Copy the string buffer */
422 RtlMoveMemory(BufferBase,
423 CapturedParams[i].Buffer,
424 CapturedParams[i].MaximumLength);
425
426 /* Set buffer */
427 CapturedParams[i].Buffer = BufferBase;
428
429 /* Copy the string structure */
430 UserParams->Strings[i] = CapturedParams[i];
431
432 /* Update the pointer */
433 BufferBase += CapturedParams[i].MaximumLength;
434 }
435 else
436 {
437 /* No need to copy any strings */
438 UserParams->Parameters[i] = Parameters[i];
439 }
440 }
441 }
442 else
443 {
444 /* Just keep the data as is */
445 UserData = Parameters;
446 }
447 }
448
449 /* Now call the worker function */
450 Status = ExpRaiseHardError(ErrorStatus,
451 NumberOfParameters,
452 UnicodeStringParameterMask,
453 UserData,
454 ValidResponseOptions,
455 &SafeResponse);
456
457 /* Check if we had done user-mode allocation */
458 if ((UserData) && (UserData != Parameters))
459 {
460 /* We did! Delete it */
461 Size = 0;
462 ZwFreeVirtualMemory(NtCurrentProcess(),
463 &UserData,
464 &Size,
465 MEM_RELEASE);
466 }
467
468 /* Return status and the response */
469 *Response = SafeResponse;
470 return Status;
471 }
472
473 /*++
474 * @name NtRaiseHardError
475 * @implemented
476 *
477 * This function sends HARDERROR_MSG LPC message to a hard-error listener,
478 * typically CSRSS.EXE. See NtSetDefaultHardErrorPort for more information.
479 * See also: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Error/NtRaiseHardError.html
480 *
481 * @param ErrorStatus
482 * Error Code
483 *
484 * @param NumberOfParameters
485 * Number of optional parameters in Parameters array
486 *
487 * @param UnicodeStringParameterMask
488 * Optional string parameter (can be only one per error code)
489 *
490 * @param Parameters
491 * Array of ULONG_PTR parameters for use in error message string
492 *
493 * @param ValidResponseOptions
494 * See HARDERROR_RESPONSE_OPTION for possible values description
495 *
496 * @param Response
497 * Pointer to HARDERROR_RESPONSE enumeration
498 *
499 * @return Status
500 *
501 * @remarks NtRaiseHardError constitutes an easy way to display messages
502 * in GUI without loading any Win32 API libraries.
503 *
504 *--*/
505 NTSTATUS
506 NTAPI
507 NtRaiseHardError(IN NTSTATUS ErrorStatus,
508 IN ULONG NumberOfParameters,
509 IN ULONG UnicodeStringParameterMask,
510 IN PULONG_PTR Parameters,
511 IN ULONG ValidResponseOptions,
512 OUT PULONG Response)
513 {
514 NTSTATUS Status = STATUS_SUCCESS;
515 PULONG_PTR SafeParams = NULL;
516 ULONG SafeResponse;
517 UNICODE_STRING SafeString;
518 ULONG i;
519 ULONG ParamSize = 0;
520 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
521
522 /* Validate parameter count */
523 if (NumberOfParameters > MAXIMUM_HARDERROR_PARAMETERS)
524 {
525 /* Fail */
526 return STATUS_INVALID_PARAMETER_2;
527 }
528
529 /* Make sure we have some at least */
530 if ((Parameters != NULL) && (NumberOfParameters == 0))
531 {
532 /* Fail */
533 return STATUS_INVALID_PARAMETER_2;
534 }
535
536 /* Check if we were called from user-mode */
537 if (PreviousMode != KernelMode)
538 {
539 /* First validate the responses */
540 switch (ValidResponseOptions)
541 {
542 /* Check all valid cases */
543 case OptionAbortRetryIgnore:
544 case OptionOk:
545 case OptionOkCancel:
546 case OptionRetryCancel:
547 case OptionYesNo:
548 case OptionYesNoCancel:
549 case OptionShutdownSystem:
550 case OptionOkNoWait:
551 case OptionCancelTryContinue:
552 break;
553
554 /* Anything else is invalid */
555 default:
556 return STATUS_INVALID_PARAMETER_4;
557 }
558
559 /* Check if we have parameters */
560 if (Parameters)
561 {
562 /* Calculate size of the parameters */
563 ParamSize = sizeof(ULONG_PTR) * NumberOfParameters;
564
565 /* Allocate a safe buffer */
566 SafeParams = ExAllocatePoolWithTag(PagedPool, ParamSize, TAG_ERR);
567 if (!SafeParams)
568 {
569 return STATUS_INSUFFICIENT_RESOURCES;
570 }
571 }
572
573 /* Enter SEH Block */
574 _SEH2_TRY
575 {
576 /* Validate the response pointer */
577 ProbeForWriteUlong(Response);
578
579 /* Check if we have parameters */
580 if (Parameters)
581 {
582 /* Validate the parameter pointers */
583 ProbeForRead(Parameters, ParamSize, sizeof(ULONG_PTR));
584
585 /* Copy them */
586 RtlCopyMemory(SafeParams, Parameters, ParamSize);
587
588 /* Now check if there's strings in it */
589 if (UnicodeStringParameterMask)
590 {
591 /* Loop every string */
592 for (i = 0; i < NumberOfParameters; i++)
593 {
594 /* Check if this parameter is a string */
595 if (UnicodeStringParameterMask & (1 << i))
596 {
597 /* Probe the structure */
598 ProbeForRead((PVOID)SafeParams[i],
599 sizeof(UNICODE_STRING),
600 sizeof(ULONG_PTR));
601
602 /* Capture it */
603 RtlCopyMemory(&SafeString,
604 (PVOID)SafeParams[i],
605 sizeof(UNICODE_STRING));
606
607 /* Probe the buffer */
608 ProbeForRead(SafeString.Buffer,
609 SafeString.MaximumLength,
610 sizeof(UCHAR));
611 }
612 }
613 }
614 }
615 }
616 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
617 {
618 /* Free captured buffer */
619 if (SafeParams) ExFreePoolWithTag(SafeParams, TAG_ERR);
620
621 /* Return the exception code */
622 _SEH2_YIELD(return _SEH2_GetExceptionCode());
623 }
624 _SEH2_END;
625
626 /* Call the system function directly, because we probed */
627 ExpRaiseHardError(ErrorStatus,
628 NumberOfParameters,
629 UnicodeStringParameterMask,
630 SafeParams,
631 ValidResponseOptions,
632 &SafeResponse);
633 }
634 else
635 {
636 /* Reuse variable */
637 SafeParams = Parameters;
638
639 /*
640 * Call the Executive Function. It will probe and copy pointers to
641 * user-mode
642 */
643 ExRaiseHardError(ErrorStatus,
644 NumberOfParameters,
645 UnicodeStringParameterMask,
646 SafeParams,
647 ValidResponseOptions,
648 &SafeResponse);
649 }
650
651 /* Check if we were called in user-mode */
652 if (PreviousMode != KernelMode)
653 {
654 /* That means we have a buffer to free */
655 if (SafeParams) ExFreePoolWithTag(SafeParams, TAG_ERR);
656
657 /* Enter SEH Block for return */
658 _SEH2_TRY
659 {
660 /* Return the response */
661 *Response = SafeResponse;
662 }
663 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
664 {
665 /* Get the exception code */
666 Status = _SEH2_GetExceptionCode();
667 }
668 _SEH2_END;
669 }
670 else
671 {
672 /* Return the response */
673 *Response = SafeResponse;
674 }
675
676 /* Return status */
677 return Status;
678 }
679
680 /*++
681 * @name NtSetDefaultHardErrorPort
682 * @implemented
683 *
684 * NtSetDefaultHardErrorPort is typically called only once. After the call,
685 * the kernel sets a BOOLEAN flag named ExReadyForErrors to TRUE, and all other
686 * attempts to change the default port fail with STATUS_UNSUCCESSFUL error code.
687 * See: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Error/NtSetDefaultHardErrorPort.html
688 * https://web.archive.org/web/20070716133753/http://www.windowsitlibrary.com/Content/356/08/2.html
689 *
690 * @param PortHandle
691 * Handle to named port object
692 *
693 * @return Status
694 *
695 * @remarks Privileges: SE_TCB_PRIVILEGE
696 *
697 *--*/
698 NTSTATUS
699 NTAPI
700 NtSetDefaultHardErrorPort(IN HANDLE PortHandle)
701 {
702 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
703 NTSTATUS Status = STATUS_UNSUCCESSFUL;
704
705 /* Check if we have the privileges */
706 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
707 {
708 DPRINT1("NtSetDefaultHardErrorPort: Caller requires "
709 "the SeTcbPrivilege privilege!\n");
710 return STATUS_PRIVILEGE_NOT_HELD;
711 }
712
713 /* Only called once during bootup, make sure we weren't called yet */
714 if (!ExReadyForErrors)
715 {
716 /* Reference the hard-error port */
717 Status = ObReferenceObjectByHandle(PortHandle,
718 0,
719 LpcPortObjectType,
720 PreviousMode,
721 (PVOID*)&ExpDefaultErrorPort,
722 NULL);
723 if (NT_SUCCESS(Status))
724 {
725 /* Keep also a reference to the process handling the hard errors */
726 ExpDefaultErrorPortProcess = PsGetCurrentProcess();
727 ObReferenceObject(ExpDefaultErrorPortProcess);
728 ExReadyForErrors = TRUE;
729 Status = STATUS_SUCCESS;
730 }
731 }
732
733 /* Return status to caller */
734 return Status;
735 }
736
737 VOID
738 __cdecl
739 _purecall(VOID)
740 {
741 /* Not supported in Kernel Mode */
742 RtlRaiseStatus(STATUS_NOT_IMPLEMENTED);
743 }
744
745 /* EOF */