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