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) ]
18 LPTOP_LEVEL_EXCEPTION_FILTER GlobalTopLevelExceptionFilter
= NULL
;
19 DWORD g_dwLastErrorToBreakOn
;
22 * Private helper function to lookup the module name from a given address.
23 * The address can point to anywhere within the module.
26 _module_name_from_addr(const void* addr
, void **module_start_addr
,
27 char* psz
, size_t nChars
)
29 MEMORY_BASIC_INFORMATION mbi
;
30 if (VirtualQuery(addr
, &mbi
, sizeof(mbi
)) != sizeof(mbi
) ||
31 !GetModuleFileNameA((HMODULE
)mbi
.AllocationBase
, psz
, nChars
))
34 *module_start_addr
= 0;
38 *module_start_addr
= (void *)mbi
.AllocationBase
;
45 _dump_context(PCONTEXT pc
)
49 * Print out the CPU registers
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
);
68 #warning Unknown architecture
73 BasepCheckForReadOnlyResource(IN PVOID Ptr
)
76 ULONG Size
, OldProtect
;
77 MEMORY_BASIC_INFORMATION mbi
;
79 LONG Ret
= EXCEPTION_CONTINUE_SEARCH
;
81 /* Check if it was an attempt to write to a read-only image section! */
82 Status
= NtQueryVirtualMemory(NtCurrentProcess(),
84 MemoryBasicInformation
,
88 if (NT_SUCCESS(Status
) &&
89 mbi
.Protect
== PAGE_READONLY
&& mbi
.Type
== MEM_IMAGE
)
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
97 Data
= RtlImageDirectoryEntryToData(mbi
.AllocationBase
,
99 IMAGE_DIRECTORY_ENTRY_RESOURCE
,
103 (ULONG_PTR
)Ptr
>= (ULONG_PTR
)Data
&&
104 (ULONG_PTR
)Ptr
< (ULONG_PTR
)Data
+ Size
)
106 /* The user tried to write into the resources. Make the page
109 Status
= NtProtectVirtualMemory(NtCurrentProcess(),
114 if (NT_SUCCESS(Status
))
116 Ret
= EXCEPTION_CONTINUE_EXECUTION
;
120 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
130 PrintStackTrace(struct _EXCEPTION_POINTERS
*ExceptionInfo
)
133 CHAR szMod
[128] = "";
134 PEXCEPTION_RECORD ExceptionRecord
= ExceptionInfo
->ExceptionRecord
;
135 PCONTEXT ContextRecord
= ExceptionInfo
->ContextRecord
;
137 /* Print a stack trace. */
138 DbgPrint("Unhandled exception\n");
139 DbgPrint("ExceptionCode: %8x\n", ExceptionRecord
->ExceptionCode
);
141 if ((NTSTATUS
)ExceptionRecord
->ExceptionCode
== STATUS_ACCESS_VIOLATION
&&
142 ExceptionRecord
->NumberParameters
== 2)
144 DbgPrint("Faulting Address: %8x\n", ExceptionRecord
->ExceptionInformation
[1]);
147 _dump_context (ContextRecord
);
148 _module_name_from_addr(ExceptionRecord
->ExceptionAddress
, &StartAddr
, szMod
, sizeof(szMod
));
149 DbgPrint("Address:\n %8x+%-8x %s\n",
151 (ULONG_PTR
)ExceptionRecord
->ExceptionAddress
- (ULONG_PTR
)StartAddr
,
154 DbgPrint("Frames:\n");
159 PULONG Frame
= (PULONG
)ContextRecord
->Ebp
;
161 for (i
= 0; Frame
[1] != 0 && Frame
[1] != 0xdeadbeef && i
< 128; i
++)
163 if (IsBadReadPtr((PVOID
)Frame
[1], 4))
165 DbgPrint(" %8x%9s %s\n", Frame
[1], "<invalid address>"," ");
169 _module_name_from_addr((const void*)Frame
[1], &StartAddr
,
170 szMod
, sizeof(szMod
));
171 DbgPrint(" %8x+%-8x %s\n",
173 (ULONG_PTR
)Frame
[1] - (ULONG_PTR
)StartAddr
,
177 if (IsBadReadPtr((PVOID
)Frame
[0], sizeof(*Frame
) * 2))
180 Frame
= (PULONG
)Frame
[0];
183 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
185 DbgPrint("<error dumping stack trace: 0x%x>\n", _SEH2_GetExceptionCode());
198 /* Query the current setting */
199 Status
= NtQueryInformationProcess(NtCurrentProcess(),
200 ProcessDefaultHardErrorMode
,
204 if (!NT_SUCCESS(Status
))
206 /* Fail if we couldn't query */
207 BaseSetLastNTError(Status
);
211 /* Check if NOT failing critical errors was requested */
212 if (ErrMode
& SEM_FAILCRITICALERRORS
)
214 /* Mask it out, since the native API works differently */
215 ErrMode
&= ~SEM_FAILCRITICALERRORS
;
219 /* OR it if the caller didn't, due to different native semantics */
220 ErrMode
|= SEM_FAILCRITICALERRORS
;
223 /* Return the mode */
231 UnhandledExceptionFilter(struct _EXCEPTION_POINTERS
*ExceptionInfo
)
234 HANDLE DebugPort
= NULL
;
236 ULONG_PTR ErrorParameters
[4];
238 PEXCEPTION_RECORD ExceptionRecord
= ExceptionInfo
->ExceptionRecord
;
240 if ((NTSTATUS
)ExceptionRecord
->ExceptionCode
== STATUS_ACCESS_VIOLATION
&&
241 ExceptionRecord
->NumberParameters
>= 2)
243 switch(ExceptionRecord
->ExceptionInformation
[0])
245 case EXCEPTION_WRITE_FAULT
:
246 /* Change the protection on some write attempts, some InstallShield setups
248 RetValue
= BasepCheckForReadOnlyResource(
249 (PVOID
)ExceptionRecord
->ExceptionInformation
[1]);
250 if (RetValue
== EXCEPTION_CONTINUE_EXECUTION
)
251 return EXCEPTION_CONTINUE_EXECUTION
;
253 case EXCEPTION_EXECUTE_FAULT
:
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
)
264 BaseSetLastNTError(ErrCode
);
265 return EXCEPTION_EXECUTE_HANDLER
;
270 /* Pass the exception to debugger. */
271 DPRINT("Passing exception to debugger\n");
272 return EXCEPTION_CONTINUE_SEARCH
;
275 if (GlobalTopLevelExceptionFilter
)
277 LONG ret
= GlobalTopLevelExceptionFilter(ExceptionInfo
);
278 if (ret
!= EXCEPTION_CONTINUE_SEARCH
)
282 if ((GetErrorMode() & SEM_NOGPFAULTERRORBOX
) == 0)
283 PrintStackTrace(ExceptionInfo
);
285 /* Save exception code and address */
286 ErrorParameters
[0] = (ULONG
)ExceptionRecord
->ExceptionCode
;
287 ErrorParameters
[1] = (ULONG_PTR
)ExceptionRecord
->ExceptionAddress
;
289 if ((NTSTATUS
)ExceptionRecord
->ExceptionCode
== STATUS_ACCESS_VIOLATION
)
291 /* get the type of operation that caused the access violation */
292 ErrorParameters
[2] = ExceptionRecord
->ExceptionInformation
[0];
296 ErrorParameters
[2] = ExceptionRecord
->ExceptionInformation
[2];
299 /* Save faulting address */
300 ErrorParameters
[3] = ExceptionRecord
->ExceptionInformation
[1];
302 /* Raise the harderror */
303 ErrCode
= NtRaiseHardError(STATUS_UNHANDLED_EXCEPTION
| 0x10000000,
304 4, 0, ErrorParameters
, OptionOkCancel
, &ErrorResponse
);
306 if (NT_SUCCESS(ErrCode
) && (ErrorResponse
== ResponseCancel
))
308 /* FIXME: Check the result, if the "Cancel" button was
309 clicked run a debugger */
310 DPRINT1("Debugging is not implemented yet\n");
314 * Returning EXCEPTION_EXECUTE_HANDLER means that the code in
315 * the __except block will be executed. Normally this will end up in a
319 return EXCEPTION_EXECUTE_HANDLER
;
327 RaiseException(IN DWORD dwExceptionCode
,
328 IN DWORD dwExceptionFlags
,
329 IN DWORD nNumberOfArguments
,
330 IN CONST ULONG_PTR
*lpArguments OPTIONAL
)
332 EXCEPTION_RECORD ExceptionRecord
;
334 /* Setup the exception record */
335 ExceptionRecord
.ExceptionCode
= dwExceptionCode
;
336 ExceptionRecord
.ExceptionRecord
= NULL
;
337 ExceptionRecord
.ExceptionAddress
= (PVOID
)RaiseException
;
338 ExceptionRecord
.ExceptionFlags
= dwExceptionFlags
& EXCEPTION_NONCONTINUABLE
;
340 /* Check if we have arguments */
344 ExceptionRecord
.NumberParameters
= 0;
348 /* We do, normalize the count */
349 if (nNumberOfArguments
> EXCEPTION_MAXIMUM_PARAMETERS
)
351 nNumberOfArguments
= EXCEPTION_MAXIMUM_PARAMETERS
;
354 /* Set the count of parameters */
355 ExceptionRecord
.NumberParameters
= nNumberOfArguments
;
357 /* Loop each parameter */
358 for (nNumberOfArguments
= 0;
359 (nNumberOfArguments
< ExceptionRecord
.NumberParameters
);
360 nNumberOfArguments
++)
362 /* Copy the exception information */
363 ExceptionRecord
.ExceptionInformation
[nNumberOfArguments
] =
368 if (dwExceptionCode
== 0xeedface || dwExceptionCode
== 0xeedfade)
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]);
375 /* Trace the wine special error and show the modulename and functionname */
376 if (dwExceptionCode
== 0x80000100 /*EXCEPTION_WINE_STUB*/)
378 /* Numbers of parameter must be equal to two */
379 if (ExceptionRecord
.NumberParameters
== 2)
381 DPRINT1("Missing function in : %s\n", ExceptionRecord
.ExceptionInformation
[0]);
382 DPRINT1("with the functionname : %s\n", ExceptionRecord
.ExceptionInformation
[1]);
386 /* Raise the exception */
387 RtlRaiseException(&ExceptionRecord
);
395 SetErrorMode(IN UINT uMode
)
397 UINT PrevErrMode
, NewMode
;
400 /* Get the previous mode */
401 PrevErrMode
= GetErrorMode();
404 /* Check if failing critical errors was requested */
405 if (NewMode
& SEM_FAILCRITICALERRORS
)
407 /* Mask it out, since the native API works differently */
408 NewMode
&= ~SEM_FAILCRITICALERRORS
;
412 /* OR it if the caller didn't, due to different native semantics */
413 NewMode
|= SEM_FAILCRITICALERRORS
;
416 /* Set the new mode */
417 Status
= NtSetInformationProcess(NtCurrentProcess(),
418 ProcessDefaultHardErrorMode
,
421 if(!NT_SUCCESS(Status
)) BaseSetLastNTError(Status
);
423 /* Return the previous mode */
430 LPTOP_LEVEL_EXCEPTION_FILTER
432 SetUnhandledExceptionFilter(
433 IN LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter
)
435 return InterlockedExchangePointer(&GlobalTopLevelExceptionFilter
,
436 lpTopLevelExceptionFilter
);
444 IsBadReadPtr(IN LPCVOID lp
,
448 BOOLEAN Result
= FALSE
;
449 volatile CHAR
*Current
;
453 if (!ucb
) return FALSE
;
454 if (!lp
) return TRUE
;
456 /* Get the page size */
457 PageSize
= BaseStaticServerData
->SysInfo
.PageSize
;
459 /* Calculate the last page */
460 Last
= (PCHAR
)((ULONG_PTR
)lp
+ ucb
- 1);
462 /* Another quick failure case */
463 if ((ULONG_PTR
)Last
< (ULONG_PTR
)lp
) return TRUE
;
468 /* Probe the entire range */
469 Current
= (volatile CHAR
*)lp
;
470 Last
= (PCHAR
)(PAGE_ROUND_DOWN(Last
));
474 Current
= (volatile CHAR
*)(PAGE_ROUND_DOWN(Current
) + PAGE_SIZE
);
475 } while (Current
<= Last
);
477 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
479 /* We hit an exception, so return true */
484 /* Return exception status */
493 IsBadHugeReadPtr(LPCVOID lp
,
496 /* Implementation is the same on 32-bit */
497 return IsBadReadPtr(lp
, ucb
);
505 IsBadCodePtr(FARPROC lpfn
)
507 /* Executing has the same privileges as reading */
508 return IsBadReadPtr((LPVOID
)lpfn
, 1);
516 IsBadWritePtr(LPVOID lp
,
520 BOOLEAN Result
= FALSE
;
521 volatile CHAR
*Current
;
525 if (!ucb
) return FALSE
;
526 if (!lp
) return TRUE
;
528 /* Get the page size */
529 PageSize
= BaseStaticServerData
->SysInfo
.PageSize
;
531 /* Calculate the last page */
532 Last
= (PCHAR
)((ULONG_PTR
)lp
+ ucb
- 1);
534 /* Another quick failure case */
535 if ((ULONG_PTR
)Last
< (ULONG_PTR
)lp
) return TRUE
;
540 /* Probe the entire range */
541 Current
= (volatile CHAR
*)lp
;
542 Last
= (PCHAR
)(PAGE_ROUND_DOWN(Last
));
546 Current
= (volatile CHAR
*)(PAGE_ROUND_DOWN(Current
) + PAGE_SIZE
);
547 } while (Current
<= Last
);
549 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
551 /* We hit an exception, so return true */
556 /* Return exception status */
565 IsBadHugeWritePtr(LPVOID lp
,
568 /* Implementation is the same on 32-bit */
569 return IsBadWritePtr(lp
, ucb
);
577 IsBadStringPtrW(IN LPCWSTR lpsz
,
580 BOOLEAN Result
= FALSE
;
581 volatile WCHAR
*Current
;
586 if (!ucchMax
) return FALSE
;
587 if (!lpsz
) return TRUE
;
589 /* Calculate the last page */
590 Last
= (PWCHAR
)((ULONG_PTR
)lpsz
+ (ucchMax
* 2) - 2);
595 /* Probe the entire range */
596 Current
= (volatile WCHAR
*)lpsz
;
597 Last
= (PWCHAR
)(PAGE_ROUND_DOWN(Last
));
602 } while (Char
&& (Current
!= Last
+ 1));
604 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
606 /* We hit an exception, so return true */
611 /* Return exception status */
620 IsBadStringPtrA(IN LPCSTR lpsz
,
623 BOOLEAN Result
= FALSE
;
624 volatile CHAR
*Current
;
629 if (!ucchMax
) return FALSE
;
630 if (!lpsz
) return TRUE
;
632 /* Calculate the last page */
633 Last
= (PCHAR
)((ULONG_PTR
)lpsz
+ ucchMax
- 1);
638 /* Probe the entire range */
639 Current
= (volatile CHAR
*)lpsz
;
640 Last
= (PCHAR
)(PAGE_ROUND_DOWN(Last
));
645 } while (Char
&& (Current
!= Last
+ 1));
647 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
649 /* We hit an exception, so return true */
654 /* Return exception status */
663 SetLastError(IN DWORD dwErrCode
)
665 /* Break if a debugger requested checking for this error code */
666 if ((g_dwLastErrorToBreakOn
) && (g_dwLastErrorToBreakOn
== dwErrCode
)) DbgBreakPoint();
668 /* Set last error if it's a new error */
669 if (NtCurrentTeb()->LastErrorValue
!= dwErrCode
) NtCurrentTeb()->LastErrorValue
= dwErrCode
;
677 BaseSetLastNTError(IN NTSTATUS Status
)
679 /* Convert from NT to Win32, then set */
680 SetLastError(RtlNtStatusToDosError(Status
));
690 /* Return the current value */
691 return NtCurrentTeb()->LastErrorValue
;