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