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