0e10aad8a9d67d4f459757cfc469328c5cebadc1
[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 PWSTR 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 / sizeof(WCHAR)] = 0;
170 DPRINT("ParamString=\'%S\'\n", ParamString);
171
172 Parameters[nParam] = (ULONG_PTR)ParamString;
173 Size += TempStringU.Length;
174 }
175 else
176 {
177 /* It's not a unicode string */
178 Parameters[nParam] = HardErrorMessage->Parameters[nParam];
179 }
180 }
181
182 *SizeOfAllUnicodeStrings = Size;
183 return STATUS_SUCCESS;
184 }
185
186 static
187 VOID
188 CsrpFreeStringParameters(
189 IN OUT PULONG_PTR Parameters,
190 IN PHARDERROR_MSG HardErrorMessage)
191 {
192 ULONG nParam, UnicodeStringParameterMask;
193
194 UnicodeStringParameterMask = HardErrorMessage->UnicodeStringParameterMask;
195
196 /* Loop all parameters */
197 for (nParam = 0;
198 nParam < HardErrorMessage->NumberOfParameters;
199 nParam++, UnicodeStringParameterMask >>= 1)
200 {
201 /* Check if the current parameter is a string */
202 if (UnicodeStringParameterMask & 0x01)
203 {
204 /* Free the string buffer */
205 RtlFreeHeap(RtlGetProcessHeap(), 0, (PVOID)Parameters[nParam]);
206 }
207 }
208 }
209
210
211 static
212 NTSTATUS
213 CsrpFormatMessages(
214 OUT PUNICODE_STRING TextStringU,
215 OUT PUNICODE_STRING CaptionStringU,
216 IN PULONG_PTR Parameters,
217 IN ULONG SizeOfStrings,
218 IN PHARDERROR_MSG Message,
219 IN HANDLE hProcess)
220 {
221 NTSTATUS Status;
222 UNICODE_STRING FileNameU, TempStringU, FormatU;
223 ANSI_STRING FormatA;
224 PRTL_MESSAGE_RESOURCE_ENTRY MessageResource;
225 PWSTR FormatString;
226 ULONG Size, ExceptionCode;
227
228 /* Get the file name of the client process */
229 CsrpGetClientFileName(&FileNameU, hProcess);
230
231 /* Check if we have a file name */
232 if (!FileNameU.Buffer)
233 {
234 /* No, use system */
235 RtlInitUnicodeString(&FileNameU, L"System");
236 }
237
238 /* Get text string of the error code */
239 Status = RtlFindMessage(GetModuleHandleW(L"ntdll"),
240 (ULONG_PTR)RT_MESSAGETABLE,
241 LANG_NEUTRAL,
242 Message->Status,
243 &MessageResource);
244
245 if (NT_SUCCESS(Status))
246 {
247 if (MessageResource->Flags)
248 {
249 RtlInitUnicodeString(&FormatU, (PWSTR)MessageResource->Text);
250 FormatA.Buffer = NULL;
251 }
252 else
253 {
254 RtlInitAnsiString(&FormatA, MessageResource->Text);
255 RtlAnsiStringToUnicodeString(&FormatU, &FormatA, TRUE);
256 }
257 }
258 else
259 {
260 /* Fall back to hardcoded value */
261 RtlInitUnicodeString(&FormatU, L"Unknown Hard Error");
262 FormatA.Buffer = NULL;
263 }
264
265 FormatString = FormatU.Buffer;
266
267 /* Check whether a caption exists */
268 if (FormatString[0] == L'{')
269 {
270 /* Set caption start */
271 TempStringU.Buffer = ++FormatString;
272
273 /* Get size of the caption */
274 for (Size = 0; *FormatString != 0 && *FormatString != L'}'; Size++)
275 FormatString++;
276
277 /* Skip '}', '\r', '\n' */
278 FormatString += 3;
279
280 TempStringU.Length = Size * sizeof(WCHAR);
281 TempStringU.MaximumLength = TempStringU.Length;
282 }
283 else
284 {
285 /* FIXME: Set string based on severity */
286 RtlInitUnicodeString(&TempStringU, L"Application Error");
287 }
288
289 /* Calculate buffer length for the caption */
290 CaptionStringU->MaximumLength = FileNameU.Length + TempStringU.Length +
291 4 * sizeof(WCHAR);
292
293 /* Allocate a buffer for the caption */
294 CaptionStringU->Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
295 HEAP_ZERO_MEMORY,
296 CaptionStringU->MaximumLength);
297
298 /* Append the file name, seperator and the caption text */
299 CaptionStringU->Length = 0;
300 RtlAppendUnicodeStringToString(CaptionStringU, &FileNameU);
301 RtlAppendUnicodeToString(CaptionStringU, L" - ");
302 RtlAppendUnicodeStringToString(CaptionStringU, &TempStringU);
303
304 /* Zero terminate the buffer */
305 CaptionStringU->Buffer[CaptionStringU->Length] = 0;
306
307 /* Free the file name buffer */
308 RtlFreeUnicodeString(&FileNameU);
309
310 /* Check if this is an exception message */
311 if (Message->Status == STATUS_UNHANDLED_EXCEPTION)
312 {
313 ExceptionCode = Parameters[0];
314
315 /* Handle special cases */
316 if (ExceptionCode == STATUS_ACCESS_VIOLATION)
317 {
318 Parameters[0] = Parameters[1];
319 Parameters[1] = Parameters[3];
320 if (Parameters[2]) Parameters[2] = (ULONG_PTR)L"written";
321 else Parameters[2] = (ULONG_PTR)L"read";
322 MessageResource = NULL;
323 }
324 else if (ExceptionCode == STATUS_IN_PAGE_ERROR)
325 {
326 Parameters[0] = Parameters[1];
327 Parameters[1] = Parameters[3];
328 MessageResource = NULL;
329 }
330 else
331 {
332 /* Fall back to hardcoded value */
333 Parameters[2] = Parameters[1];
334 Parameters[1] = Parameters[0];
335 Parameters[0] = (ULONG_PTR)L"unknown software exception";
336 }
337
338 if (!MessageResource)
339 {
340 /* Get text string of the exception code */
341 Status = RtlFindMessage(GetModuleHandleW(L"ntdll"),
342 (ULONG_PTR)RT_MESSAGETABLE,
343 LANG_NEUTRAL,
344 ExceptionCode,
345 &MessageResource);
346
347 if (NT_SUCCESS(Status))
348 {
349 if (FormatA.Buffer) RtlFreeUnicodeString(&FormatU);
350
351 if (MessageResource->Flags)
352 {
353 RtlInitUnicodeString(&FormatU, (PWSTR)MessageResource->Text);
354 FormatA.Buffer = NULL;
355 }
356 else
357 {
358 RtlInitAnsiString(&FormatA, MessageResource->Text);
359 RtlAnsiStringToUnicodeString(&FormatU, &FormatA, TRUE);
360 }
361 FormatString = FormatU.Buffer;
362 }
363 else
364 {
365 /* Fall back to hardcoded value */
366 Parameters[2] = Parameters[1];
367 Parameters[1] = Parameters[0];
368 Parameters[0] = (ULONG_PTR)L"unknown software exception";
369 }
370 }
371 }
372
373 /* Calculate length of text buffer */
374 TextStringU->MaximumLength = FormatU.Length + SizeOfStrings + 42 * sizeof(WCHAR);
375
376 /* Allocate a buffer for the text */
377 TextStringU->Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
378 HEAP_ZERO_MEMORY,
379 TextStringU->MaximumLength);
380
381 /* Wrap in SEH to protect from invalid string parameters */
382 _SEH2_TRY
383 {
384 /* Print the string into the buffer */
385 StringCbPrintfW(TextStringU->Buffer,
386 TextStringU->MaximumLength,
387 FormatString,
388 Parameters[0],
389 Parameters[1],
390 Parameters[2],
391 Parameters[3],
392 Parameters[4]);
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