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