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