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