xref: /reactos/dll/win32/kernel32/client/except.c (revision c8d07514)
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