xref: /reactos/ntoskrnl/ex/harderr.c (revision b819608e)
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 & 0x10000000))
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)
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 &~ 0x10000000;
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                                          (PVOID)Message,
229                                          (PVOID)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     else
259     {
260         /* Set defaults */
261         *Response = ResponseReturnToCaller;
262         Status = STATUS_SUCCESS;
263     }
264 
265     /* Return status */
266     return Status;
267 }
268 
269 /*++
270  * @name ExRaiseAccessViolation
271  * @implemented
272  *
273  * The ExRaiseAccessViolation routine can be used with structured exception
274  * handling to throw a driver-determined exception for a memory access
275  * violation that occurs when a driver processes I/O requests.
276  * See: http://msdn.microsoft.com/library/en-us/Kernel_r/hh/Kernel_r/k102_71b4c053-599c-4a6d-8a59-08aae6bdc534.xml.asp?frame=true
277  *      http://www.osronline.com/ddkx/kmarch/k102_814i.htm
278  *
279  * @return None
280  *
281  * @remarks None
282  *
283  *--*/
284 VOID
285 NTAPI
286 ExRaiseAccessViolation(VOID)
287 {
288     /* Raise the Right Status */
289     RtlRaiseStatus(STATUS_ACCESS_VIOLATION);
290 }
291 
292 /*++
293  * @name ExRaiseDatatypeMisalignment
294  * @implemented
295  *
296  * ExRaiseDatatypeMisalignment raises an exception with the exception
297  * code set to STATUS_DATATYPE_MISALIGNMENT
298  * See: MSDN / DDK
299  *      http://www.osronline.com/ddkx/kmarch/k102_814i.htm
300  *
301  * @return None
302  *
303  * @remarks None
304  *
305  *--*/
306 VOID
307 NTAPI
308 ExRaiseDatatypeMisalignment(VOID)
309 {
310     /* Raise the Right Status */
311     RtlRaiseStatus(STATUS_DATATYPE_MISALIGNMENT);
312 }
313 
314 /*++
315  * @name ExSystemExceptionFilter
316  * @implemented
317  *
318  * TODO: Add description
319  *
320  * @return FILLME
321  *
322  * @remarks None
323  *
324  *--*/
325 LONG
326 NTAPI
327 ExSystemExceptionFilter(VOID)
328 {
329     return KeGetPreviousMode() != KernelMode ?
330            EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH;
331 }
332 
333 /*++
334  * @name ExRaiseHardError
335  * @implemented
336  *
337  * See NtRaiseHardError
338  *
339  * @param ErrorStatus
340  *        Error Code
341  *
342  * @param NumberOfParameters
343  *        Number of optional parameters in Parameters array
344  *
345  * @param UnicodeStringParameterMask
346  *        Optional string parameter (can be only one per error code)
347  *
348  * @param Parameters
349  *        Array of ULONG parameters for use in error message string
350  *
351  * @param ValidResponseOptions
352  *        See HARDERROR_RESPONSE_OPTION for possible values description
353  *
354  * @param Response
355  *        Pointer to HARDERROR_RESPONSE enumeration
356  *
357  * @return None
358  *
359  * @remarks None
360  *
361  *--*/
362 NTSTATUS
363 NTAPI
364 ExRaiseHardError(IN NTSTATUS ErrorStatus,
365                  IN ULONG NumberOfParameters,
366                  IN ULONG UnicodeStringParameterMask,
367                  IN PULONG_PTR Parameters,
368                  IN ULONG ValidResponseOptions,
369                  OUT PULONG Response)
370 {
371     SIZE_T Size;
372     UNICODE_STRING CapturedParams[MAXIMUM_HARDERROR_PARAMETERS];
373     ULONG i;
374     PVOID UserData = NULL;
375     PHARDERROR_USER_PARAMETERS UserParams;
376     PWSTR BufferBase;
377     ULONG SafeResponse;
378     NTSTATUS Status;
379     PAGED_CODE();
380 
381     /* Check if we have parameters */
382     if (Parameters)
383     {
384         /* Check if we have strings */
385         if (UnicodeStringParameterMask)
386         {
387             /* Calculate the required size */
388             Size = FIELD_OFFSET(HARDERROR_USER_PARAMETERS, Buffer[0]);
389 
390             /* Loop each parameter */
391             for (i = 0; i < NumberOfParameters; i++)
392             {
393                 /* Check if it's part of the mask */
394                 if (UnicodeStringParameterMask & (1 << i))
395                 {
396                     /* Copy it */
397                     RtlMoveMemory(&CapturedParams[i],
398                                   (PVOID)Parameters[i],
399                                   sizeof(UNICODE_STRING));
400 
401                     /* Increase the size */
402                     Size += CapturedParams[i].MaximumLength;
403                 }
404             }
405 
406             /* Allocate the user data region */
407             Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
408                                              &UserData,
409                                              0,
410                                              &Size,
411                                              MEM_COMMIT,
412                                              PAGE_READWRITE);
413             if (!NT_SUCCESS(Status)) return Status;
414 
415             /* Set the pointers to our data */
416             UserParams = UserData;
417             BufferBase = UserParams->Buffer;
418 
419             /* Loop parameters again */
420             for (i = 0; i < NumberOfParameters; i++)
421             {
422                 /* Check if we're in the mask */
423                 if (UnicodeStringParameterMask & (1 << i))
424                 {
425                     /* Update the base */
426                     UserParams->Parameters[i] = (ULONG_PTR)&UserParams->Strings[i];
427 
428                     /* Copy the string buffer */
429                     RtlMoveMemory(BufferBase,
430                                   CapturedParams[i].Buffer,
431                                   CapturedParams[i].MaximumLength);
432 
433                     /* Set buffer */
434                     CapturedParams[i].Buffer = BufferBase;
435 
436                     /* Copy the string structure */
437                     UserParams->Strings[i] = CapturedParams[i];
438 
439                     /* Update the pointer */
440                     BufferBase += CapturedParams[i].MaximumLength;
441                 }
442                 else
443                 {
444                     /* No need to copy any strings */
445                     UserParams->Parameters[i] = Parameters[i];
446                 }
447             }
448         }
449         else
450         {
451             /* Just keep the data as is */
452             UserData = Parameters;
453         }
454     }
455 
456     /* Now call the worker function */
457     Status = ExpRaiseHardError(ErrorStatus,
458                                NumberOfParameters,
459                                UnicodeStringParameterMask,
460                                UserData,
461                                ValidResponseOptions,
462                                &SafeResponse);
463 
464     /* Check if we had done user-mode allocation */
465     if ((UserData) && (UserData != Parameters))
466     {
467         /* We did! Delete it */
468         Size = 0;
469         ZwFreeVirtualMemory(NtCurrentProcess(),
470                             &UserData,
471                             &Size,
472                             MEM_RELEASE);
473     }
474 
475     /* Return status and the response */
476     *Response = SafeResponse;
477     return Status;
478 }
479 
480 /*++
481  * @name NtRaiseHardError
482  * @implemented
483  *
484  * This function sends HARDERROR_MSG LPC message to listener
485  * (typically CSRSS.EXE). See NtSetDefaultHardErrorPort for more information
486  * See: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Error/NtRaiseHardError.html
487  *
488  * @param ErrorStatus
489  *        Error Code
490  *
491  * @param NumberOfParameters
492  *        Number of optional parameters in Parameters array
493  *
494  * @param UnicodeStringParameterMask
495  *        Optional string parameter (can be only one per error code)
496  *
497  * @param Parameters
498  *        Array of ULONG_PTR parameters for use in error message string
499  *
500  * @param ValidResponseOptions
501  *        See HARDERROR_RESPONSE_OPTION for possible values description
502  *
503  * @param Response
504  *        Pointer to HARDERROR_RESPONSE enumeration
505  *
506  * @return Status
507  *
508  * @remarks NtRaiseHardError is easy way to display message in GUI
509  *          without loading Win32 API libraries
510  *
511  *--*/
512 NTSTATUS
513 NTAPI
514 NtRaiseHardError(IN NTSTATUS ErrorStatus,
515                  IN ULONG NumberOfParameters,
516                  IN ULONG UnicodeStringParameterMask,
517                  IN PULONG_PTR Parameters,
518                  IN ULONG ValidResponseOptions,
519                  OUT PULONG Response)
520 {
521     NTSTATUS Status = STATUS_SUCCESS;
522     PULONG_PTR SafeParams = NULL;
523     ULONG SafeResponse;
524     UNICODE_STRING SafeString;
525     ULONG i;
526     ULONG ParamSize = 0;
527     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
528 
529     /* Validate parameter count */
530     if (NumberOfParameters > MAXIMUM_HARDERROR_PARAMETERS)
531     {
532         /* Fail */
533         return STATUS_INVALID_PARAMETER_2;
534     }
535 
536     /* Make sure we have some at least */
537     if ((Parameters != NULL) && (NumberOfParameters == 0))
538     {
539         /* Fail */
540         return STATUS_INVALID_PARAMETER_2;
541     }
542 
543     /* Check if we were called from user-mode */
544     if (PreviousMode != KernelMode)
545     {
546         /* First validate the responses */
547         switch (ValidResponseOptions)
548         {
549             /* Check all valid cases */
550             case OptionAbortRetryIgnore:
551             case OptionOk:
552             case OptionOkCancel:
553             case OptionRetryCancel:
554             case OptionYesNo:
555             case OptionYesNoCancel:
556             case OptionShutdownSystem:
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 Privilege */
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 port */
722         Status = ObReferenceObjectByHandle(PortHandle,
723                                            0,
724                                            LpcPortObjectType,
725                                            PreviousMode,
726                                            (PVOID*)&ExpDefaultErrorPort,
727                                            NULL);
728         if (NT_SUCCESS(Status))
729         {
730             /* Save the data */
731             ExpDefaultErrorPortProcess = PsGetCurrentProcess();
732             ExReadyForErrors = TRUE;
733         }
734     }
735 
736     /* Return status to caller */
737     return Status;
738 }
739 
740 VOID
741 __cdecl
742 _purecall(VOID)
743 {
744     /* Not supported in Kernel Mode */
745     RtlRaiseStatus(STATUS_NOT_IMPLEMENTED);
746 }
747 
748 /* EOF */
749