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