[NTOS:KDBG] Rewrite the TSS handling code in the backtrace function, removing limitations (and bugs) of the original code.

CORE-16448, PR #2003. Supersedes PR #1997.

This commit supersedes commit 6c5c7809 (r54503).

The original code was checking for the NMI or Double-Fault TSS by
comparing the current stack-traced EIP address with their corresponding
trap handler address ranges. That method was actually buggy because
nothing was ensuring that the trap handlers were in the "expected" order
in the kernel binary (and in memory).

Instead, we now can handle completely generic nested TSSes, instead of
just the NMI or the Double-Fault ones.
The way we proceed is by performing the full stack backtrace of the
current TSS, then once finished we check whether this TSS is nested
(has a parent). If so we change the (cached) current TSS to the latter,
restarting the backtrace at the parent TSS' latest EIP.

Examples of stack backtraces:
=============================

- General Protection fault:

<snip>

*** Fatal System Error: 0x0000007f
                       (0x0000000D,0x00000000,0x00000000,0x00000000)

Entered debugger on embedded INT3 at 0x0008:0x80953528.
kdb:> bt
Eip:
<ntoskrnl.exe:153529 (sdk/lib/rtl/i386/debug_asm.S:57 (RtlpBreakWithStatusInstruction))>
Frames:
<ntoskrnl.exe:899b0 (ntoskrnl/ke/bug.c:1136 (KeBugCheckWithTf))>
<ntoskrnl.exe:134826 (ntoskrnl/ke/i386/exp.c:1161 (KeRaiseUserException))>
<ntoskrnl.exe:19ae67 (ntoskrnl/ke/i386/traphdlr.c:1282 (KiTrap0DHandler))>
<ntoskrnl.exe:19a840 (:0 (KiTrap0D))>
<ntoskrnl.exe:1925e6 (ntoskrnl/include/internal/i386/intrin_i.h:45 (KiInitMachineDependent))>
<ntoskrnl.exe:187688 (ntoskrnl/ke/krnlinit.c:305 (KeInitSystem))>
<ntoskrnl.exe:17fb2f (ntoskrnl/ex/init.c:1621 (Phase1InitializationDiscard))>
<ntoskrnl.exe:3247f (ntoskrnl/ex/init.c:2019 (Phase1Initialization))>
<ntoskrnl.exe:11c079 (ntoskrnl/ps/thread.c:156 (PspSystemThreadStartup))>
<ntoskrnl.exe:135c8a (ntoskrnl/ke/i386/thrdini.c:78 (KiThreadStartup))>
<ntoskrnl.exe:11c040 (ntoskrnl/ps/thread.c:141 (PspSystemThreadStartup))>
<5d8950ec>
Couldn't access memory at 0x83E58959!

</snip>

- Double-fault (manually triggered by removing the GP handler):

Note how the backtrace explicitly specifies the crossed TSS boundaries,
and the trace in the parent TSS is indeed consistent with the previous
example. Note also that log2lines (used here to completely resolve the
trace) failed to see KiTrap08Handler(), which has been instead mistaken
for KiTrap09().

<snip>

*** Fatal System Error: 0x0000007f
                       (0x00000008,0x8009C000,0x00000000,0x00000000)

Entered debugger on embedded INT3 at 0x0008:0x80953528.
kdb:> bt
[Active TSS 0x0050 @ 0x80A10CA0]
Eip:
<ntoskrnl.exe:153529 (sdk/lib/rtl/i386/debug_asm.S:57 (RtlpBreakWithStatusInstruction))>
Frames:
<ntoskrnl.exe:899b0 (ntoskrnl/ke/bug.c:1136 (KeBugCheckWithTf))>
<ntoskrnl.exe:19a1d8 (ntoskrnl/ke/i386/traphdlr.c:917 (KiTrap09))>      // <-- Here, log2lines fails to see it's actually KiTrap08Handler.
<ntoskrnl.exe:19a145 (:0 (KiTrap08))>
[Parent TSS 0x0028 @ 0x8009C000]
<ntoskrnl.exe:1925e6 (ntoskrnl/include/internal/i386/intrin_i.h:45 (KiInitMachineDependent))>
<ntoskrnl.exe:187688 (ntoskrnl/ke/krnlinit.c:305 (KeInitSystem))>
<ntoskrnl.exe:17fb2f (ntoskrnl/ex/init.c:1621 (Phase1InitializationDiscard))>
<ntoskrnl.exe:3247f (ntoskrnl/ex/init.c:2019 (Phase1Initialization))>
<ntoskrnl.exe:11c079 (ntoskrnl/ps/thread.c:156 (PspSystemThreadStartup))>
<ntoskrnl.exe:135c8a (ntoskrnl/ke/i386/thrdini.c:78 (KiThreadStartup))>
<ntoskrnl.exe:11c040 (ntoskrnl/ps/thread.c:141 (PspSystemThreadStartup))>
<5d8950ec>
Couldn't access memory at 0x83E58959!

</snip>
This commit is contained in:
Hermès Bélusca-Maïto 2019-10-28 01:01:23 +01:00
parent f9e4d81056
commit d21ff0ed13
No known key found for this signature in database
GPG key ID: 3B2539C65E7B93D0

View file

@ -1033,86 +1033,73 @@ KdbpRetrieveTss(
return Tss;
}
static BOOLEAN
KdbpTrapFrameFromPrevTss(
PKTRAP_FRAME TrapFrame)
FORCEINLINE BOOLEAN
KdbpIsNestedTss(
IN USHORT TssSelector,
IN PKTSS Tss)
{
ULONG_PTR Eip, Ebp;
KDESCRIPTOR Gdtr;
KGDTENTRY Desc;
USHORT Sel;
PKTSS Tss;
USHORT Backlink;
Ke386GetGlobalDescriptorTable(&Gdtr.Limit);
Sel = Ke386GetTr();
if ((Sel & (sizeof(KGDTENTRY) - 1)) ||
(Sel < sizeof(KGDTENTRY)) ||
(Sel + sizeof(KGDTENTRY) - 1 > Gdtr.Limit))
if (!Tss)
return FALSE;
if (!NT_SUCCESS(KdbpSafeReadMemory(&Desc,
(PVOID)(Gdtr.Base + Sel),
sizeof(KGDTENTRY))))
return FALSE;
if (Desc.HighWord.Bits.Type != 0xB)
return FALSE;
Tss = (PKTSS)(ULONG_PTR)(Desc.BaseLow |
Desc.HighWord.Bytes.BaseMid << 16 |
Desc.HighWord.Bytes.BaseHi << 24);
if (!NT_SUCCESS(KdbpSafeReadMemory(&Sel,
/* Retrieve the TSS Backlink */
if (!NT_SUCCESS(KdbpSafeReadMemory(&Backlink,
(PVOID)&Tss->Backlink,
sizeof(USHORT))))
{
return FALSE;
}
if ((Sel & (sizeof(KGDTENTRY) - 1)) ||
(Sel < sizeof(KGDTENTRY)) ||
(Sel + sizeof(KGDTENTRY) - 1 > Gdtr.Limit))
return (Backlink != 0 && Backlink != TssSelector);
}
static BOOLEAN
KdbpTrapFrameFromPrevTss(
IN OUT PKTRAP_FRAME TrapFrame,
OUT PUSHORT TssSelector,
IN OUT PKTSS* pTss,
IN PKDESCRIPTOR pGdtr)
{
ULONG_PTR Eip, Ebp;
USHORT Backlink;
PKTSS Tss = *pTss;
/* Retrieve the TSS Backlink */
if (!NT_SUCCESS(KdbpSafeReadMemory(&Backlink,
(PVOID)&Tss->Backlink,
sizeof(USHORT))))
{
return FALSE;
}
if (!NT_SUCCESS(KdbpSafeReadMemory(&Desc,
(PVOID)(Gdtr.Base + Sel),
sizeof(KGDTENTRY))))
/* Retrieve the parent TSS */
Tss = KdbpRetrieveTss(Backlink, NULL, pGdtr);
if (!Tss)
return FALSE;
if (Desc.HighWord.Bits.Type != 0xB)
return FALSE;
Tss = (PKTSS)(ULONG_PTR)(Desc.BaseLow |
Desc.HighWord.Bytes.BaseMid << 16 |
Desc.HighWord.Bytes.BaseHi << 24);
if (!NT_SUCCESS(KdbpSafeReadMemory(&Eip,
(PVOID)&Tss->Eip,
sizeof(ULONG_PTR))))
{
return FALSE;
}
if (!NT_SUCCESS(KdbpSafeReadMemory(&Ebp,
(PVOID)&Tss->Ebp,
sizeof(ULONG_PTR))))
{
return FALSE;
}
/* Return the parent TSS and its trap frame */
*TssSelector = Backlink;
*pTss = Tss;
TrapFrame->Eip = Eip;
TrapFrame->Ebp = Ebp;
return TRUE;
}
VOID __cdecl KiTrap02(VOID);
VOID FASTCALL KiTrap03Handler(IN PKTRAP_FRAME);
VOID __cdecl KiTrap08(VOID);
VOID __cdecl KiTrap09(VOID);
static BOOLEAN
KdbpInNmiOrDoubleFaultHandler(
ULONG_PTR Address)
{
return (Address > (ULONG_PTR)KiTrap02 && Address < (ULONG_PTR)KiTrap03Handler) ||
(Address > (ULONG_PTR)KiTrap08 && Address < (ULONG_PTR)KiTrap09);
}
/*!\brief Displays a backtrace.
*/
static BOOLEAN
@ -1122,15 +1109,17 @@ KdbpCmdBackTrace(
{
ULONG ul;
ULONGLONG Result = 0;
ULONG_PTR Frame = KdbCurrentTrapFrame->Tf.Ebp;
KTRAP_FRAME TrapFrame = KdbCurrentTrapFrame->Tf;
ULONG_PTR Frame = TrapFrame.Ebp;
ULONG_PTR Address;
KTRAP_FRAME TrapFrame;
KDESCRIPTOR Gdtr;
USHORT TssSelector;
PKTSS Tss;
if (Argc >= 2)
{
/* Check for [L count] part */
ul = 0;
if (strcmp(Argv[Argc-2], "L") == 0)
{
ul = strtoul(Argv[Argc-1], NULL, 0);
@ -1157,7 +1146,7 @@ KdbpCmdBackTrace(
Argc++;
}
/* Check if frame addr or thread id is given. */
/* Check if a Frame Address or Thread ID is given */
if (Argc > 1)
{
if (Argv[1][0] == '*')
@ -1169,7 +1158,7 @@ KdbpCmdBackTrace(
return TRUE;
if (Result > (ULONGLONG)(~((ULONG_PTR)0)))
KdbpPrint("Warning: Address %I64x is beeing truncated\n",Result);
KdbpPrint("Warning: Address %I64x is beeing truncated\n", Result);
Frame = (ULONG_PTR)Result;
}
@ -1179,66 +1168,95 @@ KdbpCmdBackTrace(
return TRUE;
}
}
else
/* Retrieve the Global Descriptor Table */
Ke386GetGlobalDescriptorTable(&Gdtr.Limit);
/* Retrieve the current (active) TSS */
TssSelector = Ke386GetTr();
Tss = KdbpRetrieveTss(TssSelector, NULL, &Gdtr);
if (KdbpIsNestedTss(TssSelector, Tss))
{
/* Display the active TSS if it is nested */
KdbpPrint("[Active TSS 0x%04x @ 0x%p]\n", TssSelector, Tss);
}
/* If no Frame Address or Thread ID was given, try printing the function at EIP */
if (Argc <= 1)
{
KdbpPrint("Eip:\n");
/* Try printing the function at EIP */
if (!KdbSymPrintAddress((PVOID)KdbCurrentTrapFrame->Tf.Eip, &KdbCurrentTrapFrame->Tf))
KdbpPrint("<%08x>\n", KdbCurrentTrapFrame->Tf.Eip);
if (!KdbSymPrintAddress((PVOID)TrapFrame.Eip, &TrapFrame))
KdbpPrint("<%08x>\n", TrapFrame.Eip);
else
KdbpPrint("\n");
}
TrapFrame = KdbCurrentTrapFrame->Tf;
/* Walk through the frames */
KdbpPrint("Frames:\n");
for (;;)
{
BOOLEAN GotNextFrame;
if (Frame == 0)
break;
goto CheckForParentTSS;
if (!NT_SUCCESS(KdbpSafeReadMemory(&Address, (PVOID)(Frame + sizeof(ULONG_PTR)), sizeof (ULONG_PTR))))
Address = 0;
if (!NT_SUCCESS(KdbpSafeReadMemory(&Address, (PVOID)(Frame + sizeof(ULONG_PTR)), sizeof(ULONG_PTR))))
{
KdbpPrint("Couldn't access memory at 0x%p!\n", Frame + sizeof(ULONG_PTR));
break;
goto CheckForParentTSS;
}
if ((GotNextFrame = NT_SUCCESS(KdbpSafeReadMemory(&Frame, (PVOID)Frame, sizeof (ULONG_PTR)))))
TrapFrame.Ebp = Frame;
if (Address == 0)
goto CheckForParentTSS;
/* Print the location of the call instruction */
GotNextFrame = NT_SUCCESS(KdbpSafeReadMemory(&Frame, (PVOID)Frame, sizeof(ULONG_PTR)));
if (GotNextFrame)
TrapFrame.Ebp = Frame;
// else
// Frame = 0;
/* Print the location of the call instruction (assumed 5 bytes length) */
if (!KdbSymPrintAddress((PVOID)(Address - 5), &TrapFrame))
KdbpPrint("<%08x>\n", Address);
else
KdbpPrint("\n");
if (KdbOutputAborted) break;
if (Address == 0)
if (KdbOutputAborted)
break;
if (KdbpInNmiOrDoubleFaultHandler(Address))
{
if ((GotNextFrame = KdbpTrapFrameFromPrevTss(&TrapFrame)))
{
Address = TrapFrame.Eip;
Frame = TrapFrame.Ebp;
if (!KdbSymPrintAddress((PVOID)Address, &TrapFrame))
KdbpPrint("<%08x>\n", Address);
else
KdbpPrint("\n");
}
}
if (!GotNextFrame)
{
KdbpPrint("Couldn't access memory at 0x%p!\n", Frame);
break;
goto CheckForParentTSS; // break;
}
continue;
CheckForParentTSS:
/*
* We have ended the stack walking for the current (active) TSS.
* Check whether this TSS was nested, and if so switch to its parent
* and walk its stack.
*/
if (!KdbpIsNestedTss(TssSelector, Tss))
break; // The TSS is not nested, we stop there.
GotNextFrame = KdbpTrapFrameFromPrevTss(&TrapFrame, &TssSelector, &Tss, &Gdtr);
if (!GotNextFrame)
{
KdbpPrint("Couldn't access parent TSS 0x%04x\n", Tss->Backlink);
break; // Cannot retrieve the parent TSS, we stop there.
}
Address = TrapFrame.Eip;
Frame = TrapFrame.Ebp;
KdbpPrint("[Parent TSS 0x%04x @ 0x%p]\n", TssSelector, Tss);
if (!KdbSymPrintAddress((PVOID)Address, &TrapFrame))
KdbpPrint("<%08x>\n", Address);
else
KdbpPrint("\n");
}
return TRUE;