[NTOS:KD] Revisit KdSendPacket() and KdReceivePacket() for DBGKD_DEBUG_IO. (#4914)

- Use SAL2 annotations.
- KdSendPacket(): Validate DEBUG_IO API call.
- KdReceivePacket(): Take the LengthOfStringRead into account; use
  KdbpReadCommand() to read the input, so that correct line edition
  is available (backspace, etc.)
This commit is contained in:
Hermès Bélusca-Maïto 2022-11-27 19:58:56 +01:00
parent e619f89020
commit 84e32e4e90
No known key found for this signature in database
GPG key ID: 3B2539C65E7B93D0
2 changed files with 68 additions and 100 deletions

View file

@ -37,7 +37,7 @@ CPPORT SerialPortInfo = {0, DEFAULT_DEBUG_BAUD_RATE, 0};
static CHAR KdpScreenLineBuffer[KdpScreenLineLengthDefault + 1] = ""; static CHAR KdpScreenLineBuffer[KdpScreenLineLengthDefault + 1] = "";
static ULONG KdpScreenLineBufferPos = 0, KdpScreenLineLength = 0; static ULONG KdpScreenLineBufferPos = 0, KdpScreenLineLength = 0;
const ULONG KdpDmesgBufferSize = 128 * 1024; // 512*1024; // 5*1024*1024; const ULONG KdpDmesgBufferSize = 128 * 1024; // 512*1024;
PCHAR KdpDmesgBuffer = NULL; PCHAR KdpDmesgBuffer = NULL;
volatile ULONG KdpDmesgCurrentPosition = 0; volatile ULONG KdpDmesgCurrentPosition = 0;
volatile ULONG KdpDmesgFreeBytes = 0; volatile ULONG KdpDmesgFreeBytes = 0;
@ -576,18 +576,34 @@ extern STRING KdbPromptString;
VOID VOID
NTAPI NTAPI
KdSendPacket( KdSendPacket(
IN ULONG PacketType, _In_ ULONG PacketType,
IN PSTRING MessageHeader, _In_ PSTRING MessageHeader,
IN PSTRING MessageData, _In_opt_ PSTRING MessageData,
IN OUT PKD_CONTEXT Context) _Inout_ PKD_CONTEXT Context)
{ {
if (PacketType == PACKET_TYPE_KD_DEBUG_IO) if (PacketType == PACKET_TYPE_KD_DEBUG_IO)
{ {
PSTRING Output = MessageData; ULONG ApiNumber = ((PDBGKD_DEBUG_IO)MessageHeader->Buffer)->ApiNumber;
PLIST_ENTRY CurrentEntry; PLIST_ENTRY CurrentEntry;
PKD_DISPATCH_TABLE CurrentTable; PKD_DISPATCH_TABLE CurrentTable;
if (!KdpDebugMode.Value) return; /* Validate API call */
if (MessageHeader->Length != sizeof(DBGKD_DEBUG_IO))
return;
if ((ApiNumber != DbgKdPrintStringApi) &&
(ApiNumber != DbgKdGetStringApi))
{
return;
}
if (!MessageData)
return;
/* NOTE: MessageData->Length should be equal to
* DebugIo.u.PrintString.LengthOfString, or to
* DebugIo.u.GetString.LengthOfPromptString */
if (!KdpDebugMode.Value)
return;
/* Call the registered handlers */ /* Call the registered handlers */
CurrentEntry = KdProviders.Flink; CurrentEntry = KdProviders.Flink;
@ -599,7 +615,7 @@ KdSendPacket(
KdProvidersList); KdProvidersList);
/* Call it */ /* Call it */
CurrentTable->KdpPrintRoutine(Output->Buffer, Output->Length); CurrentTable->KdpPrintRoutine(MessageData->Buffer, MessageData->Length);
/* Next Table */ /* Next Table */
CurrentEntry = CurrentEntry->Flink; CurrentEntry = CurrentEntry->Flink;
@ -672,20 +688,17 @@ KdSendPacket(
KDSTATUS KDSTATUS
NTAPI NTAPI
KdReceivePacket( KdReceivePacket(
IN ULONG PacketType, _In_ ULONG PacketType,
OUT PSTRING MessageHeader, _Out_ PSTRING MessageHeader,
OUT PSTRING MessageData, _Out_ PSTRING MessageData,
OUT PULONG DataLength, _Out_ PULONG DataLength,
IN OUT PKD_CONTEXT Context) _Inout_ PKD_CONTEXT Context)
{ {
#ifdef KDBG #ifdef KDBG
KIRQL OldIrql; STRING NewLine = RTL_CONSTANT_STRING("\n");
STRING StringChar;
CHAR Response;
USHORT i;
ULONG DummyScanCode;
CHAR MessageBuffer[100];
STRING ResponseString; STRING ResponseString;
PDBGKD_DEBUG_IO DebugIo;
CHAR MessageBuffer[512];
#endif #endif
if (PacketType == PACKET_TYPE_KD_STATE_MANIPULATE) if (PacketType == PACKET_TYPE_KD_STATE_MANIPULATE)
@ -724,99 +737,49 @@ KdReceivePacket(
return KdPacketTimedOut; return KdPacketTimedOut;
#ifdef KDBG #ifdef KDBG
DebugIo = (PDBGKD_DEBUG_IO)MessageHeader->Buffer;
/* Validate API call */
if (MessageHeader->MaximumLength != sizeof(DBGKD_DEBUG_IO))
return KdPacketNeedsResend;
if (DebugIo->ApiNumber != DbgKdGetStringApi)
return KdPacketNeedsResend;
/* NOTE: We cannot use directly MessageData->Buffer here as it points
* to the temporary KdpMessageBuffer scratch buffer that is being
* shared with all the possible I/O KD operations that may happen. */
ResponseString.Buffer = MessageBuffer; ResponseString.Buffer = MessageBuffer;
ResponseString.Length = 0; ResponseString.Length = 0;
ResponseString.MaximumLength = min(sizeof(MessageBuffer), MessageData->MaximumLength); ResponseString.MaximumLength = min(sizeof(MessageBuffer),
StringChar.Buffer = &Response; MessageData->MaximumLength);
StringChar.Length = StringChar.MaximumLength = sizeof(Response); ResponseString.MaximumLength = min(ResponseString.MaximumLength,
DebugIo->u.GetString.LengthOfStringRead);
/* Display the string and print a new line for log neatness */ /* The prompt string has been printed by KdSendPacket; go to
*StringChar.Buffer = '\n'; * new line and print the kdb prompt -- for SYSREG2 support. */
KdpPrintString(&StringChar); KdpPrintString(&NewLine);
KdpPrintString(&KdbPromptString); // Alternatively, use "Input> "
/* Print the kdb prompt */
KdpPrintString(&KdbPromptString);
// TODO: Use an improved KdbpReadCommand() function for our purposes.
/* Acquire the printing spinlock without waiting at raised IRQL */
OldIrql = KdbpAcquireLock(&KdpSerialSpinLock);
if (!(KdbDebugState & KD_DEBUG_KDSERIAL)) if (!(KdbDebugState & KD_DEBUG_KDSERIAL))
KbdDisableMouse(); KbdDisableMouse();
/* Loop the whole string */ /* Read a line of user input and retrieve the length.
for (i = 0; i < ResponseString.MaximumLength; i++) * The output string is NULL-terminated -- documentation states
{ * that DbgPrompt() does not NULL-terminate, but it does. */
/* Check if this is serial debugging mode */ *DataLength = KdbpReadCommand(ResponseString.Buffer,
if (KdbDebugState & KD_DEBUG_KDSERIAL) ResponseString.MaximumLength);
{
/* Get the character from serial */
do
{
Response = KdbpTryGetCharSerial(MAXULONG);
} while (Response == -1);
}
else
{
/* Get the response from the keyboard */
do
{
Response = KdbpTryGetCharKeyboard(&DummyScanCode, MAXULONG);
} while (Response == -1);
}
/* Check for return */
if (Response == '\r')
{
/*
* We might need to discard the next '\n'.
* Wait a bit to make sure we receive it.
*/
KeStallExecutionProcessor(100000);
/* Check the mode */
if (KdbDebugState & KD_DEBUG_KDSERIAL)
{
/* Read and discard the next character, if any */
KdbpTryGetCharSerial(5);
}
else
{
/* Read and discard the next character, if any */
KdbpTryGetCharKeyboard(&DummyScanCode, 5);
}
/*
* Null terminate the output string -- documentation states that
* DbgPrompt does not null terminate, but it does
*/
*(PCHAR)(ResponseString.Buffer + i) = 0;
break;
}
/* Write it back and print it to the log */
*(PCHAR)(ResponseString.Buffer + i) = Response;
KdbpReleaseLock(&KdpSerialSpinLock, OldIrql);
KdpPrintString(&StringChar);
OldIrql = KdbpAcquireLock(&KdpSerialSpinLock);
}
/* Release the spinlock */
KdbpReleaseLock(&KdpSerialSpinLock, OldIrql);
/* Print a new line */
*StringChar.Buffer = '\n';
KdpPrintString(&StringChar);
/* Return the length */
RtlCopyMemory(MessageData->Buffer, ResponseString.Buffer, i);
*DataLength = i;
if (!(KdbDebugState & KD_DEBUG_KDSERIAL)) if (!(KdbDebugState & KD_DEBUG_KDSERIAL))
KbdEnableMouse(); KbdEnableMouse();
/* Return the length */
*DataLength = min(*DataLength, DebugIo->u.GetString.LengthOfStringRead);
MessageData->Length = DebugIo->u.GetString.LengthOfStringRead = *DataLength;
/* Only now we can copy back the data into MessageData->Buffer */
RtlCopyMemory(MessageData->Buffer, ResponseString.Buffer, *DataLength);
#endif #endif
return KdPacketReceived; return KdPacketReceived;
} }

View file

@ -98,6 +98,11 @@ KdbpCliMainLoop(
VOID VOID
KdbpCliInterpretInitFile(VOID); KdbpCliInterpretInitFile(VOID);
SIZE_T
KdbpReadCommand(
_Out_ PCHAR Buffer,
_In_ SIZE_T Size);
VOID VOID
KdbpPager( KdbpPager(
_In_ PCHAR Buffer, _In_ PCHAR Buffer,