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