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