[win32csr]
[reactos.git] / reactos / subsystems / win32 / csrss / 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
110 static
111 NTSTATUS
112 CsrpCaptureStringParameters(
113 OUT PULONG_PTR Parameters,
114 OUT PULONG SizeOfAllUnicodeStrings,
115 IN PHARDERROR_MSG HardErrorMessage,
116 HANDLE hProcess)
117 {
118 ULONG nParam, UnicodeStringParameterMask, Size = 0;
119 NTSTATUS Status;
120 UNICODE_STRING TempStringU;
121 CHAR *ParamString;
122
123 UnicodeStringParameterMask = HardErrorMessage->UnicodeStringParameterMask;
124
125 /* Read all strings from client space */
126 for (nParam = 0;
127 nParam < HardErrorMessage->NumberOfParameters;
128 nParam++, UnicodeStringParameterMask >>= 1)
129 {
130 Parameters[nParam] = 0;
131
132 /* Check if the current parameter is a unicode string */
133 if (UnicodeStringParameterMask & 0x01)
134 {
135 /* Read the UNICODE_STRING from the process memory */
136 Status = NtReadVirtualMemory(hProcess,
137 (PVOID)HardErrorMessage->Parameters[nParam],
138 &TempStringU,
139 sizeof(TempStringU),
140 NULL);
141
142 if (!NT_SUCCESS(Status)) return Status;
143
144 /* Allocate a buffer for the string */
145 ParamString = RtlAllocateHeap(RtlGetProcessHeap(),
146 HEAP_ZERO_MEMORY,
147 TempStringU.Length + sizeof(WCHAR));
148
149 if (!ParamString)
150 {
151 DPRINT1("Cannot allocate memory %d\n", TempStringU.Length);
152 return STATUS_NO_MEMORY;
153 }
154
155 /* Read the string buffer from the process memory */
156 Status = NtReadVirtualMemory(hProcess,
157 TempStringU.Buffer,
158 ParamString,
159 TempStringU.Length,
160 NULL);
161 if (!NT_SUCCESS(Status))
162 {
163 DPRINT1("NtReadVirtualMemory failed with code: %lx\n", Status);
164 RtlFreeHeap(RtlGetProcessHeap(), 0, ParamString);
165 return Status;
166 }
167
168 /* Zero terminate the string */
169 ParamString[TempStringU.Length] = 0;
170 ParamString[TempStringU.Length + 1] = 0;
171 DPRINT("ParamString=\'%S\'\n", ParamString);
172
173 Parameters[nParam] = (ULONG_PTR)ParamString;
174 Size += TempStringU.Length;
175 }
176 else
177 {
178 /* It's not a unicode string */
179 Parameters[nParam] = HardErrorMessage->Parameters[nParam];
180 }
181 }
182
183 *SizeOfAllUnicodeStrings = Size;
184 return STATUS_SUCCESS;
185 }
186
187 static
188 VOID
189 CsrpFreeStringParameters(
190 IN OUT PULONG_PTR Parameters,
191 IN PHARDERROR_MSG HardErrorMessage)
192 {
193 ULONG nParam, UnicodeStringParameterMask;
194
195 UnicodeStringParameterMask = HardErrorMessage->UnicodeStringParameterMask;
196
197 /* Loop all parameters */
198 for (nParam = 0;
199 nParam < HardErrorMessage->NumberOfParameters;
200 nParam++, UnicodeStringParameterMask >>= 1)
201 {
202 /* Check if the current parameter is a string */
203 if (UnicodeStringParameterMask & 0x01)
204 {
205 /* Free the string buffer */
206 RtlFreeHeap(RtlGetProcessHeap(), 0, (PVOID)Parameters[nParam]);
207 }
208 }
209 }
210
211
212 static
213 NTSTATUS
214 CsrpFormatMessages(
215 OUT PUNICODE_STRING TextStringU,
216 OUT PUNICODE_STRING CaptionStringU,
217 IN PULONG_PTR Parameters,
218 IN ULONG SizeOfStrings,
219 IN PHARDERROR_MSG Message,
220 IN HANDLE hProcess)
221 {
222 NTSTATUS Status;
223 UNICODE_STRING FileNameU, TempStringU, FormatU;
224 ANSI_STRING FormatA;
225 PMESSAGE_RESOURCE_ENTRY MessageResource;
226 PWSTR FormatString;
227 ULONG Size, ExceptionCode;
228
229 /* Get the file name of the client process */
230 CsrpGetClientFileName(&FileNameU, hProcess);
231
232 /* Check if we have a file name */
233 if (!FileNameU.Buffer)
234 {
235 /* No, use system */
236 RtlInitUnicodeString(&FileNameU, L"System");
237 }
238
239 /* Get text string of the error code */
240 Status = RtlFindMessage(GetModuleHandleW(L"ntdll"),
241 (ULONG_PTR)RT_MESSAGETABLE,
242 LANG_NEUTRAL,
243 Message->Status,
244 &MessageResource);
245
246 if (NT_SUCCESS(Status))
247 {
248 if (MessageResource->Flags)
249 {
250 RtlInitUnicodeString(&FormatU, (PWSTR)MessageResource->Text);
251 FormatA.Buffer = NULL;
252 }
253 else
254 {
255 RtlInitAnsiString(&FormatA, (PCHAR)MessageResource->Text);
256 RtlAnsiStringToUnicodeString(&FormatU, &FormatA, TRUE);
257 }
258 }
259 else
260 {
261 /* Fall back to hardcoded value */
262 RtlInitUnicodeString(&FormatU, L"Unknown Hard Error");
263 FormatA.Buffer = NULL;
264 }
265
266 FormatString = FormatU.Buffer;
267
268 /* Check whether a caption exists */
269 if (FormatString[0] == L'{')
270 {
271 /* Set caption start */
272 TempStringU.Buffer = ++FormatString;
273
274 /* Get size of the caption */
275 for (Size = 0; *FormatString != 0 && *FormatString != L'}'; Size++)
276 FormatString++;
277
278 /* Skip '}', '\r', '\n' */
279 FormatString += 3;
280
281 TempStringU.Length = Size * sizeof(WCHAR);
282 TempStringU.MaximumLength = TempStringU.Length;
283 }
284 else
285 {
286 /* FIXME: Set string based on severity */
287 RtlInitUnicodeString(&TempStringU, L"Application Error");
288 }
289
290 /* Calculate buffer length for the caption */
291 CaptionStringU->MaximumLength = FileNameU.Length + TempStringU.Length +
292 4 * sizeof(WCHAR);
293
294 /* Allocate a buffer for the caption */
295 CaptionStringU->Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
296 HEAP_ZERO_MEMORY,
297 CaptionStringU->MaximumLength);
298
299 /* Append the file name, seperator and the caption text */
300 CaptionStringU->Length = 0;
301 RtlAppendUnicodeStringToString(CaptionStringU, &FileNameU);
302 RtlAppendUnicodeToString(CaptionStringU, L" - ");
303 RtlAppendUnicodeStringToString(CaptionStringU, &TempStringU);
304
305 /* Zero terminate the buffer */
306 CaptionStringU->Buffer[CaptionStringU->Length] = 0;
307
308 /* Free the file name buffer */
309 RtlFreeUnicodeString(&FileNameU);
310
311 /* Check if this is an exception message */
312 if (Message->Status == STATUS_UNHANDLED_EXCEPTION)
313 {
314 ExceptionCode = Parameters[0];
315
316 /* Handle special cases */
317 if (ExceptionCode == STATUS_ACCESS_VIOLATION)
318 {
319 Parameters[0] = Parameters[1];
320 Parameters[1] = Parameters[3];
321 if (Parameters[2]) Parameters[2] = (ULONG_PTR)L"written";
322 else Parameters[2] = (ULONG_PTR)L"read";
323 MessageResource = NULL;
324 }
325 else if (ExceptionCode == STATUS_IN_PAGE_ERROR)
326 {
327 Parameters[0] = Parameters[1];
328 Parameters[1] = Parameters[3];
329 MessageResource = NULL;
330 }
331 else
332 {
333 /* Fall back to hardcoded value */
334 Parameters[2] = Parameters[1];
335 Parameters[1] = Parameters[0];
336 Parameters[0] = (ULONG_PTR)L"unknown software exception";
337 }
338
339 if (!MessageResource)
340 {
341 /* Get text string of the exception code */
342 Status = RtlFindMessage(GetModuleHandleW(L"ntdll"),
343 (ULONG_PTR)RT_MESSAGETABLE,
344 LANG_NEUTRAL,
345 ExceptionCode,
346 &MessageResource);
347
348 if (NT_SUCCESS(Status))
349 {
350 if (FormatA.Buffer) RtlFreeUnicodeString(&FormatU);
351
352 if (MessageResource->Flags)
353 {
354 RtlInitUnicodeString(&FormatU, (PWSTR)MessageResource->Text);
355 FormatA.Buffer = NULL;
356 }
357 else
358 {
359 RtlInitAnsiString(&FormatA, (PCHAR)MessageResource->Text);
360 RtlAnsiStringToUnicodeString(&FormatU, &FormatA, TRUE);
361 }
362 FormatString = FormatU.Buffer;
363 }
364 else
365 {
366 /* Fall back to hardcoded value */
367 Parameters[2] = Parameters[1];
368 Parameters[1] = Parameters[0];
369 Parameters[0] = (ULONG_PTR)L"unknown software exception";
370 }
371 }
372 }
373
374 /* Calculate length of text buffer */
375 TextStringU->MaximumLength = FormatU.Length + SizeOfStrings + 42 * sizeof(WCHAR);
376
377 /* Allocate a buffer for the text */
378 TextStringU->Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
379 HEAP_ZERO_MEMORY,
380 TextStringU->MaximumLength);
381
382 /* Wrap in SEH to protect from invalid string parameters */
383 _SEH2_TRY
384 {
385 /* Print the string into the buffer */
386 StringCbPrintfW(TextStringU->Buffer,
387 TextStringU->MaximumLength,
388 FormatString,
389 Parameters[0],
390 Parameters[1],
391 Parameters[2],
392 Parameters[3]);
393 Status = STATUS_SUCCESS;
394 }
395 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
396 {
397 /* Set error and free buffers */
398 Status = _SEH2_GetExceptionCode();
399 RtlFreeHeap(RtlGetProcessHeap(), 0, TextStringU->Buffer);
400 RtlFreeHeap(RtlGetProcessHeap(), 0, CaptionStringU->Buffer);
401 }
402 _SEH2_END
403
404 if (NT_SUCCESS(Status))
405 {
406 TextStringU->Length = wcslen(TextStringU->Buffer) * sizeof(WCHAR);
407 }
408
409 if (FormatA.Buffer) RtlFreeUnicodeString(&FormatU);
410
411 return Status;
412 }
413
414 static
415 ULONG
416 CsrpMessageBox(
417 PWSTR Text,
418 PWSTR Caption,
419 ULONG ValidResponseOptions,
420 ULONG Severity)
421 {
422 ULONG Type, MessageBoxResponse;
423
424 /* Set the message box type */
425 switch (ValidResponseOptions)
426 {
427 case OptionAbortRetryIgnore:
428 Type = MB_ABORTRETRYIGNORE;
429 break;
430 case OptionOk:
431 Type = MB_OK;
432 break;
433 case OptionOkCancel:
434 Type = MB_OKCANCEL;
435 break;
436 case OptionRetryCancel:
437 Type = MB_RETRYCANCEL;
438 break;
439 case OptionYesNo:
440 Type = MB_YESNO;
441 break;
442 case OptionYesNoCancel:
443 Type = MB_YESNOCANCEL;
444 break;
445 case OptionShutdownSystem:
446 Type = MB_RETRYCANCEL; // FIXME???
447 break;
448 /* Anything else is invalid */
449 default:
450 return ResponseNotHandled;
451 }
452
453 /* Set severity */
454 if (Severity == STATUS_SEVERITY_INFORMATIONAL) Type |= MB_ICONINFORMATION;
455 else if (Severity == STATUS_SEVERITY_WARNING) Type |= MB_ICONWARNING;
456 else if (Severity == STATUS_SEVERITY_ERROR) Type |= MB_ICONERROR;
457
458 Type |= MB_SYSTEMMODAL | MB_SETFOREGROUND;
459
460 DPRINT("Text = '%S', Caption = '%S', Severity = %d, Type = 0x%lx\n",
461 Text, Caption, Severity, Type);
462
463 /* Display a message box */
464 MessageBoxResponse = MessageBoxW(0, Text, Caption, Type);
465
466 /* Return response value */
467 switch (MessageBoxResponse)
468 {
469 case IDOK: return ResponseOk;
470 case IDCANCEL: return ResponseCancel;
471 case IDYES: return ResponseYes;
472 case IDNO: return ResponseNo;
473 case IDABORT: return ResponseAbort;
474 case IDIGNORE: return ResponseIgnore;
475 case IDRETRY: return ResponseRetry;
476 case IDTRYAGAIN: return ResponseTryAgain;
477 case IDCONTINUE: return ResponseContinue;
478 }
479
480 return ResponseNotHandled;
481 }
482
483 BOOL
484 WINAPI
485 Win32CsrHardError(
486 IN PCSRSS_PROCESS_DATA ProcessData,
487 IN PHARDERROR_MSG Message)
488 {
489 ULONG_PTR Parameters[MAXIMUM_HARDERROR_PARAMETERS];
490 OBJECT_ATTRIBUTES ObjectAttributes;
491 UNICODE_STRING TextU, CaptionU;
492 NTSTATUS Status;
493 HANDLE hProcess;
494 ULONG Size;
495
496 /* Default to not handled */
497 Message->Response = ResponseNotHandled;
498
499 /* Make sure we don't have too many parameters */
500 if (Message->NumberOfParameters > MAXIMUM_HARDERROR_PARAMETERS)
501 Message->NumberOfParameters = MAXIMUM_HARDERROR_PARAMETERS;
502
503 /* Initialize object attributes */
504 InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
505
506 /* Open client process */
507 Status = NtOpenProcess(&hProcess,
508 PROCESS_VM_READ | PROCESS_QUERY_INFORMATION,
509 &ObjectAttributes,
510 &Message->h.ClientId);
511
512 if (!NT_SUCCESS(Status))
513 {
514 DPRINT1("NtOpenProcess failed with code: %lx\n", Status);
515 return FALSE;
516 }
517
518 /* Capture all string parameters from the process memory */
519 Status = CsrpCaptureStringParameters(Parameters, &Size, Message, hProcess);
520 if (!NT_SUCCESS(Status))
521 {
522 NtClose(hProcess);
523 return FALSE;
524 }
525
526 /* Format the caption and message box text */
527 Status = CsrpFormatMessages(&TextU,
528 &CaptionU,
529 Parameters,
530 Size,
531 Message,
532 hProcess);
533
534 /* Cleanup */
535 CsrpFreeStringParameters(Parameters, Message);
536 NtClose(hProcess);
537
538 if (!NT_SUCCESS(Status))
539 {
540 return FALSE;
541 }
542
543 /* Display the message box */
544 Message->Response = CsrpMessageBox(TextU.Buffer,
545 CaptionU.Buffer,
546 Message->ValidResponseOptions,
547 (ULONG)Message->Status >> 30);
548
549 RtlFreeUnicodeString(&TextU);
550 RtlFreeUnicodeString(&CaptionU);
551
552 return TRUE;
553 }
554