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