- Revert r52573
[reactos.git] / dll / win32 / kernel32 / client / except.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS system libraries
5 * FILE: lib/kernel32/misc/except.c
6 * PURPOSE: Exception functions
7 * PROGRAMMER: Ariadne ( ariadne@xs4all.nl)
8 * modified from WINE [ Onno Hovers, (onno@stack.urc.tue.nl) ]
9 * UPDATE HISTORY:
10 * Created 01/11/98
11 */
12
13 #include <k32.h>
14
15 #define NDEBUG
16 #include <debug.h>
17
18 LPTOP_LEVEL_EXCEPTION_FILTER GlobalTopLevelExceptionFilter = NULL;
19 DWORD g_dwLastErrorToBreakOn;
20
21 /*
22 * Private helper function to lookup the module name from a given address.
23 * The address can point to anywhere within the module.
24 */
25 static const char*
26 _module_name_from_addr(const void* addr, void **module_start_addr,
27 char* psz, size_t nChars)
28 {
29 MEMORY_BASIC_INFORMATION mbi;
30 if (VirtualQuery(addr, &mbi, sizeof(mbi)) != sizeof(mbi) ||
31 !GetModuleFileNameA((HMODULE)mbi.AllocationBase, psz, nChars))
32 {
33 psz[0] = '\0';
34 *module_start_addr = 0;
35 }
36 else
37 {
38 *module_start_addr = (void *)mbi.AllocationBase;
39 }
40 return psz;
41 }
42
43
44 static VOID
45 _dump_context(PCONTEXT pc)
46 {
47 #ifdef _M_IX86
48 /*
49 * Print out the CPU registers
50 */
51 DbgPrint("CS:EIP %x:%x\n", pc->SegCs&0xffff, pc->Eip );
52 DbgPrint("DS %x ES %x FS %x GS %x\n", pc->SegDs&0xffff, pc->SegEs&0xffff,
53 pc->SegFs&0xffff, pc->SegGs&0xfff);
54 DbgPrint("EAX: %.8x EBX: %.8x ECX: %.8x\n", pc->Eax, pc->Ebx, pc->Ecx);
55 DbgPrint("EDX: %.8x EBP: %.8x ESI: %.8x ESP: %.8x\n", pc->Edx,
56 pc->Ebp, pc->Esi, pc->Esp);
57 DbgPrint("EDI: %.8x EFLAGS: %.8x\n", pc->Edi, pc->EFlags);
58 #elif defined(_M_AMD64)
59 DbgPrint("CS:RIP %x:%I64x\n", pc->SegCs&0xffff, pc->Rip );
60 DbgPrint("DS %x ES %x FS %x GS %x\n", pc->SegDs&0xffff, pc->SegEs&0xffff,
61 pc->SegFs&0xffff, pc->SegGs&0xfff);
62 DbgPrint("RAX: %I64x RBX: %I64x RCX: %I64x RDI: %I64x\n", pc->Rax, pc->Rbx, pc->Rcx, pc->Rdi);
63 DbgPrint("RDX: %I64x RBP: %I64x RSI: %I64x RSP: %I64x\n", pc->Rdx, pc->Rbp, pc->Rsi, pc->Rsp);
64 DbgPrint("R8: %I64x R9: %I64x R10: %I64x R11: %I64x\n", pc->R8, pc->R9, pc->R10, pc->R11);
65 DbgPrint("R12: %I64x R13: %I64x R14: %I64x R15: %I64x\n", pc->R12, pc->R13, pc->R14, pc->R15);
66 DbgPrint("EFLAGS: %.8x\n", pc->EFlags);
67 #else
68 #warning Unknown architecture
69 #endif
70 }
71
72 static LONG
73 BasepCheckForReadOnlyResource(IN PVOID Ptr)
74 {
75 PVOID Data;
76 ULONG Size, OldProtect;
77 MEMORY_BASIC_INFORMATION mbi;
78 NTSTATUS Status;
79 LONG Ret = EXCEPTION_CONTINUE_SEARCH;
80
81 /* Check if it was an attempt to write to a read-only image section! */
82 Status = NtQueryVirtualMemory(NtCurrentProcess(),
83 Ptr,
84 MemoryBasicInformation,
85 &mbi,
86 sizeof(mbi),
87 NULL);
88 if (NT_SUCCESS(Status) &&
89 mbi.Protect == PAGE_READONLY && mbi.Type == MEM_IMAGE)
90 {
91 /* Attempt to treat it as a resource section. We need to
92 use SEH here because we don't know if it's actually a
93 resource mapping */
94
95 _SEH2_TRY
96 {
97 Data = RtlImageDirectoryEntryToData(mbi.AllocationBase,
98 TRUE,
99 IMAGE_DIRECTORY_ENTRY_RESOURCE,
100 &Size);
101
102 if (Data != NULL &&
103 (ULONG_PTR)Ptr >= (ULONG_PTR)Data &&
104 (ULONG_PTR)Ptr < (ULONG_PTR)Data + Size)
105 {
106 /* The user tried to write into the resources. Make the page
107 writable... */
108 Size = 1;
109 Status = NtProtectVirtualMemory(NtCurrentProcess(),
110 &Ptr,
111 &Size,
112 PAGE_READWRITE,
113 &OldProtect);
114 if (NT_SUCCESS(Status))
115 {
116 Ret = EXCEPTION_CONTINUE_EXECUTION;
117 }
118 }
119 }
120 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
121 {
122 }
123 _SEH2_END;
124 }
125
126 return Ret;
127 }
128
129 static VOID
130 PrintStackTrace(struct _EXCEPTION_POINTERS *ExceptionInfo)
131 {
132 PVOID StartAddr;
133 CHAR szMod[128] = "";
134 PEXCEPTION_RECORD ExceptionRecord = ExceptionInfo->ExceptionRecord;
135 PCONTEXT ContextRecord = ExceptionInfo->ContextRecord;
136
137 /* Print a stack trace. */
138 DbgPrint("Unhandled exception\n");
139 DbgPrint("ExceptionCode: %8x\n", ExceptionRecord->ExceptionCode);
140
141 if ((NTSTATUS)ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION &&
142 ExceptionRecord->NumberParameters == 2)
143 {
144 DbgPrint("Faulting Address: %8x\n", ExceptionRecord->ExceptionInformation[1]);
145 }
146
147 _dump_context (ContextRecord);
148 _module_name_from_addr(ExceptionRecord->ExceptionAddress, &StartAddr, szMod, sizeof(szMod));
149 DbgPrint("Address:\n %8x+%-8x %s\n",
150 (PVOID)StartAddr,
151 (ULONG_PTR)ExceptionRecord->ExceptionAddress - (ULONG_PTR)StartAddr,
152 szMod);
153 #ifdef _M_IX86
154 DbgPrint("Frames:\n");
155
156 _SEH2_TRY
157 {
158 UINT i;
159 PULONG Frame = (PULONG)ContextRecord->Ebp;
160
161 for (i = 0; Frame[1] != 0 && Frame[1] != 0xdeadbeef && i < 128; i++)
162 {
163 if (IsBadReadPtr((PVOID)Frame[1], 4))
164 {
165 DbgPrint(" %8x%9s %s\n", Frame[1], "<invalid address>"," ");
166 }
167 else
168 {
169 _module_name_from_addr((const void*)Frame[1], &StartAddr,
170 szMod, sizeof(szMod));
171 DbgPrint(" %8x+%-8x %s\n",
172 (PVOID)StartAddr,
173 (ULONG_PTR)Frame[1] - (ULONG_PTR)StartAddr,
174 szMod);
175 }
176
177 if (IsBadReadPtr((PVOID)Frame[0], sizeof(*Frame) * 2))
178 break;
179
180 Frame = (PULONG)Frame[0];
181 }
182 }
183 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
184 {
185 DbgPrint("<error dumping stack trace: 0x%x>\n", _SEH2_GetExceptionCode());
186 }
187 _SEH2_END;
188 #endif
189 }
190
191 UINT
192 WINAPI
193 GetErrorMode(VOID)
194 {
195 NTSTATUS Status;
196 UINT ErrMode;
197
198 /* Query the current setting */
199 Status = NtQueryInformationProcess(NtCurrentProcess(),
200 ProcessDefaultHardErrorMode,
201 (PVOID)&ErrMode,
202 sizeof(ErrMode),
203 NULL);
204 if (!NT_SUCCESS(Status))
205 {
206 /* Fail if we couldn't query */
207 BaseSetLastNTError(Status);
208 return 0;
209 }
210
211 /* Check if NOT failing critical errors was requested */
212 if (ErrMode & SEM_FAILCRITICALERRORS)
213 {
214 /* Mask it out, since the native API works differently */
215 ErrMode &= ~SEM_FAILCRITICALERRORS;
216 }
217 else
218 {
219 /* OR it if the caller didn't, due to different native semantics */
220 ErrMode |= SEM_FAILCRITICALERRORS;
221 }
222
223 /* Return the mode */
224 return ErrMode;
225 }
226
227 /*
228 * @implemented
229 */
230 LONG WINAPI
231 UnhandledExceptionFilter(struct _EXCEPTION_POINTERS *ExceptionInfo)
232 {
233 LONG RetValue;
234 HANDLE DebugPort = NULL;
235 NTSTATUS ErrCode;
236 ULONG_PTR ErrorParameters[4];
237 ULONG ErrorResponse;
238 PEXCEPTION_RECORD ExceptionRecord = ExceptionInfo->ExceptionRecord;
239
240 if ((NTSTATUS)ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION &&
241 ExceptionRecord->NumberParameters >= 2)
242 {
243 switch(ExceptionRecord->ExceptionInformation[0])
244 {
245 case EXCEPTION_WRITE_FAULT:
246 /* Change the protection on some write attempts, some InstallShield setups
247 have this bug */
248 RetValue = BasepCheckForReadOnlyResource(
249 (PVOID)ExceptionRecord->ExceptionInformation[1]);
250 if (RetValue == EXCEPTION_CONTINUE_EXECUTION)
251 return EXCEPTION_CONTINUE_EXECUTION;
252 break;
253 case EXCEPTION_EXECUTE_FAULT:
254 /* FIXME */
255 break;
256 }
257 }
258
259 /* Is there a debugger running ? */
260 ErrCode = NtQueryInformationProcess(NtCurrentProcess(), ProcessDebugPort,
261 &DebugPort, sizeof(HANDLE), NULL);
262 if (!NT_SUCCESS(ErrCode) && ErrCode != STATUS_NOT_IMPLEMENTED)
263 {
264 BaseSetLastNTError(ErrCode);
265 return EXCEPTION_EXECUTE_HANDLER;
266 }
267
268 if (DebugPort)
269 {
270 /* Pass the exception to debugger. */
271 DPRINT("Passing exception to debugger\n");
272 return EXCEPTION_CONTINUE_SEARCH;
273 }
274
275 if (GlobalTopLevelExceptionFilter)
276 {
277 LONG ret = GlobalTopLevelExceptionFilter(ExceptionInfo);
278 if (ret != EXCEPTION_CONTINUE_SEARCH)
279 return ret;
280 }
281
282 if ((GetErrorMode() & SEM_NOGPFAULTERRORBOX) == 0)
283 PrintStackTrace(ExceptionInfo);
284
285 /* Save exception code and address */
286 ErrorParameters[0] = (ULONG)ExceptionRecord->ExceptionCode;
287 ErrorParameters[1] = (ULONG_PTR)ExceptionRecord->ExceptionAddress;
288
289 if ((NTSTATUS)ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION)
290 {
291 /* get the type of operation that caused the access violation */
292 ErrorParameters[2] = ExceptionRecord->ExceptionInformation[0];
293 }
294 else
295 {
296 ErrorParameters[2] = ExceptionRecord->ExceptionInformation[2];
297 }
298
299 /* Save faulting address */
300 ErrorParameters[3] = ExceptionRecord->ExceptionInformation[1];
301
302 /* Raise the harderror */
303 ErrCode = NtRaiseHardError(STATUS_UNHANDLED_EXCEPTION | 0x10000000,
304 4, 0, ErrorParameters, OptionOkCancel, &ErrorResponse);
305
306 if (NT_SUCCESS(ErrCode) && (ErrorResponse == ResponseCancel))
307 {
308 /* FIXME: Check the result, if the "Cancel" button was
309 clicked run a debugger */
310 DPRINT1("Debugging is not implemented yet\n");
311 }
312
313 /*
314 * Returning EXCEPTION_EXECUTE_HANDLER means that the code in
315 * the __except block will be executed. Normally this will end up in a
316 * Terminate process.
317 */
318
319 return EXCEPTION_EXECUTE_HANDLER;
320 }
321
322 /*
323 * @implemented
324 */
325 VOID
326 WINAPI
327 RaiseException(IN DWORD dwExceptionCode,
328 IN DWORD dwExceptionFlags,
329 IN DWORD nNumberOfArguments,
330 IN CONST ULONG_PTR *lpArguments OPTIONAL)
331 {
332 EXCEPTION_RECORD ExceptionRecord;
333
334 /* Setup the exception record */
335 ExceptionRecord.ExceptionCode = dwExceptionCode;
336 ExceptionRecord.ExceptionRecord = NULL;
337 ExceptionRecord.ExceptionAddress = (PVOID)RaiseException;
338 ExceptionRecord.ExceptionFlags = dwExceptionFlags & EXCEPTION_NONCONTINUABLE;
339
340 /* Check if we have arguments */
341 if (!lpArguments)
342 {
343 /* We don't */
344 ExceptionRecord.NumberParameters = 0;
345 }
346 else
347 {
348 /* We do, normalize the count */
349 if (nNumberOfArguments > EXCEPTION_MAXIMUM_PARAMETERS)
350 {
351 nNumberOfArguments = EXCEPTION_MAXIMUM_PARAMETERS;
352 }
353
354 /* Set the count of parameters */
355 ExceptionRecord.NumberParameters = nNumberOfArguments;
356
357 /* Loop each parameter */
358 for (nNumberOfArguments = 0;
359 (nNumberOfArguments < ExceptionRecord.NumberParameters);
360 nNumberOfArguments ++)
361 {
362 /* Copy the exception information */
363 ExceptionRecord.ExceptionInformation[nNumberOfArguments] =
364 *lpArguments++;
365 }
366 }
367
368 if (dwExceptionCode == 0xeedface || dwExceptionCode == 0xeedfade)
369 {
370 DPRINT1("Delphi Exception at address: %p\n", ExceptionRecord.ExceptionInformation[0]);
371 DPRINT1("Exception-Object: %p\n", ExceptionRecord.ExceptionInformation[1]);
372 DPRINT1("Exception text: %s\n", ExceptionRecord.ExceptionInformation[2]);
373 }
374
375 /* Trace the wine special error and show the modulename and functionname */
376 if (dwExceptionCode == 0x80000100 /*EXCEPTION_WINE_STUB*/)
377 {
378 /* Numbers of parameter must be equal to two */
379 if (ExceptionRecord.NumberParameters == 2)
380 {
381 DPRINT1("Missing function in : %s\n", ExceptionRecord.ExceptionInformation[0]);
382 DPRINT1("with the functionname : %s\n", ExceptionRecord.ExceptionInformation[1]);
383 }
384 }
385
386 /* Raise the exception */
387 RtlRaiseException(&ExceptionRecord);
388 }
389
390 /*
391 * @implemented
392 */
393 UINT
394 WINAPI
395 SetErrorMode(IN UINT uMode)
396 {
397 UINT PrevErrMode, NewMode;
398 NTSTATUS Status;
399
400 /* Get the previous mode */
401 PrevErrMode = GetErrorMode();
402 NewMode = uMode;
403
404 /* Check if failing critical errors was requested */
405 if (NewMode & SEM_FAILCRITICALERRORS)
406 {
407 /* Mask it out, since the native API works differently */
408 NewMode &= ~SEM_FAILCRITICALERRORS;
409 }
410 else
411 {
412 /* OR it if the caller didn't, due to different native semantics */
413 NewMode |= SEM_FAILCRITICALERRORS;
414 }
415
416 /* Set the new mode */
417 Status = NtSetInformationProcess(NtCurrentProcess(),
418 ProcessDefaultHardErrorMode,
419 (PVOID)&NewMode,
420 sizeof(NewMode));
421 if(!NT_SUCCESS(Status)) BaseSetLastNTError(Status);
422
423 /* Return the previous mode */
424 return PrevErrMode;
425 }
426
427 /*
428 * @implemented
429 */
430 LPTOP_LEVEL_EXCEPTION_FILTER
431 WINAPI
432 SetUnhandledExceptionFilter(
433 IN LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
434 {
435 return InterlockedExchangePointer(&GlobalTopLevelExceptionFilter,
436 lpTopLevelExceptionFilter);
437 }
438
439 /*
440 * @implemented
441 */
442 BOOL
443 WINAPI
444 IsBadReadPtr(IN LPCVOID lp,
445 IN UINT_PTR ucb)
446 {
447 ULONG PageSize;
448 BOOLEAN Result = FALSE;
449 volatile CHAR *Current;
450 PCHAR Last;
451
452 /* Quick cases */
453 if (!ucb) return FALSE;
454 if (!lp) return TRUE;
455
456 /* Get the page size */
457 PageSize = BaseStaticServerData->SysInfo.PageSize;
458
459 /* Calculate the last page */
460 Last = (PCHAR)((ULONG_PTR)lp + ucb - 1);
461
462 /* Another quick failure case */
463 if ((ULONG_PTR)Last < (ULONG_PTR)lp) return TRUE;
464
465 /* Enter SEH */
466 _SEH2_TRY
467 {
468 /* Probe the entire range */
469 Current = (volatile CHAR*)lp;
470 Last = (PCHAR)(PAGE_ROUND_DOWN(Last));
471 do
472 {
473 *Current;
474 Current = (volatile CHAR*)(PAGE_ROUND_DOWN(Current) + PAGE_SIZE);
475 } while (Current <= Last);
476 }
477 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
478 {
479 /* We hit an exception, so return true */
480 Result = TRUE;
481 }
482 _SEH2_END
483
484 /* Return exception status */
485 return Result;
486 }
487
488 /*
489 * @implemented
490 */
491 BOOL
492 NTAPI
493 IsBadHugeReadPtr(LPCVOID lp,
494 UINT_PTR ucb)
495 {
496 /* Implementation is the same on 32-bit */
497 return IsBadReadPtr(lp, ucb);
498 }
499
500 /*
501 * @implemented
502 */
503 BOOL
504 NTAPI
505 IsBadCodePtr(FARPROC lpfn)
506 {
507 /* Executing has the same privileges as reading */
508 return IsBadReadPtr((LPVOID)lpfn, 1);
509 }
510
511 /*
512 * @implemented
513 */
514 BOOL
515 NTAPI
516 IsBadWritePtr(LPVOID lp,
517 UINT_PTR ucb)
518 {
519 ULONG PageSize;
520 BOOLEAN Result = FALSE;
521 volatile CHAR *Current;
522 PCHAR Last;
523
524 /* Quick cases */
525 if (!ucb) return FALSE;
526 if (!lp) return TRUE;
527
528 /* Get the page size */
529 PageSize = BaseStaticServerData->SysInfo.PageSize;
530
531 /* Calculate the last page */
532 Last = (PCHAR)((ULONG_PTR)lp + ucb - 1);
533
534 /* Another quick failure case */
535 if ((ULONG_PTR)Last < (ULONG_PTR)lp) return TRUE;
536
537 /* Enter SEH */
538 _SEH2_TRY
539 {
540 /* Probe the entire range */
541 Current = (volatile CHAR*)lp;
542 Last = (PCHAR)(PAGE_ROUND_DOWN(Last));
543 do
544 {
545 *Current = *Current;
546 Current = (volatile CHAR*)(PAGE_ROUND_DOWN(Current) + PAGE_SIZE);
547 } while (Current <= Last);
548 }
549 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
550 {
551 /* We hit an exception, so return true */
552 Result = TRUE;
553 }
554 _SEH2_END
555
556 /* Return exception status */
557 return Result;
558 }
559
560 /*
561 * @implemented
562 */
563 BOOL
564 NTAPI
565 IsBadHugeWritePtr(LPVOID lp,
566 UINT_PTR ucb)
567 {
568 /* Implementation is the same on 32-bit */
569 return IsBadWritePtr(lp, ucb);
570 }
571
572 /*
573 * @implemented
574 */
575 BOOL
576 NTAPI
577 IsBadStringPtrW(IN LPCWSTR lpsz,
578 UINT_PTR ucchMax)
579 {
580 BOOLEAN Result = FALSE;
581 volatile WCHAR *Current;
582 PWCHAR Last;
583 WCHAR Char;
584
585 /* Quick cases */
586 if (!ucchMax) return FALSE;
587 if (!lpsz) return TRUE;
588
589 /* Calculate the last page */
590 Last = (PWCHAR)((ULONG_PTR)lpsz + (ucchMax * 2) - 2);
591
592 /* Enter SEH */
593 _SEH2_TRY
594 {
595 /* Probe the entire range */
596 Current = (volatile WCHAR*)lpsz;
597 Last = (PWCHAR)(PAGE_ROUND_DOWN(Last));
598 do
599 {
600 Char = *Current;
601 Current++;
602 } while (Char && (Current != Last + 1));
603 }
604 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
605 {
606 /* We hit an exception, so return true */
607 Result = TRUE;
608 }
609 _SEH2_END
610
611 /* Return exception status */
612 return Result;
613 }
614
615 /*
616 * @implemented
617 */
618 BOOL
619 NTAPI
620 IsBadStringPtrA(IN LPCSTR lpsz,
621 UINT_PTR ucchMax)
622 {
623 BOOLEAN Result = FALSE;
624 volatile CHAR *Current;
625 PCHAR Last;
626 CHAR Char;
627
628 /* Quick cases */
629 if (!ucchMax) return FALSE;
630 if (!lpsz) return TRUE;
631
632 /* Calculate the last page */
633 Last = (PCHAR)((ULONG_PTR)lpsz + ucchMax - 1);
634
635 /* Enter SEH */
636 _SEH2_TRY
637 {
638 /* Probe the entire range */
639 Current = (volatile CHAR*)lpsz;
640 Last = (PCHAR)(PAGE_ROUND_DOWN(Last));
641 do
642 {
643 Char = *Current;
644 Current++;
645 } while (Char && (Current != Last + 1));
646 }
647 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
648 {
649 /* We hit an exception, so return true */
650 Result = TRUE;
651 }
652 _SEH2_END
653
654 /* Return exception status */
655 return Result;
656 }
657
658 /*
659 * @implemented
660 */
661 VOID
662 WINAPI
663 SetLastError(IN DWORD dwErrCode)
664 {
665 /* Break if a debugger requested checking for this error code */
666 if ((g_dwLastErrorToBreakOn) && (g_dwLastErrorToBreakOn == dwErrCode)) DbgBreakPoint();
667
668 /* Set last error if it's a new error */
669 if (NtCurrentTeb()->LastErrorValue != dwErrCode) NtCurrentTeb()->LastErrorValue = dwErrCode;
670 }
671
672 /*
673 * @implemented
674 */
675 VOID
676 WINAPI
677 BaseSetLastNTError(IN NTSTATUS Status)
678 {
679 /* Convert from NT to Win32, then set */
680 SetLastError(RtlNtStatusToDosError(Status));
681 }
682
683 /*
684 * @implemented
685 */
686 DWORD
687 WINAPI
688 GetLastError(VOID)
689 {
690 /* Return the current value */
691 return NtCurrentTeb()->LastErrorValue;
692 }
693
694 /* EOF */