Start source tree (final, I hope!) restructuration. Part 1/X
[reactos.git] / reactos / win32ss / base / 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: %lx\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 DECLSPEC_HOTPATCH
435 SetUnhandledExceptionFilter(IN LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
436 {
437 PVOID EncodedPointer, EncodedOldPointer;
438
439 EncodedPointer = RtlEncodePointer(lpTopLevelExceptionFilter);
440 EncodedOldPointer = InterlockedExchangePointer((PVOID*)&GlobalTopLevelExceptionFilter,
441 EncodedPointer);
442 return RtlDecodePointer(EncodedOldPointer);
443 }
444
445 /*
446 * @implemented
447 */
448 BOOL
449 WINAPI
450 IsBadReadPtr(IN LPCVOID lp,
451 IN UINT_PTR ucb)
452 {
453 ULONG PageSize;
454 BOOLEAN Result = FALSE;
455 volatile CHAR *Current;
456 PCHAR Last;
457
458 /* Quick cases */
459 if (!ucb) return FALSE;
460 if (!lp) return TRUE;
461
462 /* Get the page size */
463 PageSize = BaseStaticServerData->SysInfo.PageSize;
464
465 /* Calculate start and end */
466 Current = (volatile CHAR*)lp;
467 Last = (PCHAR)((ULONG_PTR)lp + ucb - 1);
468
469 /* Another quick failure case */
470 if (Last < Current) return TRUE;
471
472 /* Enter SEH */
473 _SEH2_TRY
474 {
475 /* Do an initial probe */
476 *Current;
477
478 /* Align the addresses */
479 Current = (volatile CHAR *)ROUND_DOWN(Current, PageSize);
480 Last = (PCHAR)ROUND_DOWN(Last, PageSize);
481
482 /* Probe the entire range */
483 while (Current != Last)
484 {
485 Current += PageSize;
486 *Current;
487 }
488 }
489 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
490 {
491 /* We hit an exception, so return true */
492 Result = TRUE;
493 }
494 _SEH2_END
495
496 /* Return exception status */
497 return Result;
498 }
499
500 /*
501 * @implemented
502 */
503 BOOL
504 NTAPI
505 IsBadHugeReadPtr(LPCVOID lp,
506 UINT_PTR ucb)
507 {
508 /* Implementation is the same on 32-bit */
509 return IsBadReadPtr(lp, ucb);
510 }
511
512 /*
513 * @implemented
514 */
515 BOOL
516 NTAPI
517 IsBadCodePtr(FARPROC lpfn)
518 {
519 /* Executing has the same privileges as reading */
520 return IsBadReadPtr((LPVOID)lpfn, 1);
521 }
522
523 /*
524 * @implemented
525 */
526 BOOL
527 NTAPI
528 IsBadWritePtr(IN LPVOID lp,
529 IN UINT_PTR ucb)
530 {
531 ULONG PageSize;
532 BOOLEAN Result = FALSE;
533 volatile CHAR *Current;
534 PCHAR Last;
535
536 /* Quick cases */
537 if (!ucb) return FALSE;
538 if (!lp) return TRUE;
539
540 /* Get the page size */
541 PageSize = BaseStaticServerData->SysInfo.PageSize;
542
543 /* Calculate start and end */
544 Current = (volatile CHAR*)lp;
545 Last = (PCHAR)((ULONG_PTR)lp + ucb - 1);
546
547 /* Another quick failure case */
548 if (Last < Current) return TRUE;
549
550 /* Enter SEH */
551 _SEH2_TRY
552 {
553 /* Do an initial probe */
554 *Current = *Current;
555
556 /* Align the addresses */
557 Current = (volatile CHAR *)ROUND_DOWN(Current, PageSize);
558 Last = (PCHAR)ROUND_DOWN(Last, PageSize);
559
560 /* Probe the entire range */
561 while (Current != Last)
562 {
563 Current += PageSize;
564 *Current = *Current;
565 }
566 }
567 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
568 {
569 /* We hit an exception, so return true */
570 Result = TRUE;
571 }
572 _SEH2_END
573
574 /* Return exception status */
575 return Result;
576 }
577
578 /*
579 * @implemented
580 */
581 BOOL
582 NTAPI
583 IsBadHugeWritePtr(IN LPVOID lp,
584 IN UINT_PTR ucb)
585 {
586 /* Implementation is the same on 32-bit */
587 return IsBadWritePtr(lp, ucb);
588 }
589
590 /*
591 * @implemented
592 */
593 BOOL
594 NTAPI
595 IsBadStringPtrW(IN LPCWSTR lpsz,
596 IN UINT_PTR ucchMax)
597 {
598 BOOLEAN Result = FALSE;
599 volatile WCHAR *Current;
600 PWCHAR Last;
601 WCHAR Char;
602
603 /* Quick cases */
604 if (!ucchMax) return FALSE;
605 if (!lpsz) return TRUE;
606
607 /* Calculate start and end */
608 Current = (volatile WCHAR*)lpsz;
609 Last = (PWCHAR)((ULONG_PTR)lpsz + (ucchMax * 2) - 2);
610
611 /* Enter SEH */
612 _SEH2_TRY
613 {
614 /* Probe the entire range */
615 Char = *Current++;
616 while ((Char) && (Current != Last)) Char = *Current++;
617 }
618 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
619 {
620 /* We hit an exception, so return true */
621 Result = TRUE;
622 }
623 _SEH2_END
624
625 /* Return exception status */
626 return Result;
627 }
628
629 /*
630 * @implemented
631 */
632 BOOL
633 NTAPI
634 IsBadStringPtrA(IN LPCSTR lpsz,
635 IN UINT_PTR ucchMax)
636 {
637 BOOLEAN Result = FALSE;
638 volatile CHAR *Current;
639 PCHAR Last;
640 CHAR Char;
641
642 /* Quick cases */
643 if (!ucchMax) return FALSE;
644 if (!lpsz) return TRUE;
645
646 /* Calculate start and end */
647 Current = (volatile CHAR*)lpsz;
648 Last = (PCHAR)((ULONG_PTR)lpsz + ucchMax - 1);
649
650 /* Enter SEH */
651 _SEH2_TRY
652 {
653 /* Probe the entire range */
654 Char = *Current++;
655 while ((Char) && (Current != Last)) Char = *Current++;
656 }
657 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
658 {
659 /* We hit an exception, so return true */
660 Result = TRUE;
661 }
662 _SEH2_END
663
664 /* Return exception status */
665 return Result;
666 }
667
668 /*
669 * @implemented
670 */
671 VOID
672 WINAPI
673 SetLastError(IN DWORD dwErrCode)
674 {
675 /* Break if a debugger requested checking for this error code */
676 if ((g_dwLastErrorToBreakOn) && (g_dwLastErrorToBreakOn == dwErrCode)) DbgBreakPoint();
677
678 /* Set last error if it's a new error */
679 if (NtCurrentTeb()->LastErrorValue != dwErrCode) NtCurrentTeb()->LastErrorValue = dwErrCode;
680 }
681
682 /*
683 * @implemented
684 */
685 DWORD
686 WINAPI
687 BaseSetLastNTError(IN NTSTATUS Status)
688 {
689 DWORD dwErrCode;
690
691 /* Convert from NT to Win32, then set */
692 dwErrCode = RtlNtStatusToDosError(Status);
693 SetLastError(dwErrCode);
694 return dwErrCode;
695 }
696
697 /*
698 * @implemented
699 */
700 DWORD
701 WINAPI
702 GetLastError(VOID)
703 {
704 /* Return the current value */
705 return NtCurrentTeb()->LastErrorValue;
706 }
707
708 /* EOF */