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