From d489f1a170b66c3dd76e783d2ef7172e9fb1cdcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herm=C3=A8s=20B=C3=A9lusca-Ma=C3=AFto?= Date: Sat, 19 Apr 2025 18:27:14 +0200 Subject: [PATCH] [NTOS:KD] Supplement minimal handling in KdSend/ReceivePacket when no kernel debugger is present. CORE-20107 Improve the minimal handling done in the fall-back KdSend/ReceivePacket() routines when they are invoked by KD64 (in Release build). Part of my `kdbg_tests_kdio_split` branch for splitting kd/kdio.c into a separate KD dll. KdSendPacket: - Silently ignore PACKET_TYPE_KD_STATE_CHANGE32/64 DbgKdLoadSymbolsStateChange notification. - Partially manage and log the unhandled PACKET_TYPE_KD_STATE_CHANGE DbgKdExceptionStateChange notification. - Log other unhandled PACKET_TYPE_KD_STATE_CHANGE32/64 and PACKET_TYPE_KD_STATE_MANIPULATE notifications. When KdSendPacket receives the DbgKdExceptionStateChange notification, it cannot handle it (no debugger is there!). However, we need to claim the debugger to be present, so that its KD64 caller: KdpSendWaitContinue(), can call back KdReceivePacket(PACKET_TYPE_KD_STATE_MANIPULATE), which, in turn, informs KD that the exception cannot be handled, by returning a failure code in the ManipulateState's ContinueStatus. During bugchecks, this allows KiBugCheckDebugBreak() to not loop infinitely, but instead fall back into calling HalHaltSystem() to halt the computer. --- ntoskrnl/kd/kdio.c | 96 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 75 insertions(+), 21 deletions(-) diff --git a/ntoskrnl/kd/kdio.c b/ntoskrnl/kd/kdio.c index 36f28d00a73..8c90e8da25c 100644 --- a/ntoskrnl/kd/kdio.c +++ b/ntoskrnl/kd/kdio.c @@ -607,16 +607,73 @@ KdSendPacket( { PDBGKD_DEBUG_IO DebugIo; - if (PacketType != PACKET_TYPE_KD_DEBUG_IO) + if (PacketType == PACKET_TYPE_KD_STATE_CHANGE32 || + PacketType == PACKET_TYPE_KD_STATE_CHANGE64) { -#ifndef KDBG - if (PacketType == PACKET_TYPE_KD_STATE_CHANGE64) + PDBGKD_ANY_WAIT_STATE_CHANGE WaitStateChange = (PDBGKD_ANY_WAIT_STATE_CHANGE)MessageHeader->Buffer; + + if (WaitStateChange->NewState == DbgKdLoadSymbolsStateChange) + return; // Ignore: invoked anytime a new module is loaded. + + /* We should not get there, unless an exception has been raised */ + if (WaitStateChange->NewState == DbgKdExceptionStateChange) { - // KdIoPrintf("%s: PacketType %d is ignored without KDBG\n", __FUNCTION__, PacketType); + PEXCEPTION_RECORD64 ExceptionRecord = &WaitStateChange->u.Exception.ExceptionRecord; + + /* + * Claim the debugger to be present, so that KdpSendWaitContinue() + * can call back KdReceivePacket(PACKET_TYPE_KD_STATE_MANIPULATE), + * which, in turn, informs KD that the exception cannot be handled. + */ + KD_DEBUGGER_NOT_PRESENT = FALSE; + SharedUserData->KdDebuggerEnabled |= 0x00000002; + + KdIoPrintf("%s: Got exception 0x%08lx @ 0x%p, Flags 0x%08x, %s - Info[0]: 0x%p\n", + __FUNCTION__, + ExceptionRecord->ExceptionCode, + (PVOID)(ULONG_PTR)ExceptionRecord->ExceptionAddress, + ExceptionRecord->ExceptionFlags, + WaitStateChange->u.Exception.FirstChance ? "FirstChance" : "LastChance", + ExceptionRecord->ExceptionInformation[0]); +#if defined(_M_IX86) || defined(_M_AMD64) || defined(_M_ARM) || defined(_M_ARM64) +extern VOID NTAPI RtlpBreakWithStatusInstruction(VOID); + if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) && + ((PVOID)(ULONG_PTR)ExceptionRecord->ExceptionAddress == (PVOID)RtlpBreakWithStatusInstruction)) + { + PCONTEXT ContextRecord = &KeGetCurrentPrcb()->ProcessorState.ContextFrame; + ULONG Status = +#if defined(_M_IX86) + ContextRecord->Eax; +#elif defined(_M_AMD64) + (ULONG)ContextRecord->Rcx; +#elif defined(_M_ARM) + ContextRecord->R0; +#else // defined(_M_ARM64) + (ULONG)ContextRecord->X0; +#endif + KdIoPrintf("STATUS_BREAKPOINT Status 0x%08lx\n", Status); + } +// #else +// #error Unknown architecture +#endif return; } -#endif + KdIoPrintf("%s: PACKET_TYPE_KD_STATE_CHANGE32/64 NewState %d is UNIMPLEMENTED\n", + __FUNCTION__, WaitStateChange->NewState); + return; + } + else + if (PacketType == PACKET_TYPE_KD_STATE_MANIPULATE) + { + PDBGKD_MANIPULATE_STATE64 ManipulateState = (PDBGKD_MANIPULATE_STATE64)MessageHeader->Buffer; + KdIoPrintf("%s: PACKET_TYPE_KD_STATE_MANIPULATE for ApiNumber %lu\n", + __FUNCTION__, ManipulateState->ApiNumber); + return; + } + + if (PacketType != PACKET_TYPE_KD_DEBUG_IO) + { KdIoPrintf("%s: PacketType %d is UNIMPLEMENTED\n", __FUNCTION__, PacketType); return; } @@ -660,29 +717,26 @@ KdReceivePacket( CHAR MessageBuffer[512]; #endif -#ifndef KDBG - // Polling happens regularly, so check it first. if (PacketType == PACKET_TYPE_KD_POLL_BREAKIN) { - // KdIoPrintf("%s: PacketType %d is refused without KDBG\n", __FUNCTION__, PacketType); + /* We don't support breaks-in */ return KdPacketTimedOut; } -#endif + + if (PacketType == PACKET_TYPE_KD_STATE_MANIPULATE) + { + PDBGKD_MANIPULATE_STATE64 ManipulateState = (PDBGKD_MANIPULATE_STATE64)MessageHeader->Buffer; + RtlZeroMemory(MessageHeader->Buffer, MessageHeader->MaximumLength); + + /* The exception (notified via DbgKdExceptionStateChange in + * KdSendPacket()) cannot be handled: return a failure code */ + ManipulateState->ApiNumber = DbgKdContinueApi; + ManipulateState->u.Continue.ContinueStatus = DBG_EXCEPTION_NOT_HANDLED; + return KdPacketReceived; + } if (PacketType != PACKET_TYPE_KD_DEBUG_IO) { -#ifndef KDBG - if (PacketType == PACKET_TYPE_KD_STATE_MANIPULATE) - { - PDBGKD_MANIPULATE_STATE64 ManipulateState = (PDBGKD_MANIPULATE_STATE64)MessageHeader->Buffer; - - // KdIoPrintf("%s: PacketType %d is ignored without KDBG\n", __FUNCTION__, PacketType); - ManipulateState->ApiNumber = DbgKdContinueApi; - ManipulateState->u.Continue.ContinueStatus = STATUS_SUCCESS; - return KdPacketReceived; - } -#endif - KdIoPrintf("%s: PacketType %d is UNIMPLEMENTED\n", __FUNCTION__, PacketType); return KdPacketTimedOut; }