1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS system libraries 4 * FILE: dll/win32/kernel32/client/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 #include <strsafe.h> 16 17 #define NDEBUG 18 #include <debug.h> 19 20 /* 21 * Private helper function to lookup the module name from a given address. 22 * The address can point to anywhere within the module. 23 */ 24 static const char* 25 _module_name_from_addr(const void* addr, void **module_start_addr, 26 char* psz, size_t nChars, char** module_name) 27 { 28 MEMORY_BASIC_INFORMATION mbi; 29 if (VirtualQuery(addr, &mbi, sizeof(mbi)) != sizeof(mbi) || 30 !GetModuleFileNameA((HMODULE)mbi.AllocationBase, psz, nChars)) 31 { 32 psz[0] = '\0'; 33 *module_name = psz; 34 *module_start_addr = 0; 35 } 36 else 37 { 38 char* s1 = strrchr(psz, '\\'), *s2 = strrchr(psz, '/'); 39 if (s2 && !s1) 40 s1 = s2; 41 else if (s1 && s2 && s1 < s2) 42 s1 = s2; 43 44 if (!s1) 45 s1 = psz; 46 else 47 s1++; 48 49 *module_name = s1; 50 *module_start_addr = (void *)mbi.AllocationBase; 51 } 52 return psz; 53 } 54 55 56 static VOID 57 _dump_context(PCONTEXT pc) 58 { 59 #ifdef _M_IX86 60 /* 61 * Print out the CPU registers 62 */ 63 DbgPrint("CS:EIP %x:%x\n", pc->SegCs&0xffff, pc->Eip ); 64 DbgPrint("DS %x ES %x FS %x GS %x\n", pc->SegDs&0xffff, pc->SegEs&0xffff, 65 pc->SegFs&0xffff, pc->SegGs&0xfff); 66 DbgPrint("EAX: %.8x EBX: %.8x ECX: %.8x\n", pc->Eax, pc->Ebx, pc->Ecx); 67 DbgPrint("EDX: %.8x EBP: %.8x ESI: %.8x ESP: %.8x\n", pc->Edx, 68 pc->Ebp, pc->Esi, pc->Esp); 69 DbgPrint("EDI: %.8x EFLAGS: %.8x\n", pc->Edi, pc->EFlags); 70 #elif defined(_M_AMD64) 71 DbgPrint("CS:RIP %x:%I64x\n", pc->SegCs&0xffff, pc->Rip ); 72 DbgPrint("DS %x ES %x FS %x GS %x\n", pc->SegDs&0xffff, pc->SegEs&0xffff, 73 pc->SegFs&0xffff, pc->SegGs&0xfff); 74 DbgPrint("RAX: %I64x RBX: %I64x RCX: %I64x RDI: %I64x\n", pc->Rax, pc->Rbx, pc->Rcx, pc->Rdi); 75 DbgPrint("RDX: %I64x RBP: %I64x RSI: %I64x RSP: %I64x\n", pc->Rdx, pc->Rbp, pc->Rsi, pc->Rsp); 76 DbgPrint("R8: %I64x R9: %I64x R10: %I64x R11: %I64x\n", pc->R8, pc->R9, pc->R10, pc->R11); 77 DbgPrint("R12: %I64x R13: %I64x R14: %I64x R15: %I64x\n", pc->R12, pc->R13, pc->R14, pc->R15); 78 DbgPrint("EFLAGS: %.8x\n", pc->EFlags); 79 #elif defined(_M_ARM) 80 DbgPrint("PC: %08lx LR: %08lx SP: %08lx\n", pc->Pc); 81 DbgPrint("R0: %08lx R1: %08lx R2: %08lx R3: %08lx\n", pc->R0, pc->R1, pc->R2, pc->R3); 82 DbgPrint("R4: %08lx R5: %08lx R6: %08lx R7: %08lx\n", pc->R4, pc->R5, pc->R6, pc->R7); 83 DbgPrint("R8: %08lx R9: %08lx R10: %08lx R11: %08lx\n", pc->R8, pc->R9, pc->R10, pc->R11); 84 DbgPrint("R12: %08lx CPSR: %08lx FPSCR: %08lx\n", pc->R12, pc->Cpsr, pc->R1, pc->Fpscr, pc->R3); 85 #else 86 #error "Unknown architecture" 87 #endif 88 } 89 90 static VOID 91 PrintStackTrace(IN PEXCEPTION_POINTERS ExceptionInfo) 92 { 93 PVOID StartAddr; 94 CHAR szMod[128] = "", *szModFile; 95 PEXCEPTION_RECORD ExceptionRecord = ExceptionInfo->ExceptionRecord; 96 PCONTEXT ContextRecord = ExceptionInfo->ContextRecord; 97 98 /* Print a stack trace */ 99 DbgPrint("Unhandled exception\n"); 100 DbgPrint("ExceptionCode: %8x\n", ExceptionRecord->ExceptionCode); 101 102 if (ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION && 103 ExceptionRecord->NumberParameters == 2) 104 { 105 DbgPrint("Faulting Address: %8x\n", ExceptionRecord->ExceptionInformation[1]); 106 } 107 108 _dump_context(ContextRecord); 109 _module_name_from_addr(ExceptionRecord->ExceptionAddress, &StartAddr, szMod, sizeof(szMod), &szModFile); 110 DbgPrint("Address:\n<%s:%x> (%s@%x)\n", 111 szModFile, 112 (ULONG_PTR)ExceptionRecord->ExceptionAddress - (ULONG_PTR)StartAddr, 113 szMod, 114 StartAddr); 115 #ifdef _M_IX86 116 DbgPrint("Frames:\n"); 117 118 _SEH2_TRY 119 { 120 UINT i; 121 PULONG Frame = (PULONG)ContextRecord->Ebp; 122 123 for (i = 0; Frame[1] != 0 && Frame[1] != 0xdeadbeef && i < 128; i++) 124 { 125 if (IsBadReadPtr((PVOID)Frame[1], 4)) 126 { 127 DbgPrint("<%s:%x>\n", "[invalid address]", Frame[1]); 128 } 129 else 130 { 131 _module_name_from_addr((const void*)Frame[1], &StartAddr, 132 szMod, sizeof(szMod), &szModFile); 133 DbgPrint("<%s:%x> (%s@%x)\n", 134 szModFile, 135 (ULONG_PTR)Frame[1] - (ULONG_PTR)StartAddr, 136 szMod, 137 StartAddr); 138 } 139 140 if (IsBadReadPtr((PVOID)Frame[0], sizeof(*Frame) * 2)) 141 break; 142 143 Frame = (PULONG)Frame[0]; 144 } 145 } 146 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 147 { 148 DbgPrint("<error dumping stack trace: 0x%x>\n", _SEH2_GetExceptionCode()); 149 } 150 _SEH2_END; 151 #endif 152 } 153 154 /* GLOBALS ********************************************************************/ 155 156 LPTOP_LEVEL_EXCEPTION_FILTER GlobalTopLevelExceptionFilter; 157 DWORD g_dwLastErrorToBreakOn; 158 159 /* FUNCTIONS ******************************************************************/ 160 161 LONG 162 WINAPI 163 BasepCheckForReadOnlyResource(IN PVOID Ptr) 164 { 165 PVOID Data; 166 ULONG Size, OldProtect; 167 MEMORY_BASIC_INFORMATION mbi; 168 NTSTATUS Status; 169 LONG Ret = EXCEPTION_CONTINUE_SEARCH; 170 171 /* Check if it was an attempt to write to a read-only image section! */ 172 Status = NtQueryVirtualMemory(NtCurrentProcess(), 173 Ptr, 174 MemoryBasicInformation, 175 &mbi, 176 sizeof(mbi), 177 NULL); 178 if (NT_SUCCESS(Status) && 179 mbi.Protect == PAGE_READONLY && mbi.Type == MEM_IMAGE) 180 { 181 /* Attempt to treat it as a resource section. We need to 182 use SEH here because we don't know if it's actually a 183 resource mapping */ 184 _SEH2_TRY 185 { 186 Data = RtlImageDirectoryEntryToData(mbi.AllocationBase, 187 TRUE, 188 IMAGE_DIRECTORY_ENTRY_RESOURCE, 189 &Size); 190 191 if (Data != NULL && 192 (ULONG_PTR)Ptr >= (ULONG_PTR)Data && 193 (ULONG_PTR)Ptr < (ULONG_PTR)Data + Size) 194 { 195 /* The user tried to write into the resources. Make the page 196 writable... */ 197 Size = 1; 198 Status = NtProtectVirtualMemory(NtCurrentProcess(), 199 &Ptr, 200 &Size, 201 PAGE_READWRITE, 202 &OldProtect); 203 if (NT_SUCCESS(Status)) 204 { 205 Ret = EXCEPTION_CONTINUE_EXECUTION; 206 } 207 } 208 } 209 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 210 { 211 } 212 _SEH2_END; 213 } 214 215 return Ret; 216 } 217 218 UINT 219 WINAPI 220 GetErrorMode(VOID) 221 { 222 NTSTATUS Status; 223 UINT ErrMode; 224 225 /* Query the current setting */ 226 Status = NtQueryInformationProcess(NtCurrentProcess(), 227 ProcessDefaultHardErrorMode, 228 &ErrMode, 229 sizeof(ErrMode), 230 NULL); 231 if (!NT_SUCCESS(Status)) 232 { 233 /* Fail if we couldn't query */ 234 BaseSetLastNTError(Status); 235 return 0; 236 } 237 238 /* Check if NOT failing critical errors was requested */ 239 if (ErrMode & SEM_FAILCRITICALERRORS) 240 { 241 /* Mask it out, since the native API works differently */ 242 ErrMode &= ~SEM_FAILCRITICALERRORS; 243 } 244 else 245 { 246 /* OR it if the caller didn't, due to different native semantics */ 247 ErrMode |= SEM_FAILCRITICALERRORS; 248 } 249 250 /* Return the mode */ 251 return ErrMode; 252 } 253 254 /* 255 * @implemented 256 */ 257 LONG 258 WINAPI 259 UnhandledExceptionFilter(IN PEXCEPTION_POINTERS ExceptionInfo) 260 { 261 static UNICODE_STRING AeDebugKey = 262 RTL_CONSTANT_STRING(L"\\Registry\\Machine\\" REGSTR_PATH_AEDEBUG); 263 264 static BOOLEAN IsSecondChance = FALSE; 265 266 /* Exception data */ 267 NTSTATUS Status; 268 PEXCEPTION_RECORD ExceptionRecord = ExceptionInfo->ExceptionRecord; 269 LPTOP_LEVEL_EXCEPTION_FILTER RealFilter; 270 LONG RetValue; 271 272 /* Debugger and hard error parameters */ 273 HANDLE DebugPort = NULL; 274 ULONG_PTR ErrorParameters[4]; 275 ULONG DebugResponse, ErrorResponse; 276 277 /* Post-Mortem "Auto-Execute" (AE) Debugger registry data */ 278 HANDLE KeyHandle; 279 OBJECT_ATTRIBUTES ObjectAttributes; 280 UNICODE_STRING ValueString; 281 ULONG Length; 282 UCHAR Buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + MAX_PATH * sizeof(WCHAR)]; 283 PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)Buffer; 284 BOOLEAN AeDebugAuto = FALSE; 285 PWCHAR AeDebugPath = NULL; 286 WCHAR AeDebugCmdLine[MAX_PATH]; 287 288 /* Debugger process data */ 289 BOOL Success; 290 HRESULT hr; 291 ULONG PrependLength; 292 HANDLE hDebugEvent; 293 HANDLE WaitHandles[2]; 294 STARTUPINFOW StartupInfo; 295 PROCESS_INFORMATION ProcessInfo; 296 297 /* In case this is a nested exception, just kill the process */ 298 if (ExceptionRecord->ExceptionFlags & EXCEPTION_NESTED_CALL) 299 { 300 NtTerminateProcess(NtCurrentProcess(), ExceptionRecord->ExceptionCode); 301 return EXCEPTION_EXECUTE_HANDLER; 302 } 303 304 if ((ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) && 305 (ExceptionRecord->NumberParameters >= 2)) 306 { 307 switch (ExceptionRecord->ExceptionInformation[0]) 308 { 309 case EXCEPTION_WRITE_FAULT: 310 { 311 /* 312 * Change the protection on some write attempts, 313 * some InstallShield setups have this bug. 314 */ 315 RetValue = BasepCheckForReadOnlyResource( 316 (PVOID)ExceptionRecord->ExceptionInformation[1]); 317 if (RetValue == EXCEPTION_CONTINUE_EXECUTION) 318 return EXCEPTION_CONTINUE_EXECUTION; 319 break; 320 } 321 322 case EXCEPTION_EXECUTE_FAULT: 323 /* FIXME */ 324 break; 325 } 326 } 327 328 /* If the process is being debugged, pass the exception to the debugger */ 329 Status = NtQueryInformationProcess(NtCurrentProcess(), 330 ProcessDebugPort, 331 &DebugPort, 332 sizeof(DebugPort), 333 NULL); 334 if (NT_SUCCESS(Status) && DebugPort) 335 { 336 DPRINT("Passing exception to debugger\n"); 337 return EXCEPTION_CONTINUE_SEARCH; 338 } 339 340 /* No debugger present, let's continue... */ 341 342 RealFilter = RtlDecodePointer(GlobalTopLevelExceptionFilter); 343 if (RealFilter) 344 { 345 RetValue = RealFilter(ExceptionInfo); 346 if (RetValue != EXCEPTION_CONTINUE_SEARCH) 347 return RetValue; 348 } 349 350 /* ReactOS-specific: DPRINT a stack trace */ 351 PrintStackTrace(ExceptionInfo); 352 353 /* 354 * Now pop up an error if needed. Check both the process-wide (Win32) 355 * and per-thread error-mode flags (NT). 356 */ 357 if ((GetErrorMode() & SEM_NOGPFAULTERRORBOX) || 358 (RtlGetThreadErrorMode() & RTL_SEM_NOGPFAULTERRORBOX)) 359 { 360 /* Do not display the pop-up error box, just transfer control to the exception handler */ 361 return EXCEPTION_EXECUTE_HANDLER; 362 } 363 364 /* Save exception code and address */ 365 ErrorParameters[0] = (ULONG_PTR)ExceptionRecord->ExceptionCode; 366 ErrorParameters[1] = (ULONG_PTR)ExceptionRecord->ExceptionAddress; 367 368 if (ExceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR) 369 { 370 /* 371 * Get the underlying status code that resulted in the exception, 372 * and just forget about the type of operation (read/write). 373 */ 374 ErrorParameters[2] = ExceptionRecord->ExceptionInformation[2]; 375 } 376 else // if (ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) or others... 377 { 378 /* Get the type of operation that caused the access violation */ 379 ErrorParameters[2] = ExceptionRecord->ExceptionInformation[0]; 380 } 381 382 /* Save faulting address */ 383 ErrorParameters[3] = ExceptionRecord->ExceptionInformation[1]; 384 385 /* 386 * Prepare the hard error dialog: default to only OK 387 * in case we do not have any debugger at our disposal. 388 */ 389 DebugResponse = OptionOk; 390 AeDebugAuto = FALSE; 391 392 /* 393 * Retrieve Post-Mortem Debugger settings from the registry, under: 394 * HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug 395 * (REGSTR_PATH_AEDEBUG). 396 */ 397 398 InitializeObjectAttributes(&ObjectAttributes, 399 &AeDebugKey, 400 OBJ_CASE_INSENSITIVE, 401 NULL, 402 NULL); 403 Status = NtOpenKey(&KeyHandle, KEY_QUERY_VALUE, &ObjectAttributes); 404 if (NT_SUCCESS(Status)) 405 { 406 /* 407 * Read the 'Auto' REG_SZ value: 408 * "0" (or any other value): Prompt the user for starting the debugger. 409 * "1": Start the debugger without prompting. 410 */ 411 RtlInitUnicodeString(&ValueString, REGSTR_VAL_AEDEBUG_AUTO); 412 Status = NtQueryValueKey(KeyHandle, 413 &ValueString, 414 KeyValuePartialInformation, 415 PartialInfo, 416 sizeof(Buffer), 417 &Length); 418 if (NT_SUCCESS(Status) && (PartialInfo->Type == REG_SZ)) 419 { 420 AeDebugAuto = (*(PWCHAR)PartialInfo->Data == L'1'); 421 } 422 else 423 { 424 AeDebugAuto = FALSE; 425 } 426 427 /* 428 * Read and store the 'Debugger' REG_SZ value. Its usual format is: 429 * C:\dbgtools\ntsd.exe -p %ld -e %ld -g 430 * with the first and second parameters being respectively 431 * the process ID and a notification event handle. 432 */ 433 RtlInitUnicodeString(&ValueString, REGSTR_VAL_AEDEBUG_DEBUGGER); 434 Status = NtQueryValueKey(KeyHandle, 435 &ValueString, 436 KeyValuePartialInformation, 437 PartialInfo, 438 sizeof(Buffer), 439 &Length); 440 if (NT_SUCCESS(Status) && (PartialInfo->Type == REG_SZ)) 441 { 442 /* We hope the string is NULL-terminated */ 443 AeDebugPath = (PWCHAR)PartialInfo->Data; 444 445 /* Skip any prepended whitespace */ 446 while ( *AeDebugPath && 447 ((*AeDebugPath == L' ') || 448 (*AeDebugPath == L'\t')) ) // iswspace(*AeDebugPath) 449 { 450 ++AeDebugPath; 451 } 452 453 if (*AeDebugPath) 454 { 455 /* We have a debugger path, we can prompt the user to debug the program */ 456 DebugResponse = OptionOkCancel; 457 } 458 else 459 { 460 /* We actually do not have anything, reset the pointer */ 461 AeDebugPath = NULL; 462 } 463 } 464 else 465 { 466 AeDebugPath = NULL; 467 } 468 469 NtClose(KeyHandle); 470 } 471 472 // TODO: Start a ReactOS Fault Reporter (unimplemented!) 473 // 474 // For now we are doing the "old way" (aka Win2k), that is also the fallback 475 // case for Windows XP/2003 in case it does not find faultrep.dll to display 476 // the nice "Application Error" dialog box: We use a hard error to communicate 477 // the problem and prompt the user to continue debugging the application or 478 // to terminate it. 479 // 480 // Since Windows XP/2003, we have the ReportFault API available. 481 // See http://www.clausbrod.de/twiki/pub/Blog/DefinePrivatePublic20070616/reportfault.cpp 482 // and https://msdn.microsoft.com/en-us/library/windows/desktop/bb513616(v=vs.85).aspx 483 // and the legacy ReportFault API: https://msdn.microsoft.com/en-us/library/windows/desktop/bb513615(v=vs.85).aspx 484 // 485 // NOTE: Starting Vista+, the fault API is constituted of the WerXXX functions. 486 // 487 // Also see Vostokov's book "Memory Dump Analysis Anthology Collector's Edition, Volume 1" 488 // at: https://books.google.fr/books?id=9w2x6NHljg4C&pg=PA115&lpg=PA115 489 490 if (!(AeDebugPath && AeDebugAuto)) 491 { 492 /* 493 * Either there is no debugger specified, or the debugger should 494 * not start automatically: display the hard error no matter what. 495 * For a first chance exception, allow continuing or debugging; 496 * for a second chance exception, just debug or kill the process. 497 */ 498 Status = NtRaiseHardError(STATUS_UNHANDLED_EXCEPTION | HARDERROR_OVERRIDE_ERRORMODE, 499 4, 500 0, 501 ErrorParameters, 502 (!IsSecondChance ? DebugResponse : OptionOk), 503 &ErrorResponse); 504 } 505 else 506 { 507 Status = STATUS_SUCCESS; 508 ErrorResponse = (AeDebugPath ? ResponseCancel : ResponseOk); 509 } 510 511 /* 512 * If the user has chosen not to debug the process, or 513 * if this is a second chance exception, kill the process. 514 */ 515 if (!NT_SUCCESS(Status) || (ErrorResponse != ResponseCancel) || IsSecondChance) 516 goto Quit; 517 518 /* If the exception comes from a CSR Server, kill it (this will lead to ReactOS shutdown) */ 519 if (BaseRunningInServerProcess) 520 { 521 IsSecondChance = TRUE; 522 goto Quit; 523 } 524 525 /* 526 * Attach a debugger to this process. The debugger process is given 527 * the process ID of the current process to be debugged, as well as 528 * a notification event handle. After being spawned, the debugger 529 * initializes and attaches to the process by calling DebugActiveProcess. 530 * When the debugger is ready, it signals the notification event, 531 * so that we can give it control over the process being debugged, 532 * by passing it the exception. 533 * 534 * See https://msdn.microsoft.com/en-us/library/ms809754.aspx 535 * and http://www.debuginfo.com/articles/ntsdwatson.html 536 * and https://sourceware.org/ml/gdb-patches/2012-08/msg00893.html 537 * for more details. 538 */ 539 540 /* Create an inheritable notification debug event for the debugger */ 541 InitializeObjectAttributes(&ObjectAttributes, 542 NULL, 543 OBJ_INHERIT, 544 NULL, 545 NULL); 546 Status = NtCreateEvent(&hDebugEvent, 547 EVENT_ALL_ACCESS, 548 &ObjectAttributes, 549 NotificationEvent, 550 FALSE); 551 if (!NT_SUCCESS(Status)) 552 hDebugEvent = NULL; 553 554 /* Build the debugger command line */ 555 556 Success = FALSE; 557 558 /* 559 * We will add two longs (process ID and event handle) to the command 560 * line. The biggest 32-bit unsigned int (0xFFFFFFFF == 4.294.967.295) 561 * takes 10 decimal digits. We then count the terminating NULL. 562 */ 563 Length = wcslen(AeDebugPath) + 2*10 + 1; 564 565 /* Check whether the debugger path may be a relative path */ 566 if ((*AeDebugPath != L'"') && 567 (RtlDetermineDosPathNameType_U(AeDebugPath) == RtlPathTypeRelative)) 568 { 569 /* Relative path, prepend SystemRoot\System32 */ 570 PrependLength = wcslen(SharedUserData->NtSystemRoot) + 10 /* == wcslen(L"\\System32\\") */; 571 if (PrependLength + Length <= ARRAYSIZE(AeDebugCmdLine)) 572 { 573 hr = StringCchPrintfW(AeDebugCmdLine, 574 PrependLength + 1, 575 L"%s\\System32\\", 576 SharedUserData->NtSystemRoot); 577 Success = SUCCEEDED(hr); 578 } 579 } 580 else 581 { 582 /* Full path */ 583 PrependLength = 0; 584 if (Length <= ARRAYSIZE(AeDebugCmdLine)) 585 Success = TRUE; 586 } 587 588 /* Format the command line */ 589 if (Success) 590 { 591 hr = StringCchPrintfW(&AeDebugCmdLine[PrependLength], 592 Length, 593 AeDebugPath, 594 HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess), // GetCurrentProcessId() 595 hDebugEvent); 596 Success = SUCCEEDED(hr); 597 } 598 599 /* Start the debugger */ 600 if (Success) 601 { 602 DPRINT1("\nStarting debugger: '%S'\n", AeDebugCmdLine); 603 604 RtlZeroMemory(&StartupInfo, sizeof(StartupInfo)); 605 RtlZeroMemory(&ProcessInfo, sizeof(ProcessInfo)); 606 607 StartupInfo.cb = sizeof(StartupInfo); 608 StartupInfo.lpDesktop = L"WinSta0\\Default"; 609 610 Success = CreateProcessW(NULL, 611 AeDebugCmdLine, 612 NULL, NULL, 613 TRUE, 0, 614 NULL, NULL, 615 &StartupInfo, &ProcessInfo); 616 } 617 618 if (Success) 619 { 620 WaitHandles[0] = hDebugEvent; 621 WaitHandles[1] = ProcessInfo.hProcess; 622 623 /* Loop until the debugger gets ready or terminates unexpectedly */ 624 do 625 { 626 /* Alertable wait */ 627 Status = NtWaitForMultipleObjects(ARRAYSIZE(WaitHandles), 628 WaitHandles, 629 WaitAny, 630 TRUE, NULL); 631 } while ((Status == STATUS_ALERTED) || (Status == STATUS_USER_APC)); 632 633 /* 634 * The debugger terminated unexpectedly and we cannot attach to it. 635 * Kill the process being debugged. 636 */ 637 if (Status == STATUS_WAIT_1) 638 { 639 /* Be sure there is no other debugger attached */ 640 Status = NtQueryInformationProcess(NtCurrentProcess(), 641 ProcessDebugPort, 642 &DebugPort, 643 sizeof(DebugPort), 644 NULL); 645 if (!NT_SUCCESS(Status) || !DebugPort) 646 { 647 /* No debugger is attached, kill the process at next round */ 648 IsSecondChance = TRUE; 649 } 650 } 651 652 CloseHandle(ProcessInfo.hThread); 653 CloseHandle(ProcessInfo.hProcess); 654 655 if (hDebugEvent) 656 NtClose(hDebugEvent); 657 658 return EXCEPTION_CONTINUE_SEARCH; 659 } 660 661 /* We failed starting the debugger, close the event handle and kill the process */ 662 663 if (hDebugEvent) 664 NtClose(hDebugEvent); 665 666 IsSecondChance = TRUE; 667 668 669 Quit: 670 /* If this is a second chance exception, kill the process */ 671 if (IsSecondChance) 672 NtTerminateProcess(NtCurrentProcess(), ExceptionRecord->ExceptionCode); 673 674 /* Otherwise allow handling exceptions in first chance */ 675 676 /* 677 * Returning EXCEPTION_EXECUTE_HANDLER means that the code in 678 * the __except block will be executed. Normally this will end up in a 679 * Terminate process. 680 */ 681 682 return EXCEPTION_EXECUTE_HANDLER; 683 } 684 685 /* 686 * @implemented 687 */ 688 VOID 689 WINAPI 690 RaiseException(IN DWORD dwExceptionCode, 691 IN DWORD dwExceptionFlags, 692 IN DWORD nNumberOfArguments, 693 IN CONST ULONG_PTR *lpArguments OPTIONAL) 694 { 695 EXCEPTION_RECORD ExceptionRecord; 696 697 /* Setup the exception record */ 698 ExceptionRecord.ExceptionCode = dwExceptionCode; 699 ExceptionRecord.ExceptionRecord = NULL; 700 ExceptionRecord.ExceptionAddress = (PVOID)RaiseException; 701 ExceptionRecord.ExceptionFlags = dwExceptionFlags & EXCEPTION_NONCONTINUABLE; 702 703 /* Check if we have arguments */ 704 if (!lpArguments) 705 { 706 /* We don't */ 707 ExceptionRecord.NumberParameters = 0; 708 } 709 else 710 { 711 /* We do, normalize the count */ 712 if (nNumberOfArguments > EXCEPTION_MAXIMUM_PARAMETERS) 713 nNumberOfArguments = EXCEPTION_MAXIMUM_PARAMETERS; 714 715 /* Set the count of parameters and copy them */ 716 ExceptionRecord.NumberParameters = nNumberOfArguments; 717 RtlCopyMemory(ExceptionRecord.ExceptionInformation, 718 lpArguments, 719 nNumberOfArguments * sizeof(ULONG)); 720 } 721 722 /* Better handling of Delphi Exceptions... a ReactOS Hack */ 723 if (dwExceptionCode == 0xeedface || dwExceptionCode == 0xeedfade) 724 { 725 DPRINT1("Delphi Exception at address: %p\n", ExceptionRecord.ExceptionInformation[0]); 726 DPRINT1("Exception-Object: %p\n", ExceptionRecord.ExceptionInformation[1]); 727 DPRINT1("Exception text: %lx\n", ExceptionRecord.ExceptionInformation[2]); 728 } 729 730 /* Trace the wine special error and show the modulename and functionname */ 731 if (dwExceptionCode == 0x80000100 /* EXCEPTION_WINE_STUB */) 732 { 733 /* Numbers of parameter must be equal to two */ 734 if (ExceptionRecord.NumberParameters == 2) 735 { 736 DPRINT1("Missing function in : %s\n", ExceptionRecord.ExceptionInformation[0]); 737 DPRINT1("with the functionname : %s\n", ExceptionRecord.ExceptionInformation[1]); 738 } 739 } 740 741 /* Raise the exception */ 742 RtlRaiseException(&ExceptionRecord); 743 } 744 745 /* 746 * @implemented 747 */ 748 UINT 749 WINAPI 750 SetErrorMode(IN UINT uMode) 751 { 752 UINT PrevErrMode, NewMode; 753 754 /* Get the previous mode */ 755 PrevErrMode = GetErrorMode(); 756 NewMode = uMode; 757 758 /* Check if failing critical errors was requested */ 759 if (NewMode & SEM_FAILCRITICALERRORS) 760 { 761 /* Mask it out, since the native API works differently */ 762 NewMode &= ~SEM_FAILCRITICALERRORS; 763 } 764 else 765 { 766 /* OR it if the caller didn't, due to different native semantics */ 767 NewMode |= SEM_FAILCRITICALERRORS; 768 } 769 770 /* Always keep no alignment faults if they were set */ 771 NewMode |= (PrevErrMode & SEM_NOALIGNMENTFAULTEXCEPT); 772 773 /* Set the new mode */ 774 NtSetInformationProcess(NtCurrentProcess(), 775 ProcessDefaultHardErrorMode, 776 (PVOID)&NewMode, 777 sizeof(NewMode)); 778 779 /* Return the previous mode */ 780 return PrevErrMode; 781 } 782 783 /* 784 * @implemented 785 */ 786 LPTOP_LEVEL_EXCEPTION_FILTER 787 WINAPI 788 DECLSPEC_HOTPATCH 789 SetUnhandledExceptionFilter(IN LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter) 790 { 791 PVOID EncodedPointer, EncodedOldPointer; 792 793 EncodedPointer = RtlEncodePointer(lpTopLevelExceptionFilter); 794 EncodedOldPointer = InterlockedExchangePointer((PVOID*)&GlobalTopLevelExceptionFilter, 795 EncodedPointer); 796 return RtlDecodePointer(EncodedOldPointer); 797 } 798 799 /* 800 * @implemented 801 */ 802 BOOL 803 WINAPI 804 IsBadReadPtr(IN LPCVOID lp, 805 IN UINT_PTR ucb) 806 { 807 ULONG PageSize; 808 BOOLEAN Result = FALSE; 809 volatile CHAR *Current; 810 PCHAR Last; 811 812 /* Quick cases */ 813 if (!ucb) return FALSE; 814 if (!lp) return TRUE; 815 816 /* Get the page size */ 817 PageSize = BaseStaticServerData->SysInfo.PageSize; 818 819 /* Calculate start and end */ 820 Current = (volatile CHAR*)lp; 821 Last = (PCHAR)((ULONG_PTR)lp + ucb - 1); 822 823 /* Another quick failure case */ 824 if (Last < Current) return TRUE; 825 826 /* Enter SEH */ 827 _SEH2_TRY 828 { 829 /* Do an initial probe */ 830 *Current; 831 832 /* Align the addresses */ 833 Current = (volatile CHAR *)ROUND_DOWN(Current, PageSize); 834 Last = (PCHAR)ROUND_DOWN(Last, PageSize); 835 836 /* Probe the entire range */ 837 while (Current != Last) 838 { 839 Current += PageSize; 840 *Current; 841 } 842 } 843 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 844 { 845 /* We hit an exception, so return true */ 846 Result = TRUE; 847 } 848 _SEH2_END 849 850 /* Return exception status */ 851 return Result; 852 } 853 854 /* 855 * @implemented 856 */ 857 BOOL 858 NTAPI 859 IsBadHugeReadPtr(LPCVOID lp, 860 UINT_PTR ucb) 861 { 862 /* Implementation is the same on 32-bit */ 863 return IsBadReadPtr(lp, ucb); 864 } 865 866 /* 867 * @implemented 868 */ 869 BOOL 870 NTAPI 871 IsBadCodePtr(FARPROC lpfn) 872 { 873 /* Executing has the same privileges as reading */ 874 return IsBadReadPtr((LPVOID)lpfn, 1); 875 } 876 877 /* 878 * @implemented 879 */ 880 BOOL 881 NTAPI 882 IsBadWritePtr(IN LPVOID lp, 883 IN UINT_PTR ucb) 884 { 885 ULONG PageSize; 886 BOOLEAN Result = FALSE; 887 volatile CHAR *Current; 888 PCHAR Last; 889 890 /* Quick cases */ 891 if (!ucb) return FALSE; 892 if (!lp) return TRUE; 893 894 /* Get the page size */ 895 PageSize = BaseStaticServerData->SysInfo.PageSize; 896 897 /* Calculate start and end */ 898 Current = (volatile CHAR*)lp; 899 Last = (PCHAR)((ULONG_PTR)lp + ucb - 1); 900 901 /* Another quick failure case */ 902 if (Last < Current) return TRUE; 903 904 /* Enter SEH */ 905 _SEH2_TRY 906 { 907 /* Do an initial probe */ 908 *Current = *Current; 909 910 /* Align the addresses */ 911 Current = (volatile CHAR *)ROUND_DOWN(Current, PageSize); 912 Last = (PCHAR)ROUND_DOWN(Last, PageSize); 913 914 /* Probe the entire range */ 915 while (Current != Last) 916 { 917 Current += PageSize; 918 *Current = *Current; 919 } 920 } 921 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 922 { 923 /* We hit an exception, so return true */ 924 Result = TRUE; 925 } 926 _SEH2_END 927 928 /* Return exception status */ 929 return Result; 930 } 931 932 /* 933 * @implemented 934 */ 935 BOOL 936 NTAPI 937 IsBadHugeWritePtr(IN LPVOID lp, 938 IN UINT_PTR ucb) 939 { 940 /* Implementation is the same on 32-bit */ 941 return IsBadWritePtr(lp, ucb); 942 } 943 944 /* 945 * @implemented 946 */ 947 BOOL 948 NTAPI 949 IsBadStringPtrW(IN LPCWSTR lpsz, 950 IN UINT_PTR ucchMax) 951 { 952 BOOLEAN Result = FALSE; 953 volatile WCHAR *Current; 954 PWCHAR Last; 955 WCHAR Char; 956 957 /* Quick cases */ 958 if (!ucchMax) return FALSE; 959 if (!lpsz) return TRUE; 960 961 /* Calculate start and end */ 962 Current = (volatile WCHAR*)lpsz; 963 Last = (PWCHAR)((ULONG_PTR)lpsz + (ucchMax * 2) - 2); 964 965 /* Enter SEH */ 966 _SEH2_TRY 967 { 968 /* Probe the entire range */ 969 Char = *Current++; 970 while ((Char) && (Current != Last)) Char = *Current++; 971 } 972 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 973 { 974 /* We hit an exception, so return true */ 975 Result = TRUE; 976 } 977 _SEH2_END 978 979 /* Return exception status */ 980 return Result; 981 } 982 983 /* 984 * @implemented 985 */ 986 BOOL 987 NTAPI 988 IsBadStringPtrA(IN LPCSTR lpsz, 989 IN UINT_PTR ucchMax) 990 { 991 BOOLEAN Result = FALSE; 992 volatile CHAR *Current; 993 PCHAR Last; 994 CHAR Char; 995 996 /* Quick cases */ 997 if (!ucchMax) return FALSE; 998 if (!lpsz) return TRUE; 999 1000 /* Calculate start and end */ 1001 Current = (volatile CHAR*)lpsz; 1002 Last = (PCHAR)((ULONG_PTR)lpsz + ucchMax - 1); 1003 1004 /* Enter SEH */ 1005 _SEH2_TRY 1006 { 1007 /* Probe the entire range */ 1008 Char = *Current++; 1009 while ((Char) && (Current != Last)) Char = *Current++; 1010 } 1011 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1012 { 1013 /* We hit an exception, so return true */ 1014 Result = TRUE; 1015 } 1016 _SEH2_END 1017 1018 /* Return exception status */ 1019 return Result; 1020 } 1021 1022 /* 1023 * @implemented 1024 */ 1025 VOID 1026 WINAPI 1027 SetLastError(IN DWORD dwErrCode) 1028 { 1029 /* Break if a debugger requested checking for this error code */ 1030 if ((g_dwLastErrorToBreakOn) && (g_dwLastErrorToBreakOn == dwErrCode)) DbgBreakPoint(); 1031 1032 /* Set last error if it's a new error */ 1033 if (NtCurrentTeb()->LastErrorValue != dwErrCode) NtCurrentTeb()->LastErrorValue = dwErrCode; 1034 } 1035 1036 /* 1037 * @implemented 1038 */ 1039 DWORD 1040 WINAPI 1041 BaseSetLastNTError(IN NTSTATUS Status) 1042 { 1043 DWORD dwErrCode; 1044 1045 /* Convert from NT to Win32, then set */ 1046 dwErrCode = RtlNtStatusToDosError(Status); 1047 SetLastError(dwErrCode); 1048 return dwErrCode; 1049 } 1050 1051 /* 1052 * @implemented 1053 */ 1054 DWORD 1055 WINAPI 1056 GetLastError(VOID) 1057 { 1058 /* Return the current value */ 1059 return NtCurrentTeb()->LastErrorValue; 1060 } 1061 1062 /* EOF */ 1063