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