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