[PATCH v6 3/4] ntdll: Implement __fastfail().
Jinoh Kang
jinoh.kang.kr at gmail.com
Sat Jan 22 10:51:49 CST 2022
__fastfail() is used by the Visual C++ runtime and Windows system
libraries to signal that the in-process state is corrupted and
unrecoverable.
If __fastfail() is invoked, the NT kernel raises a second-chance
non-continuable exception STATUS_STACK_BUFFER_OVERRUN. This quickly
terminates the process, bypassing all in-process exception handlers
(since they all rely on the potentially corrupted process state).
Signed-off-by: Jinoh Kang <jinoh.kang.kr at gmail.com>
---
Notes:
v3 -> v4: fix BRK #0xF003 detection on ARM64
add comments
v4 -> v5: use NtRaiseException()
v5 -> v6:
- don't special case __fastfail in i386 and x86_64.
- fix ERR() call spacing issues.
dlls/ntdll/tests/exception.c | 1 -
dlls/ntdll/unix/signal_arm.c | 40 +++++++++++++++++++++++++++++++--
dlls/ntdll/unix/signal_arm64.c | 40 +++++++++++++++++++++++++++++++++
dlls/ntdll/unix/signal_i386.c | 30 +++++++++++++++++++++++++
dlls/ntdll/unix/signal_x86_64.c | 30 +++++++++++++++++++++++++
5 files changed, 138 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c
index 488a5e94729..71d25fa3e93 100644
--- a/dlls/ntdll/tests/exception.c
+++ b/dlls/ntdll/tests/exception.c
@@ -8446,7 +8446,6 @@ static void subtest_fastfail(unsigned int code)
} while (de.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT);
- todo_wine
ok(had_ff || broken(had_se) /* Win7 */, "fast fail did not occur\n");
wait_child_process( pi.hProcess );
diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c
index a1bcb0ddd32..a3c98cdb85a 100644
--- a/dlls/ntdll/unix/signal_arm.c
+++ b/dlls/ntdll/unix/signal_arm.c
@@ -615,6 +615,32 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec )
}
+/***********************************************************************
+ * raise_second_chance_exception
+ *
+ * Raise a second chance exception.
+ */
+static void raise_second_chance_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec )
+{
+ CONTEXT context;
+
+ rec->ExceptionAddress = (void *)PC_sig(sigcontext);
+ if (is_inside_syscall( sigcontext ))
+ {
+ /* Windows would bug check here */
+ ERR( "Direct second chance exception code %x flags %x addr %p (inside syscall)\n",
+ rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress );
+ NtTerminateProcess( NtCurrentProcess(), rec->ExceptionCode );
+ }
+ else
+ {
+ save_context( &context, sigcontext );
+ NtRaiseException( rec, &context, FALSE );
+ restore_context( &context, sigcontext );
+ }
+}
+
+
/***********************************************************************
* call_user_apc_dispatcher
*/
@@ -812,13 +838,23 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
switch (get_trap_code(signal, context))
{
case TRAP_ARM_PRIVINFLT: /* Invalid opcode exception */
- if (*(WORD *)PC_sig(context) == 0xdefe) /* breakpoint */
+ switch (*(WORD *)PC_sig(context))
{
+ case 0xdefb: /* __fastfail */
+ rec.ExceptionCode = STATUS_STACK_BUFFER_OVERRUN;
+ rec.ExceptionFlags = EH_NONCONTINUABLE;
+ rec.NumberParameters = 1;
+ rec.ExceptionInformation[0] = REGn_sig( 0, context );
+ raise_second_chance_exception( context, &rec );
+ return;
+ case 0xdefe: /* breakpoint */
rec.ExceptionCode = EXCEPTION_BREAKPOINT;
rec.NumberParameters = 1;
break;
+ default:
+ rec.ExceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
+ break;
}
- rec.ExceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
break;
case TRAP_ARM_PAGEFLT: /* Page fault */
rec.NumberParameters = 2;
diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c
index 94f280e218a..c3ab1aeb599 100644
--- a/dlls/ntdll/unix/signal_arm64.c
+++ b/dlls/ntdll/unix/signal_arm64.c
@@ -656,6 +656,32 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec )
}
+/***********************************************************************
+ * raise_second_chance_exception
+ *
+ * Raise a second chance exception.
+ */
+static void raise_second_chance_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec )
+{
+ CONTEXT context;
+
+ rec->ExceptionAddress = (void *)PC_sig(sigcontext);
+ if (is_inside_syscall( sigcontext ))
+ {
+ /* Windows would bug check here */
+ ERR( "Direct second chance exception code %x flags %x addr %p (inside syscall)\n",
+ rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress );
+ NtTerminateProcess( NtCurrentProcess(), rec->ExceptionCode );
+ }
+ else
+ {
+ save_context( &context, sigcontext );
+ NtRaiseException( rec, &context, FALSE );
+ restore_context( &context, sigcontext );
+ }
+}
+
+
/***********************************************************************
* call_user_apc_dispatcher
*/
@@ -908,6 +934,7 @@ static void bus_handler( int signal, siginfo_t *siginfo, void *sigcontext )
static void trap_handler( int signal, siginfo_t *siginfo, void *sigcontext )
{
EXCEPTION_RECORD rec = { 0 };
+ ucontext_t *context = sigcontext;
switch (siginfo->si_code)
{
@@ -916,6 +943,19 @@ static void trap_handler( int signal, siginfo_t *siginfo, void *sigcontext )
break;
case TRAP_BRKPT:
default:
+ /* debug exceptions do not update ESR on Linux, so we fetch the instruction directly. */
+ if (!(PSTATE_sig( context ) & 0x10) && /* AArch64 (not WoW) */
+ !(PC_sig( context ) & 3) &&
+ *(ULONG *)PC_sig( context ) == 0xd43e0060UL) /* brk #0xf003 */
+ {
+ /* __fastfail */
+ rec.ExceptionCode = STATUS_STACK_BUFFER_OVERRUN;
+ rec.ExceptionFlags = EH_NONCONTINUABLE;
+ rec.NumberParameters = 1;
+ rec.ExceptionInformation[0] = REGn_sig( 0, context );
+ raise_second_chance_exception( context, &rec );
+ return;
+ }
rec.ExceptionCode = EXCEPTION_BREAKPOINT;
rec.NumberParameters = 1;
break;
diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c
index 6bb5649e2b5..3b3ba83c463 100644
--- a/dlls/ntdll/unix/signal_i386.c
+++ b/dlls/ntdll/unix/signal_i386.c
@@ -1485,6 +1485,28 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec )
setup_raise_exception( sigcontext, stack, rec, &xcontext );
}
+/***********************************************************************
+ * raise_second_chance_exception
+ *
+ * Raise a second chance exception.
+ */
+static void raise_second_chance_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, struct xcontext *xcontext )
+{
+ if (is_inside_syscall( sigcontext ))
+ {
+ /* Windows would bug check here */
+ WINE_ERR( "Direct second chance exception code %x flags %x addr %p (inside syscall)\n",
+ rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress );
+ NtTerminateProcess( NtCurrentProcess(), rec->ExceptionCode );
+ }
+ else
+ {
+ NtRaiseException( rec, &xcontext->c, FALSE );
+ restore_context( xcontext, sigcontext );
+ }
+}
+
+
/* stack layout when calling an user apc function.
* FIXME: match Windows ABI. */
struct apc_stack_layout
@@ -1661,6 +1683,14 @@ static BOOL handle_interrupt( unsigned int interrupt, ucontext_t *sigcontext, vo
switch(interrupt)
{
+ case 0x29:
+ /* __fastfail: process state is corrupted */
+ rec->ExceptionCode = STATUS_STACK_BUFFER_OVERRUN;
+ rec->ExceptionFlags = EH_NONCONTINUABLE;
+ rec->NumberParameters = 1;
+ rec->ExceptionInformation[0] = ECX_sig( sigcontext );
+ raise_second_chance_exception( sigcontext, rec, xcontext );
+ return TRUE;
case 0x2d:
if (!is_wow64)
{
diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c
index 68e0c7ce66e..e06ac71bb4c 100644
--- a/dlls/ntdll/unix/signal_x86_64.c
+++ b/dlls/ntdll/unix/signal_x86_64.c
@@ -2198,6 +2198,28 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec )
}
+/***********************************************************************
+ * raise_second_chance_exception
+ *
+ * Raise a second chance exception.
+ */
+static void raise_second_chance_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, struct xcontext *xcontext )
+{
+ if (is_inside_syscall( sigcontext ))
+ {
+ /* Windows would bug check here */
+ ERR( "Direct second chance exception code %x flags %x addr %p (inside syscall)\n",
+ rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress );
+ NtTerminateProcess( NtCurrentProcess(), rec->ExceptionCode );
+ }
+ else
+ {
+ NtRaiseException( rec, &xcontext->c, FALSE );
+ restore_context( xcontext, sigcontext );
+ }
+}
+
+
/***********************************************************************
* call_user_apc_dispatcher
*/
@@ -2444,6 +2466,14 @@ static inline BOOL handle_interrupt( ucontext_t *sigcontext, EXCEPTION_RECORD *r
switch (ERROR_sig(sigcontext) >> 3)
{
+ case 0x29:
+ /* __fastfail: process state is corrupted */
+ rec->ExceptionCode = STATUS_STACK_BUFFER_OVERRUN;
+ rec->ExceptionFlags = EH_NONCONTINUABLE;
+ rec->NumberParameters = 1;
+ rec->ExceptionInformation[0] = RCX_sig( sigcontext );
+ raise_second_chance_exception( sigcontext, rec, xcontext );
+ return TRUE;
case 0x2c:
rec->ExceptionCode = STATUS_ASSERTION_FAILURE;
break;
--
2.31.1
More information about the wine-devel
mailing list