[rshell]
[reactos.git] / win32ss / user / winsrv / usersrv / harderror.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS User API Server DLL
4 * FILE: win32ss/user/winsrv/usersrv/harderror.c
5 * PURPOSE: Hard errors
6 * PROGRAMMERS: Dmitry Philippov (shedon@mail.ru)
7 * Timo Kreuzer (timo.kreuzer@reactos.org)
8 */
9
10 /* INCLUDES *******************************************************************/
11
12 #include "usersrv.h"
13
14 #define NDEBUG
15 #include <debug.h>
16
17 #include <strsafe.h>
18
19 #define IDTRYAGAIN 10
20 #define IDCONTINUE 11
21
22 /* FUNCTIONS ******************************************************************/
23
24 static
25 NTSTATUS
26 UserpGetClientFileName(
27 OUT PUNICODE_STRING ClientFileNameU,
28 HANDLE hProcess)
29 {
30 PLIST_ENTRY ModuleListHead;
31 PLIST_ENTRY Entry;
32 PLDR_DATA_TABLE_ENTRY Module;
33 PPEB_LDR_DATA Ldr;
34 PROCESS_BASIC_INFORMATION ClientBasicInfo;
35 LDR_DATA_TABLE_ENTRY ModuleData;
36 PVOID ClientDllBase;
37 NTSTATUS Status;
38 PPEB Peb;
39
40 /* Initialize string */
41 ClientFileNameU->MaximumLength = 0;
42 ClientFileNameU->Length = 0;
43 ClientFileNameU->Buffer = NULL;
44
45 /* Query process information */
46 Status = NtQueryInformationProcess(hProcess,
47 ProcessBasicInformation,
48 &ClientBasicInfo,
49 sizeof(ClientBasicInfo),
50 NULL);
51 if (!NT_SUCCESS(Status)) return Status;
52
53 Peb = ClientBasicInfo.PebBaseAddress;
54 if (!Peb) return STATUS_UNSUCCESSFUL;
55
56 Status = NtReadVirtualMemory(hProcess, &Peb->Ldr, &Ldr, sizeof(Ldr), NULL);
57 if (!NT_SUCCESS(Status)) return Status;
58
59 ModuleListHead = &Ldr->InLoadOrderModuleList;
60 Status = NtReadVirtualMemory(hProcess,
61 &ModuleListHead->Flink,
62 &Entry,
63 sizeof(Entry),
64 NULL);
65 if (!NT_SUCCESS(Status)) return Status;
66
67 if (Entry == ModuleListHead) return STATUS_UNSUCCESSFUL;
68
69 Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
70
71 Status = NtReadVirtualMemory(hProcess,
72 Module,
73 &ModuleData,
74 sizeof(ModuleData),
75 NULL);
76 if (!NT_SUCCESS(Status)) return Status;
77
78 Status = NtReadVirtualMemory(hProcess,
79 &Peb->ImageBaseAddress,
80 &ClientDllBase,
81 sizeof(ClientDllBase),
82 NULL);
83 if (!NT_SUCCESS(Status)) return Status;
84
85 if (ClientDllBase != ModuleData.DllBase) return STATUS_UNSUCCESSFUL;
86
87 ClientFileNameU->MaximumLength = ModuleData.BaseDllName.MaximumLength;
88 ClientFileNameU->Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
89 HEAP_ZERO_MEMORY,
90 ClientFileNameU->MaximumLength);
91
92 Status = NtReadVirtualMemory(hProcess,
93 ModuleData.BaseDllName.Buffer,
94 ClientFileNameU->Buffer,
95 ClientFileNameU->MaximumLength,
96 NULL);
97 if (!NT_SUCCESS(Status))
98 {
99 RtlFreeHeap(RtlGetProcessHeap(), 0, ClientFileNameU->Buffer);
100 ClientFileNameU->Buffer = NULL;
101 ClientFileNameU->MaximumLength = 0;
102 return Status;
103 }
104
105 ClientFileNameU->Length = wcslen(ClientFileNameU->Buffer)*sizeof(wchar_t);
106 DPRINT("ClientFileNameU=\'%wZ\'\n", &ClientFileNameU);
107
108 return STATUS_SUCCESS;
109 }
110
111 static
112 VOID
113 UserpFreeStringParameters(
114 IN OUT PULONG_PTR Parameters,
115 IN PHARDERROR_MSG HardErrorMessage)
116 {
117 ULONG nParam;
118
119 /* Loop all parameters */
120 for (nParam = 0; nParam < HardErrorMessage->NumberOfParameters; nParam++)
121 {
122 /* Check if the current parameter is a string */
123 if (HardErrorMessage->UnicodeStringParameterMask & (1 << nParam) && Parameters[nParam])
124 {
125 /* Free the string buffer */
126 RtlFreeHeap(RtlGetProcessHeap(), 0, (PVOID)Parameters[nParam]);
127 }
128 }
129 }
130
131 static
132 NTSTATUS
133 UserpCaptureStringParameters(
134 OUT PULONG_PTR Parameters,
135 OUT PULONG SizeOfAllUnicodeStrings,
136 IN PHARDERROR_MSG HardErrorMessage,
137 HANDLE hProcess)
138 {
139 ULONG nParam, Size = 0;
140 NTSTATUS Status = STATUS_SUCCESS;
141 UNICODE_STRING TempStringU, ParamStringU;
142 ANSI_STRING TempStringA;
143
144 if (SizeOfAllUnicodeStrings)
145 *SizeOfAllUnicodeStrings = 0;
146
147 /* Read all strings from client space */
148 for (nParam = 0; nParam < HardErrorMessage->NumberOfParameters; nParam++)
149 {
150 Parameters[nParam] = 0;
151
152 /* Check if the current parameter is a unicode string */
153 if (HardErrorMessage->UnicodeStringParameterMask & (1 << nParam))
154 {
155 /* Read the UNICODE_STRING from the process memory */
156 Status = NtReadVirtualMemory(hProcess,
157 (PVOID)HardErrorMessage->Parameters[nParam],
158 &ParamStringU,
159 sizeof(ParamStringU),
160 NULL);
161
162 if (!NT_SUCCESS(Status))
163 break;
164
165 /* Allocate a buffer for the string */
166 TempStringU.MaximumLength = ParamStringU.Length;
167 TempStringU.Length = ParamStringU.Length;
168 TempStringU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
169 HEAP_ZERO_MEMORY,
170 TempStringU.MaximumLength);
171
172 if (!TempStringU.Buffer)
173 {
174 DPRINT1("Cannot allocate memory %u\n", TempStringU.MaximumLength);
175 Status = STATUS_NO_MEMORY;
176 }
177
178 /* Read the string buffer from the process memory */
179 Status = NtReadVirtualMemory(hProcess,
180 ParamStringU.Buffer,
181 TempStringU.Buffer,
182 ParamStringU.Length,
183 NULL);
184 if (!NT_SUCCESS(Status))
185 {
186 DPRINT1("NtReadVirtualMemory failed with code: %lx\n", Status);
187 RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringU.Buffer);
188 break;
189 }
190
191 DPRINT("ParamString=\'%wZ\'\n", &TempStringU);
192
193 /* Allocate a buffer for converted to ANSI string */
194 TempStringA.MaximumLength = RtlUnicodeStringToAnsiSize(&TempStringU);
195 TempStringA.Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
196 HEAP_ZERO_MEMORY,
197 TempStringA.MaximumLength);
198
199 if (!TempStringA.Buffer)
200 {
201 DPRINT1("Cannot allocate memory %u\n", TempStringA.MaximumLength);
202 RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringU.Buffer);
203 Status = STATUS_NO_MEMORY;
204 break;
205 }
206
207 /* Convert string to ANSI and free temporary buffer */
208 Status = RtlUnicodeStringToAnsiString(&TempStringA, &TempStringU, FALSE);
209 RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringU.Buffer);
210 if (!NT_SUCCESS(Status))
211 {
212 RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringA.Buffer);
213 break;
214 }
215
216 /* Note: RtlUnicodeStringToAnsiString returns NULL terminated string */
217 Parameters[nParam] = (ULONG_PTR)TempStringA.Buffer;
218 Size += TempStringU.Length;
219 }
220 else
221 {
222 /* It's not a unicode string */
223 Parameters[nParam] = HardErrorMessage->Parameters[nParam];
224 }
225 }
226
227 if (!NT_SUCCESS(Status))
228 {
229 UserpFreeStringParameters(Parameters, HardErrorMessage);
230 return Status;
231 }
232
233 if (SizeOfAllUnicodeStrings)
234 *SizeOfAllUnicodeStrings = Size;
235
236 return Status;
237 }
238
239 static
240 NTSTATUS
241 UserpFormatMessages(
242 OUT PUNICODE_STRING TextStringU,
243 OUT PUNICODE_STRING CaptionStringU,
244 IN PULONG_PTR Parameters,
245 IN ULONG SizeOfStrings,
246 IN PHARDERROR_MSG Message,
247 IN HANDLE hProcess)
248 {
249 NTSTATUS Status;
250 UNICODE_STRING FileNameU, TempStringU, FormatU;
251 ANSI_STRING FormatA;
252 PMESSAGE_RESOURCE_ENTRY MessageResource;
253 PWSTR FormatString;
254 ULONG Size, ExceptionCode;
255
256 /* Get the file name of the client process */
257 UserpGetClientFileName(&FileNameU, hProcess);
258
259 /* Check if we have a file name */
260 if (!FileNameU.Buffer)
261 {
262 /* No, use system */
263 RtlInitUnicodeString(&FileNameU, L"System");
264 }
265
266 /* Get text string of the error code */
267 Status = RtlFindMessage(GetModuleHandleW(L"ntdll"),
268 (ULONG_PTR)RT_MESSAGETABLE,
269 LANG_NEUTRAL,
270 Message->Status,
271 &MessageResource);
272
273 if (NT_SUCCESS(Status))
274 {
275 if (MessageResource->Flags)
276 {
277 RtlInitUnicodeString(&FormatU, (PWSTR)MessageResource->Text);
278 FormatA.Buffer = NULL;
279 }
280 else
281 {
282 RtlInitAnsiString(&FormatA, (PCHAR)MessageResource->Text);
283 RtlAnsiStringToUnicodeString(&FormatU, &FormatA, TRUE);
284 }
285 }
286 else
287 {
288 /* Fall back to hardcoded value */
289 RtlInitUnicodeString(&FormatU, L"Unknown Hard Error");
290 FormatA.Buffer = NULL;
291 }
292
293 FormatString = FormatU.Buffer;
294
295 /* Check whether a caption exists */
296 if (FormatString[0] == L'{')
297 {
298 /* Set caption start */
299 TempStringU.Buffer = ++FormatString;
300
301 /* Get size of the caption */
302 for (Size = 0; *FormatString != 0 && *FormatString != L'}'; Size++)
303 FormatString++;
304
305 /* Skip '}', '\r', '\n' */
306 FormatString += 3;
307
308 TempStringU.Length = Size * sizeof(WCHAR);
309 TempStringU.MaximumLength = TempStringU.Length;
310 }
311 else
312 {
313 /* FIXME: Set string based on severity */
314 RtlInitUnicodeString(&TempStringU, L"Application Error");
315 }
316
317 /* Calculate buffer length for the caption */
318 CaptionStringU->MaximumLength = FileNameU.Length + TempStringU.Length +
319 4 * sizeof(WCHAR);
320
321 /* Allocate a buffer for the caption */
322 CaptionStringU->Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
323 HEAP_ZERO_MEMORY,
324 CaptionStringU->MaximumLength);
325
326 /* Append the file name, seperator and the caption text */
327 CaptionStringU->Length = 0;
328 RtlAppendUnicodeStringToString(CaptionStringU, &FileNameU);
329 RtlAppendUnicodeToString(CaptionStringU, L" - ");
330 RtlAppendUnicodeStringToString(CaptionStringU, &TempStringU);
331
332 /* Zero terminate the buffer */
333 CaptionStringU->Buffer[CaptionStringU->Length / sizeof(WCHAR)] = 0;
334
335 /* Free the file name buffer */
336 RtlFreeUnicodeString(&FileNameU);
337
338 /* Check if this is an exception message */
339 if (Message->Status == STATUS_UNHANDLED_EXCEPTION)
340 {
341 ExceptionCode = Parameters[0];
342
343 /* Handle special cases */
344 if (ExceptionCode == STATUS_ACCESS_VIOLATION)
345 {
346 Parameters[0] = Parameters[1];
347 Parameters[1] = Parameters[3];
348 if (Parameters[2]) Parameters[2] = (ULONG_PTR)L"written";
349 else Parameters[2] = (ULONG_PTR)L"read";
350 MessageResource = NULL;
351 }
352 else if (ExceptionCode == STATUS_IN_PAGE_ERROR)
353 {
354 Parameters[0] = Parameters[1];
355 Parameters[1] = Parameters[3];
356 MessageResource = NULL;
357 }
358 else
359 {
360 /* Fall back to hardcoded value */
361 Parameters[2] = Parameters[1];
362 Parameters[1] = Parameters[0];
363 Parameters[0] = (ULONG_PTR)L"unknown software exception";
364 }
365
366 if (!MessageResource)
367 {
368 /* Get text string of the exception code */
369 Status = RtlFindMessage(GetModuleHandleW(L"ntdll"),
370 (ULONG_PTR)RT_MESSAGETABLE,
371 LANG_NEUTRAL,
372 ExceptionCode,
373 &MessageResource);
374
375 if (NT_SUCCESS(Status))
376 {
377 if (FormatA.Buffer) RtlFreeUnicodeString(&FormatU);
378
379 if (MessageResource->Flags)
380 {
381 RtlInitUnicodeString(&FormatU, (PWSTR)MessageResource->Text);
382 FormatA.Buffer = NULL;
383 }
384 else
385 {
386 RtlInitAnsiString(&FormatA, (PCHAR)MessageResource->Text);
387 RtlAnsiStringToUnicodeString(&FormatU, &FormatA, TRUE);
388 }
389 FormatString = FormatU.Buffer;
390 }
391 else
392 {
393 /* Fall back to hardcoded value */
394 Parameters[2] = Parameters[1];
395 Parameters[1] = Parameters[0];
396 Parameters[0] = (ULONG_PTR)L"unknown software exception";
397 }
398 }
399 }
400
401 /* Calculate length of text buffer */
402 TextStringU->MaximumLength = FormatU.Length + SizeOfStrings + 42 * sizeof(WCHAR);
403
404 /* Allocate a buffer for the text */
405 TextStringU->Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
406 HEAP_ZERO_MEMORY,
407 TextStringU->MaximumLength);
408
409 /* Wrap in SEH to protect from invalid string parameters */
410 _SEH2_TRY
411 {
412 /* Print the string into the buffer */
413 StringCbPrintfW(TextStringU->Buffer,
414 TextStringU->MaximumLength,
415 FormatString,
416 Parameters[0],
417 Parameters[1],
418 Parameters[2],
419 Parameters[3]);
420 Status = STATUS_SUCCESS;
421 }
422 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
423 {
424 /* Set error and free buffers */
425 Status = _SEH2_GetExceptionCode();
426 RtlFreeHeap(RtlGetProcessHeap(), 0, TextStringU->Buffer);
427 RtlFreeHeap(RtlGetProcessHeap(), 0, CaptionStringU->Buffer);
428 }
429 _SEH2_END
430
431 if (NT_SUCCESS(Status))
432 {
433 TextStringU->Length = wcslen(TextStringU->Buffer) * sizeof(WCHAR);
434 }
435
436 if (FormatA.Buffer) RtlFreeUnicodeString(&FormatU);
437
438 return Status;
439 }
440
441 static
442 ULONG
443 UserpMessageBox(
444 PWSTR Text,
445 PWSTR Caption,
446 ULONG ValidResponseOptions,
447 ULONG Severity)
448 {
449 ULONG Type, MessageBoxResponse;
450
451 /* Set the message box type */
452 switch (ValidResponseOptions)
453 {
454 case OptionAbortRetryIgnore:
455 Type = MB_ABORTRETRYIGNORE;
456 break;
457 case OptionOk:
458 Type = MB_OK;
459 break;
460 case OptionOkCancel:
461 Type = MB_OKCANCEL;
462 break;
463 case OptionRetryCancel:
464 Type = MB_RETRYCANCEL;
465 break;
466 case OptionYesNo:
467 Type = MB_YESNO;
468 break;
469 case OptionYesNoCancel:
470 Type = MB_YESNOCANCEL;
471 break;
472 case OptionShutdownSystem:
473 Type = MB_RETRYCANCEL; // FIXME???
474 break;
475 /* Anything else is invalid */
476 default:
477 return ResponseNotHandled;
478 }
479
480 /* Set severity */
481 if (Severity == STATUS_SEVERITY_INFORMATIONAL) Type |= MB_ICONINFORMATION;
482 else if (Severity == STATUS_SEVERITY_WARNING) Type |= MB_ICONWARNING;
483 else if (Severity == STATUS_SEVERITY_ERROR) Type |= MB_ICONERROR;
484
485 Type |= MB_SYSTEMMODAL | MB_SETFOREGROUND;
486
487 DPRINT("Text = '%S', Caption = '%S', Severity = %d, Type = 0x%lx\n",
488 Text, Caption, Severity, Type);
489
490 /* Display a message box */
491 MessageBoxResponse = MessageBoxW(0, Text, Caption, Type);
492
493 /* Return response value */
494 switch (MessageBoxResponse)
495 {
496 case IDOK: return ResponseOk;
497 case IDCANCEL: return ResponseCancel;
498 case IDYES: return ResponseYes;
499 case IDNO: return ResponseNo;
500 case IDABORT: return ResponseAbort;
501 case IDIGNORE: return ResponseIgnore;
502 case IDRETRY: return ResponseRetry;
503 case IDTRYAGAIN: return ResponseTryAgain;
504 case IDCONTINUE: return ResponseContinue;
505 }
506
507 return ResponseNotHandled;
508 }
509
510 VOID
511 WINAPI
512 UserServerHardError(
513 IN PCSR_THREAD ThreadData,
514 IN PHARDERROR_MSG Message)
515 {
516 #if DBG
517 PCSR_PROCESS ProcessData = ThreadData->Process;
518 #endif
519 ULONG_PTR Parameters[MAXIMUM_HARDERROR_PARAMETERS];
520 OBJECT_ATTRIBUTES ObjectAttributes;
521 UNICODE_STRING TextU, CaptionU;
522 NTSTATUS Status;
523 HANDLE hProcess;
524 ULONG Size;
525
526 /* Default to not handled */
527 ASSERT(ProcessData != NULL);
528 Message->Response = ResponseNotHandled;
529
530 /* Make sure we don't have too many parameters */
531 if (Message->NumberOfParameters > MAXIMUM_HARDERROR_PARAMETERS)
532 Message->NumberOfParameters = MAXIMUM_HARDERROR_PARAMETERS;
533
534 /* Initialize object attributes */
535 InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
536
537 /* Open client process */
538 Status = NtOpenProcess(&hProcess,
539 PROCESS_VM_READ | PROCESS_QUERY_INFORMATION,
540 &ObjectAttributes,
541 &Message->h.ClientId);
542
543 if (!NT_SUCCESS(Status))
544 {
545 DPRINT1("NtOpenProcess failed with code: %lx\n", Status);
546 return;
547 }
548
549 /* Capture all string parameters from the process memory */
550 Status = UserpCaptureStringParameters(Parameters, &Size, Message, hProcess);
551 if (!NT_SUCCESS(Status))
552 {
553 NtClose(hProcess);
554 return;
555 }
556
557 /* Format the caption and message box text */
558 Status = UserpFormatMessages(&TextU,
559 &CaptionU,
560 Parameters,
561 Size,
562 Message,
563 hProcess);
564
565 /* Cleanup */
566 UserpFreeStringParameters(Parameters, Message);
567 NtClose(hProcess);
568
569 if (!NT_SUCCESS(Status))
570 {
571 return;
572 }
573
574 /* Display the message box */
575 Message->Response = UserpMessageBox(TextU.Buffer,
576 CaptionU.Buffer,
577 Message->ValidResponseOptions,
578 (ULONG)Message->Status >> 30);
579
580 RtlFreeUnicodeString(&TextU);
581 RtlFreeUnicodeString(&CaptionU);
582
583 return;
584 }
585
586 /* EOF */