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) ]
12 /* INCLUDES *******************************************************************/
20 * Private helper function to lookup the module name from a given address.
21 * The address can point to anywhere within the module.
24 _module_name_from_addr(const void* addr
, void **module_start_addr
,
25 char* psz
, size_t nChars
)
27 MEMORY_BASIC_INFORMATION mbi
;
28 if (VirtualQuery(addr
, &mbi
, sizeof(mbi
)) != sizeof(mbi
) ||
29 !GetModuleFileNameA((HMODULE
)mbi
.AllocationBase
, psz
, nChars
))
32 *module_start_addr
= 0;
36 *module_start_addr
= (void *)mbi
.AllocationBase
;
43 _dump_context(PCONTEXT pc
)
47 * Print out the CPU registers
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
);
66 DbgPrint("PC: %08lx LR: %08lx SP: %08lx\n", pc
->Pc
);
67 DbgPrint("R0: %08lx R1: %08lx R2: %08lx R3: %08lx\n", pc
->R0
, pc
->R1
, pc
->R2
, pc
->R3
);
68 DbgPrint("R4: %08lx R5: %08lx R6: %08lx R7: %08lx\n", pc
->R4
, pc
->R5
, pc
->R6
, pc
->R7
);
69 DbgPrint("R8: %08lx R9: %08lx R10: %08lx R11: %08lx\n", pc
->R8
, pc
->R9
, pc
->R10
, pc
->R11
);
70 DbgPrint("R12: %08lx CPSR: %08lx FPSCR: %08lx\n", pc
->R12
, pc
->Cpsr
, pc
->R1
, pc
->Fpscr
, pc
->R3
);
72 #error "Unknown architecture"
77 PrintStackTrace(struct _EXCEPTION_POINTERS
*ExceptionInfo
)
81 PEXCEPTION_RECORD ExceptionRecord
= ExceptionInfo
->ExceptionRecord
;
82 PCONTEXT ContextRecord
= ExceptionInfo
->ContextRecord
;
84 /* Print a stack trace. */
85 DbgPrint("Unhandled exception\n");
86 DbgPrint("ExceptionCode: %8x\n", ExceptionRecord
->ExceptionCode
);
88 if ((NTSTATUS
)ExceptionRecord
->ExceptionCode
== STATUS_ACCESS_VIOLATION
&&
89 ExceptionRecord
->NumberParameters
== 2)
91 DbgPrint("Faulting Address: %8x\n", ExceptionRecord
->ExceptionInformation
[1]);
94 _dump_context (ContextRecord
);
95 _module_name_from_addr(ExceptionRecord
->ExceptionAddress
, &StartAddr
, szMod
, sizeof(szMod
));
96 DbgPrint("Address:\n %8x+%-8x %s\n",
98 (ULONG_PTR
)ExceptionRecord
->ExceptionAddress
- (ULONG_PTR
)StartAddr
,
101 DbgPrint("Frames:\n");
106 PULONG Frame
= (PULONG
)ContextRecord
->Ebp
;
108 for (i
= 0; Frame
[1] != 0 && Frame
[1] != 0xdeadbeef && i
< 128; i
++)
110 if (IsBadReadPtr((PVOID
)Frame
[1], 4))
112 DbgPrint(" %8x%9s %s\n", Frame
[1], "<invalid address>"," ");
116 _module_name_from_addr((const void*)Frame
[1], &StartAddr
,
117 szMod
, sizeof(szMod
));
118 DbgPrint(" %8x+%-8x %s\n",
120 (ULONG_PTR
)Frame
[1] - (ULONG_PTR
)StartAddr
,
124 if (IsBadReadPtr((PVOID
)Frame
[0], sizeof(*Frame
) * 2))
127 Frame
= (PULONG
)Frame
[0];
130 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
132 DbgPrint("<error dumping stack trace: 0x%x>\n", _SEH2_GetExceptionCode());
138 /* GLOBALS ********************************************************************/
140 LPTOP_LEVEL_EXCEPTION_FILTER GlobalTopLevelExceptionFilter
;
141 DWORD g_dwLastErrorToBreakOn
;
143 /* FUNCTIONS ******************************************************************/
147 BasepCheckForReadOnlyResource(IN PVOID Ptr
)
150 ULONG Size
, OldProtect
;
151 MEMORY_BASIC_INFORMATION mbi
;
153 LONG Ret
= EXCEPTION_CONTINUE_SEARCH
;
155 /* Check if it was an attempt to write to a read-only image section! */
156 Status
= NtQueryVirtualMemory(NtCurrentProcess(),
158 MemoryBasicInformation
,
162 if (NT_SUCCESS(Status
) &&
163 mbi
.Protect
== PAGE_READONLY
&& mbi
.Type
== MEM_IMAGE
)
165 /* Attempt to treat it as a resource section. We need to
166 use SEH here because we don't know if it's actually a
170 Data
= RtlImageDirectoryEntryToData(mbi
.AllocationBase
,
172 IMAGE_DIRECTORY_ENTRY_RESOURCE
,
176 (ULONG_PTR
)Ptr
>= (ULONG_PTR
)Data
&&
177 (ULONG_PTR
)Ptr
< (ULONG_PTR
)Data
+ Size
)
179 /* The user tried to write into the resources. Make the page
182 Status
= NtProtectVirtualMemory(NtCurrentProcess(),
187 if (NT_SUCCESS(Status
))
189 Ret
= EXCEPTION_CONTINUE_EXECUTION
;
193 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
209 /* Query the current setting */
210 Status
= NtQueryInformationProcess(NtCurrentProcess(),
211 ProcessDefaultHardErrorMode
,
215 if (!NT_SUCCESS(Status
))
217 /* Fail if we couldn't query */
218 BaseSetLastNTError(Status
);
222 /* Check if NOT failing critical errors was requested */
223 if (ErrMode
& SEM_FAILCRITICALERRORS
)
225 /* Mask it out, since the native API works differently */
226 ErrMode
&= ~SEM_FAILCRITICALERRORS
;
230 /* OR it if the caller didn't, due to different native semantics */
231 ErrMode
|= SEM_FAILCRITICALERRORS
;
234 /* Return the mode */
242 UnhandledExceptionFilter(struct _EXCEPTION_POINTERS
*ExceptionInfo
)
245 HANDLE DebugPort
= NULL
;
247 ULONG_PTR ErrorParameters
[4];
249 PEXCEPTION_RECORD ExceptionRecord
= ExceptionInfo
->ExceptionRecord
;
250 LPTOP_LEVEL_EXCEPTION_FILTER RealFilter
;
252 if ((NTSTATUS
)ExceptionRecord
->ExceptionCode
== STATUS_ACCESS_VIOLATION
&&
253 ExceptionRecord
->NumberParameters
>= 2)
255 switch(ExceptionRecord
->ExceptionInformation
[0])
257 case EXCEPTION_WRITE_FAULT
:
258 /* Change the protection on some write attempts, some InstallShield setups
260 RetValue
= BasepCheckForReadOnlyResource(
261 (PVOID
)ExceptionRecord
->ExceptionInformation
[1]);
262 if (RetValue
== EXCEPTION_CONTINUE_EXECUTION
)
263 return EXCEPTION_CONTINUE_EXECUTION
;
265 case EXCEPTION_EXECUTE_FAULT
:
271 /* Is there a debugger running ? */
272 ErrCode
= NtQueryInformationProcess(NtCurrentProcess(), ProcessDebugPort
,
273 &DebugPort
, sizeof(HANDLE
), NULL
);
274 if (!NT_SUCCESS(ErrCode
) && ErrCode
!= STATUS_NOT_IMPLEMENTED
)
276 BaseSetLastNTError(ErrCode
);
277 return EXCEPTION_EXECUTE_HANDLER
;
282 /* Pass the exception to debugger. */
283 DPRINT("Passing exception to debugger\n");
284 return EXCEPTION_CONTINUE_SEARCH
;
287 RealFilter
= RtlDecodePointer(GlobalTopLevelExceptionFilter
);
290 LONG ret
= RealFilter(ExceptionInfo
);
291 if (ret
!= EXCEPTION_CONTINUE_SEARCH
)
295 PrintStackTrace(ExceptionInfo
);
297 /* Save exception code and address */
298 ErrorParameters
[0] = (ULONG
)ExceptionRecord
->ExceptionCode
;
299 ErrorParameters
[1] = (ULONG_PTR
)ExceptionRecord
->ExceptionAddress
;
301 if ((NTSTATUS
)ExceptionRecord
->ExceptionCode
== STATUS_ACCESS_VIOLATION
)
303 /* get the type of operation that caused the access violation */
304 ErrorParameters
[2] = ExceptionRecord
->ExceptionInformation
[0];
308 ErrorParameters
[2] = ExceptionRecord
->ExceptionInformation
[2];
311 /* Save faulting address */
312 ErrorParameters
[3] = ExceptionRecord
->ExceptionInformation
[1];
314 /* Raise the harderror */
315 ErrCode
= NtRaiseHardError(STATUS_UNHANDLED_EXCEPTION
,
316 4, 0, ErrorParameters
, OptionOkCancel
, &ErrorResponse
);
318 if (NT_SUCCESS(ErrCode
) && (ErrorResponse
== ResponseCancel
))
320 /* FIXME: Check the result, if the "Cancel" button was
321 clicked run a debugger */
322 DPRINT1("Debugging is not implemented yet\n");
326 * Returning EXCEPTION_EXECUTE_HANDLER means that the code in
327 * the __except block will be executed. Normally this will end up in a
331 return EXCEPTION_EXECUTE_HANDLER
;
339 RaiseException(IN DWORD dwExceptionCode
,
340 IN DWORD dwExceptionFlags
,
341 IN DWORD nNumberOfArguments
,
342 IN CONST ULONG_PTR
*lpArguments OPTIONAL
)
344 EXCEPTION_RECORD ExceptionRecord
;
346 /* Setup the exception record */
347 ExceptionRecord
.ExceptionCode
= dwExceptionCode
;
348 ExceptionRecord
.ExceptionRecord
= NULL
;
349 ExceptionRecord
.ExceptionAddress
= (PVOID
)RaiseException
;
350 ExceptionRecord
.ExceptionFlags
= dwExceptionFlags
& EXCEPTION_NONCONTINUABLE
;
352 /* Check if we have arguments */
356 ExceptionRecord
.NumberParameters
= 0;
360 /* We do, normalize the count */
361 if (nNumberOfArguments
> EXCEPTION_MAXIMUM_PARAMETERS
)
363 nNumberOfArguments
= EXCEPTION_MAXIMUM_PARAMETERS
;
366 /* Set the count of parameters and copy them */
367 ExceptionRecord
.NumberParameters
= nNumberOfArguments
;
368 RtlCopyMemory(ExceptionRecord
.ExceptionInformation
,
370 nNumberOfArguments
* sizeof(ULONG
));
373 /* Better handling of Delphi Exceptions... a ReactOS Hack */
374 if (dwExceptionCode
== 0xeedface || dwExceptionCode
== 0xeedfade)
376 DPRINT1("Delphi Exception at address: %p\n", ExceptionRecord
.ExceptionInformation
[0]);
377 DPRINT1("Exception-Object: %p\n", ExceptionRecord
.ExceptionInformation
[1]);
378 DPRINT1("Exception text: %lx\n", ExceptionRecord
.ExceptionInformation
[2]);
381 /* Trace the wine special error and show the modulename and functionname */
382 if (dwExceptionCode
== 0x80000100 /*EXCEPTION_WINE_STUB*/)
384 /* Numbers of parameter must be equal to two */
385 if (ExceptionRecord
.NumberParameters
== 2)
387 DPRINT1("Missing function in : %s\n", ExceptionRecord
.ExceptionInformation
[0]);
388 DPRINT1("with the functionname : %s\n", ExceptionRecord
.ExceptionInformation
[1]);
392 /* Raise the exception */
393 RtlRaiseException(&ExceptionRecord
);
401 SetErrorMode(IN UINT uMode
)
403 UINT PrevErrMode
, NewMode
;
405 /* Get the previous mode */
406 PrevErrMode
= GetErrorMode();
409 /* Check if failing critical errors was requested */
410 if (NewMode
& SEM_FAILCRITICALERRORS
)
412 /* Mask it out, since the native API works differently */
413 NewMode
&= ~SEM_FAILCRITICALERRORS
;
417 /* OR it if the caller didn't, due to different native semantics */
418 NewMode
|= SEM_FAILCRITICALERRORS
;
421 /* Always keep no alignment faults if they were set */
422 NewMode
|= (PrevErrMode
& SEM_NOALIGNMENTFAULTEXCEPT
);
424 /* Set the new mode */
425 NtSetInformationProcess(NtCurrentProcess(),
426 ProcessDefaultHardErrorMode
,
430 /* Return the previous mode */
437 LPTOP_LEVEL_EXCEPTION_FILTER
440 SetUnhandledExceptionFilter(IN LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter
)
442 PVOID EncodedPointer
, EncodedOldPointer
;
444 EncodedPointer
= RtlEncodePointer(lpTopLevelExceptionFilter
);
445 EncodedOldPointer
= InterlockedExchangePointer((PVOID
*)&GlobalTopLevelExceptionFilter
,
447 return RtlDecodePointer(EncodedOldPointer
);
455 IsBadReadPtr(IN LPCVOID lp
,
459 BOOLEAN Result
= FALSE
;
460 volatile CHAR
*Current
;
464 if (!ucb
) return FALSE
;
465 if (!lp
) return TRUE
;
467 /* Get the page size */
468 PageSize
= BaseStaticServerData
->SysInfo
.PageSize
;
470 /* Calculate start and end */
471 Current
= (volatile CHAR
*)lp
;
472 Last
= (PCHAR
)((ULONG_PTR
)lp
+ ucb
- 1);
474 /* Another quick failure case */
475 if (Last
< Current
) return TRUE
;
480 /* Do an initial probe */
483 /* Align the addresses */
484 Current
= (volatile CHAR
*)ROUND_DOWN(Current
, PageSize
);
485 Last
= (PCHAR
)ROUND_DOWN(Last
, PageSize
);
487 /* Probe the entire range */
488 while (Current
!= Last
)
494 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
496 /* We hit an exception, so return true */
501 /* Return exception status */
510 IsBadHugeReadPtr(LPCVOID lp
,
513 /* Implementation is the same on 32-bit */
514 return IsBadReadPtr(lp
, ucb
);
522 IsBadCodePtr(FARPROC lpfn
)
524 /* Executing has the same privileges as reading */
525 return IsBadReadPtr((LPVOID
)lpfn
, 1);
533 IsBadWritePtr(IN LPVOID lp
,
537 BOOLEAN Result
= FALSE
;
538 volatile CHAR
*Current
;
542 if (!ucb
) return FALSE
;
543 if (!lp
) return TRUE
;
545 /* Get the page size */
546 PageSize
= BaseStaticServerData
->SysInfo
.PageSize
;
548 /* Calculate start and end */
549 Current
= (volatile CHAR
*)lp
;
550 Last
= (PCHAR
)((ULONG_PTR
)lp
+ ucb
- 1);
552 /* Another quick failure case */
553 if (Last
< Current
) return TRUE
;
558 /* Do an initial probe */
561 /* Align the addresses */
562 Current
= (volatile CHAR
*)ROUND_DOWN(Current
, PageSize
);
563 Last
= (PCHAR
)ROUND_DOWN(Last
, PageSize
);
565 /* Probe the entire range */
566 while (Current
!= Last
)
572 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
574 /* We hit an exception, so return true */
579 /* Return exception status */
588 IsBadHugeWritePtr(IN LPVOID lp
,
591 /* Implementation is the same on 32-bit */
592 return IsBadWritePtr(lp
, ucb
);
600 IsBadStringPtrW(IN LPCWSTR lpsz
,
603 BOOLEAN Result
= FALSE
;
604 volatile WCHAR
*Current
;
609 if (!ucchMax
) return FALSE
;
610 if (!lpsz
) return TRUE
;
612 /* Calculate start and end */
613 Current
= (volatile WCHAR
*)lpsz
;
614 Last
= (PWCHAR
)((ULONG_PTR
)lpsz
+ (ucchMax
* 2) - 2);
619 /* Probe the entire range */
621 while ((Char
) && (Current
!= Last
)) Char
= *Current
++;
623 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
625 /* We hit an exception, so return true */
630 /* Return exception status */
639 IsBadStringPtrA(IN LPCSTR lpsz
,
642 BOOLEAN Result
= FALSE
;
643 volatile CHAR
*Current
;
648 if (!ucchMax
) return FALSE
;
649 if (!lpsz
) return TRUE
;
651 /* Calculate start and end */
652 Current
= (volatile CHAR
*)lpsz
;
653 Last
= (PCHAR
)((ULONG_PTR
)lpsz
+ ucchMax
- 1);
658 /* Probe the entire range */
660 while ((Char
) && (Current
!= Last
)) Char
= *Current
++;
662 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
664 /* We hit an exception, so return true */
669 /* Return exception status */
678 SetLastError(IN DWORD dwErrCode
)
680 /* Break if a debugger requested checking for this error code */
681 if ((g_dwLastErrorToBreakOn
) && (g_dwLastErrorToBreakOn
== dwErrCode
)) DbgBreakPoint();
683 /* Set last error if it's a new error */
684 if (NtCurrentTeb()->LastErrorValue
!= dwErrCode
) NtCurrentTeb()->LastErrorValue
= dwErrCode
;
692 BaseSetLastNTError(IN NTSTATUS Status
)
696 /* Convert from NT to Win32, then set */
697 dwErrCode
= RtlNtStatusToDosError(Status
);
698 SetLastError(dwErrCode
);
709 /* Return the current value */
710 return NtCurrentTeb()->LastErrorValue
;