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