cfa16c6eb3498cffa773aeb652c733f2e5c0ccde
[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 PMESSAGE_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, (PCHAR)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, (PCHAR)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 Status = STATUS_SUCCESS;
393 }
394 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
395 {
396 /* Set error and free buffers */
397 Status = _SEH2_GetExceptionCode();
398 RtlFreeHeap(RtlGetProcessHeap(), 0, TextStringU->Buffer);
399 RtlFreeHeap(RtlGetProcessHeap(), 0, CaptionStringU->Buffer);
400 }
401 _SEH2_END
402
403 if (NT_SUCCESS(Status))
404 {
405 TextStringU->Length = wcslen(TextStringU->Buffer) * sizeof(WCHAR);
406 }
407
408 if (FormatA.Buffer) RtlFreeUnicodeString(&FormatU);
409
410 return Status;
411 }
412
413 static
414 ULONG
415 CsrpMessageBox(
416 PWSTR Text,
417 PWSTR Caption,
418 ULONG ValidResponseOptions,
419 ULONG Severity)
420 {
421 ULONG Type, MessageBoxResponse;
422
423 /* Set the message box type */
424 switch (ValidResponseOptions)
425 {
426 case OptionAbortRetryIgnore:
427 Type = MB_ABORTRETRYIGNORE;
428 break;
429 case OptionOk:
430 Type = MB_OK;
431 break;
432 case OptionOkCancel:
433 Type = MB_OKCANCEL;
434 break;
435 case OptionRetryCancel:
436 Type = MB_RETRYCANCEL;
437 break;
438 case OptionYesNo:
439 Type = MB_YESNO;
440 break;
441 case OptionYesNoCancel:
442 Type = MB_YESNOCANCEL;
443 break;
444 case OptionShutdownSystem:
445 Type = MB_RETRYCANCEL; // FIXME???
446 break;
447 /* Anything else is invalid */
448 default:
449 return ResponseNotHandled;
450 }
451
452 /* Set severity */
453 if (Severity == STATUS_SEVERITY_INFORMATIONAL) Type |= MB_ICONINFORMATION;
454 else if (Severity == STATUS_SEVERITY_WARNING) Type |= MB_ICONWARNING;
455 else if (Severity == STATUS_SEVERITY_ERROR) Type |= MB_ICONERROR;
456
457 Type |= MB_SYSTEMMODAL | MB_SETFOREGROUND;
458
459 DPRINT("Text = '%S', Caption = '%S', Severity = %d, Type = 0x%lx\n",
460 Text, Caption, Severity, Type);
461
462 /* Display a message box */
463 MessageBoxResponse = MessageBoxW(0, Text, Caption, Type);
464
465 /* Return response value */
466 switch (MessageBoxResponse)
467 {
468 case IDOK: return ResponseOk;
469 case IDCANCEL: return ResponseCancel;
470 case IDYES: return ResponseYes;
471 case IDNO: return ResponseNo;
472 case IDABORT: return ResponseAbort;
473 case IDIGNORE: return ResponseIgnore;
474 case IDRETRY: return ResponseRetry;
475 case IDTRYAGAIN: return ResponseTryAgain;
476 case IDCONTINUE: return ResponseContinue;
477 }
478
479 return ResponseNotHandled;
480 }
481
482 BOOL
483 WINAPI
484 Win32CsrHardError(
485 IN PCSRSS_PROCESS_DATA ProcessData,
486 IN PHARDERROR_MSG Message)
487 {
488 ULONG_PTR Parameters[MAXIMUM_HARDERROR_PARAMETERS];
489 OBJECT_ATTRIBUTES ObjectAttributes;
490 UNICODE_STRING TextU, CaptionU;
491 NTSTATUS Status;
492 HANDLE hProcess;
493 ULONG Size;
494
495 /* Default to not handled */
496 Message->Response = ResponseNotHandled;
497
498 /* Make sure we don't have too many parameters */
499 if (Message->NumberOfParameters > MAXIMUM_HARDERROR_PARAMETERS)
500 Message->NumberOfParameters = MAXIMUM_HARDERROR_PARAMETERS;
501
502 /* Initialize object attributes */
503 InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
504
505 /* Open client process */
506 Status = NtOpenProcess(&hProcess,
507 PROCESS_VM_READ | PROCESS_QUERY_INFORMATION,
508 &ObjectAttributes,
509 &Message->h.ClientId);
510
511 if (!NT_SUCCESS(Status))
512 {
513 DPRINT1("NtOpenProcess failed with code: %lx\n", Status);
514 return FALSE;
515 }
516
517 /* Capture all string parameters from the process memory */
518 Status = CsrpCaptureStringParameters(Parameters, &Size, Message, hProcess);
519 if (!NT_SUCCESS(Status))
520 {
521 NtClose(hProcess);
522 return FALSE;
523 }
524
525 /* Format the caption and message box text */
526 Status = CsrpFormatMessages(&TextU,
527 &CaptionU,
528 Parameters,
529 Size,
530 Message,
531 hProcess);
532
533 /* Cleanup */
534 CsrpFreeStringParameters(Parameters, Message);
535 NtClose(hProcess);
536
537 if (!NT_SUCCESS(Status))
538 {
539 return FALSE;
540 }
541
542 /* Display the message box */
543 Message->Response = CsrpMessageBox(TextU.Buffer,
544 CaptionU.Buffer,
545 Message->ValidResponseOptions,
546 (ULONG)Message->Status >> 30);
547
548 RtlFreeUnicodeString(&TextU);
549 RtlFreeUnicodeString(&CaptionU);
550
551 return TRUE;
552 }
553