Description
Commit 51ee32f5f8 unveiled a weird condition on GCC builds only (not happening with MSVC ones), wherein executing nested SEH handlers leads to either dereferencing a NULL RegistrationFrame, or having no RegistrationFrame available, when running in a specific environment alike to what winlogon.exe executes.
In the above-mentioned commit, the problem was unveiled by moving the WNetClearConnections(NULL); function call (implemented in mpr.dll) from a separately-spawned thread, and into the main winlogon thread.
This routine invoked a 3rd party module (nfs41_np.dll) that was executing the kernel32!OutputDebugStringA() function, and the crash happened while "executing" the PSEH implementation of an inner _SEH2_TRY .
(A hack that hides the problem, is to surround the OutputDebugStringA() call, or the outer WNetClearConnections(NULL); call, within a _SEH2_TRY / _SEH2_EXCEPT / _SEH2_END; block. But this is only a hack, and not THE actual fix!)
However, I managed to isolate the problem to the specific usage of nested SEH handlers and made a minimal test application that can reproduce the problem, without the need to invoke OutputDebugStringA.
The nested SEH handlers pattern, also used by kernel32!OutputDebugStringA and that causes the problem is the following one:
_SEH2_TRY
|
{
|
/* Throw a random exception to jump to the _SEH2_EXCEPT block below */ |
RaiseException(0xDEADBEEF, 0, 0, NULL); // NOTE: kernel32!OutputDebugStringA raises DBG_PRINTEXCEPTION_C |
}
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
{
|
_SEH2_TRY // <------ This _SEH2_TRY crashes!! |
{
|
_SEH2_TRY
|
{
|
// Do soething useful, like: DbgPrint("%s", OutputString); |
}
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
{
|
}
|
_SEH2_END;
|
}
|
_SEH2_FINALLY
|
{
|
}
|
_SEH2_END;
|
}
|
_SEH2_END;
|
A MAJOR observation is that this problem happens when the test program is started in an environment similar to that winlogon.exe is started, i.e., using the ntdll!RtlCreateUserProcess() and manually registered with the CSR subsystem, and NOT when starting the process with kernel32!CreateProcess() !
I attach two precompiled binaries + their PDB (only for MSVC build), together with the source-code in 0002-ROSTESTS-NTRTL_SEH-Add-tests-for-SEH-in-application.patch (see also the modules_rostests_win32_ntrtl_seh.zip
archive):
- an MSVC v19.29.30159 x86 compiled ntrtl_seh_msvc.exe
binary, that does *NOT* reproduces the problem;
- a RosBE-Windows 2.2.1 gcc v8.4.0 compiled ntrtl_seh_gcc.exe
binary, with which the problem can be reproduced.
(GUI interactive versions of these tests can also be found in the ntrtl_seh_interactive.zip archive.)
I also join a launcher app, nativerun.exe, to easily start the test programs in an environment similar to that of winlogon.exe.
To ease diagnostics, a patch to apply to ReactOS (on ntoskrnl and csrsrv), which adds extra detailed DPRINTs, is also attached: 0001-wip-Diagnose-SEH2-crashes.patch
Testing procedure:
- Locally patch ReactOS with the attached 0001-wip-Diagnose-SEH2-crashes.patch
(optional);
- Compile ReactOS with MSVC, so that you can easily obtain debugging traces etc. with WinDbg. (Or you can use a GCC build, as you wish!)
- Install ReactOS;
- Copy the nativerun.exe launcher app, as well as the two test binaries ntrtl_seh_msvc.exe and ntrtl_seh_gcc.exe in the system32 directory of your ReactOS installation;
- From command-line, execute in turn ntrtl_seh_msvc.exe and ntrtl_seh_gcc.exe directly – this would use kernel32!CreateProcess to load them – , then, use the nativerun.exe launcher app to run these – now testing an execution environment similar to winlogon's. Observe the debug output in the attached kernel debugger.
- Alternatively you can replace the winlogon.exe executable with any of the test programs (and renaming them to winlogon.exe) then reboot ReactOS and observe the debug output in the attached kernel debugger.
Results:
- ✅ ntrtl_seh_msvc.exe started directly (i.e. with kernel32!CreateProcess()): Works
Output summary below; analysis of the exception RegistrationFrame's in ntrtl_seh_msvc_normal.txtReplaced Original unhandled exception filter: 0x00402490
OutputDebugStringA with SEH test
ODS(0x00403370) -> 'WL: WinMain(1)
'
MyOutputDebugStringA - Entering _SEH2_TRY(1)
MyOutputDebugStringA - Inside _SEH2_TRY(1)
MyOutputDebugStringA - Entering _SEH2_TRY(2)
MyOutputDebugStringA - Inside _SEH2_TRY(2)
WL: WinMain(1)
MyOutputDebugStringA - Exited _SEH2_EXCEPT(2)
MyOutputDebugStringA - Inside and exiting _SEH2_FINALLY(1)
MyOutputDebugStringA - Exited _SEH2_TRY(1)/_SEH2_FINALLY(1)
ODS(0x004033AC) -> 'WL: WinMain(2)
'
MyOutputDebugStringA - Entering _SEH2_TRY(1)
MyOutputDebugStringA - Inside _SEH2_TRY(1)
MyOutputDebugStringA - Entering _SEH2_TRY(2)
MyOutputDebugStringA - Inside _SEH2_TRY(2)
WL: WinMain(2)
MyOutputDebugStringA - Exited _SEH2_EXCEPT(2)
MyOutputDebugStringA - Inside and exiting _SEH2_FINALLY(1)
MyOutputDebugStringA - Exited _SEH2_TRY(1)/_SEH2_FINALLY(1)
ODS(0x004033BC) -> 'WL: WinMain(3)
'
MyOutputDebugStringA - Entering _SEH2_TRY(1)
MyOutputDebugStringA - Inside _SEH2_TRY(1)
MyOutputDebugStringA - Entering _SEH2_TRY(2)
MyOutputDebugStringA - Inside _SEH2_TRY(2)
WL: WinMain(3)
MyOutputDebugStringA - Exited _SEH2_EXCEPT(2)
MyOutputDebugStringA - Inside and exiting _SEH2_FINALLY(1)
MyOutputDebugStringA - Exited _SEH2_TRY(1)/_SEH2_FINALLY(1)
Test succeeded!
- ✅ ntrtl_seh_gcc.exe started directly: Works
Output summary below; analysis of the exception RegistrationFrame's in ntrtl_seh_gcc_normal.txtReplaced Original unhandled exception filter: 0x00401CFC
OutputDebugStringA with SEH test
ODS(0x00404272) -> 'WL: WinMain(1)
'
MyOutputDebugStringA - Entering _SEH2_TRY(1)
MyOutputDebugStringA - Inside _SEH2_TRY(1)
MyOutputDebugStringA - Entering _SEH2_TRY(2)
MyOutputDebugStringA - Inside _SEH2_TRY(2)
WL: WinMain(1)
MyOutputDebugStringA - Exited _SEH2_EXCEPT(2)
_SEH3$_FinallyFunction - Inside and exiting _SEH2_FINALLY(1)
MyOutputDebugStringA - Exited _SEH2_TRY(1)/_SEH2_FINALLY(1)
ODS(0x004042B0) -> 'WL: WinMain(2)
'
MyOutputDebugStringA - Entering _SEH2_TRY(1)
MyOutputDebugStringA - Inside _SEH2_TRY(1)
MyOutputDebugStringA - Entering _SEH2_TRY(2)
MyOutputDebugStringA - Inside _SEH2_TRY(2)
WL: WinMain(2)
MyOutputDebugStringA - Exited _SEH2_EXCEPT(2)
_SEH3$_FinallyFunction - Inside and exiting _SEH2_FINALLY(1)
MyOutputDebugStringA - Exited _SEH2_TRY(1)/_SEH2_FINALLY(1)
ODS(0x004042C0) -> 'WL: WinMain(3)
'
MyOutputDebugStringA - Entering _SEH2_TRY(1)
MyOutputDebugStringA - Inside _SEH2_TRY(1)
MyOutputDebugStringA - Entering _SEH2_TRY(2)
MyOutputDebugStringA - Inside _SEH2_TRY(2)
WL: WinMain(3)
MyOutputDebugStringA - Exited _SEH2_EXCEPT(2)
_SEH3$_FinallyFunction - Inside and exiting _SEH2_FINALLY(1)
MyOutputDebugStringA - Exited _SEH2_TRY(1)/_SEH2_FINALLY(1)
Test succeeded!
- ✅ nativerun.exe ntrtl_seh_msvc.exe , i.e. environment similar to winlogon.exe (with ntdll!RtlCreateUserProcess()) : Works
Output summary below; analysis of the exception RegistrationFrame's in ntrtl_seh_msvc_native.txtWin32 GUI process detected, registering with CSRSS
Process 0x6F8 (1784) created successfully.
Replaced Original unhandled exception filter: 0x00402490
OutputDebugStringA with SEH test
ODS(0x00403370) -> 'WL: WinMain(1)
'
MyOutputDebugStringA - Entering _SEH2_TRY(1)
MyOutputDebugStringA - Inside _SEH2_TRY(1)
MyOutputDebugStringA - Entering _SEH2_TRY(2)
MyOutputDebugStringA - Inside _SEH2_TRY(2)
WL: WinMain(1)
MyOutputDebugStringA - Exited _SEH2_EXCEPT(2)
MyOutputDebugStringA - Inside and exiting _SEH2_FINALLY(1)
MyOutputDebugStringA - Exited _SEH2_TRY(1)/_SEH2_FINALLY(1)
ODS(0x004033AC) -> 'WL: WinMain(2)
'
MyOutputDebugStringA - Entering _SEH2_TRY(1)
MyOutputDebugStringA - Inside _SEH2_TRY(1)
MyOutputDebugStringA - Entering _SEH2_TRY(2)
MyOutputDebugStringA - Inside _SEH2_TRY(2)
WL: WinMain(2)
MyOutputDebugStringA - Exited _SEH2_EXCEPT(2)
MyOutputDebugStringA - Inside and exiting _SEH2_FINALLY(1)
MyOutputDebugStringA - Exited _SEH2_TRY(1)/_SEH2_FINALLY(1)
ODS(0x004033BC) -> 'WL: WinMain(3)
'
MyOutputDebugStringA - Entering _SEH2_TRY(1)
MyOutputDebugStringA - Inside _SEH2_TRY(1)
MyOutputDebugStringA - Entering _SEH2_TRY(2)
MyOutputDebugStringA - Inside _SEH2_TRY(2)
WL: WinMain(3)
MyOutputDebugStringA - Exited _SEH2_EXCEPT(2)
MyOutputDebugStringA - Inside and exiting _SEH2_FINALLY(1)
MyOutputDebugStringA - Exited _SEH2_TRY(1)/_SEH2_FINALLY(1)
Test succeeded!
- ⛔ nativerun.exe ntrtl_seh_gcc.exe , i.e. environment similar to winlogon.exe (with ntdll!RtlCreateUserProcess()) : CRASH!
Output summary below; analysis of the exception RegistrationFrame's in ntrtl_seh_gcc_native.txtWin32 GUI process detected, registering with CSRSS
Process 0x740 (1856) created successfully.
Replaced Original unhandled exception filter: 0x00401CFC
OutputDebugStringA with SEH test
ODS(0x00404272) -> 'WL: WinMain(1)
'
MyOutputDebugStringA - Entering _SEH2_TRY(1)
MyOutputDebugStringA - Inside _SEH2_TRY(1)
MyOutputDebugStringA - Entering _SEH2_TRY(2)
MyOutputDebugStringA - Inside _SEH2_TRY(2)
WL: WinMain(1)
MyOutputDebugStringA - Exited _SEH2_EXCEPT(2)
_SEH3$_FinallyFunction - Inside and exiting _SEH2_FINALLY(1)
MyOutputDebugStringA - Exited _SEH2_TRY(1)/_SEH2_FINALLY(1)
ODS(0x004042B0) -> 'WL: WinMain(2)
'
MyOutputDebugStringA - Entering _SEH2_TRY(1)
DbgkForwardException: ExceptionRecord: F5C7BC90 (ExceptionCode: 0xc0000005, ExceptionAddress: 0x004017D2), Port: TRUE, SecondChance: FALSE
DbgkForwardException: ExceptionRecord: F5C7B97C (ExceptionCode: 0xc0000005, ExceptionAddress: 0x004017D2), Port: TRUE, SecondChance: TRUE
DbgkForwardException: ExceptionRecord: F5C7B97C (ExceptionCode: 0xc0000005, ExceptionAddress: 0x004017D2), Port: FALSE, SecondChance: TRUE
CsrApiRequestThread() - Got LPC_EXCEPTION with:
ExceptionCode: 0xc0000005, flags 0x0, ExceptionAddress: 0x004017D2 ; IsFirstChance: FALSE
Info[0]: 0x00000000
Info[1]: 0x0000000F
CsrApiRequestThread() - LPC_EXCEPTION invoking NtTerminateProcess(ProcessHandle 0x000006E4, 128)
Attachments
Issue Links
- blocks
-
CORE-20307 REGRESSION: BSOD 0xc000021a when logging off or shutting down
-
- Resolved
-
-
CORE-20309 REGRESSION: Restarting / shutting down is broken
-
- Resolved
-
- relates to
-
CORE-16746 MS SEH0026 broken with GCC
-
- Open
-