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