Merge pull request #2340 from RadWolfie/impl-thread-stack-reader

Implement Thread Stack Readers
This commit is contained in:
ergo720 2022-04-06 11:14:52 +02:00 committed by GitHub
commit 4b5bef273d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 201 additions and 19 deletions

View File

@ -163,6 +163,14 @@ inline constexpr uint32_t FLASH_DEVICE4_END = (FLASH_DEVICE4_BASE - 1 + FLA
#define XBOX_MEMORY_SIZE (MiB(64))
#define CHIHIRO_MEMORY_SIZE (MiB(128))
// Common page calculations
#define ROUND_UP_4K(size) (((size) + PAGE_MASK) & (~PAGE_MASK))
#define ROUND_UP(size, alignment) (((size) + (alignment - 1)) & (~(alignment - 1)))
#define ROUND_DOWN_4K(size) ((size) & (~PAGE_MASK))
#define ROUND_DOWN(size, alignment) ((size) & (~(alignment - 1)))
#define CHECK_ALIGNMENT(size, alignment) (((size) % (alignment)) == 0)
#define PAGE_ALIGN(address) ROUND_DOWN_4K(address)
// Windows' address space allocation granularity;
// See https://blogs.msdn.microsoft.com/oldnewthing/20031008-00/?p=42223
const int BLOCK_SIZE = KiB(64);

View File

@ -58,6 +58,37 @@ xbox::dword_xt WINAPI RtlAnsiStringToUnicodeSize(const xbox::STRING *str)
return (str->Length + sizeof(ANSI_NULL)) * sizeof(WCHAR);
}
// Source: ReactOS (excluded DPC stack check)
xbox::boolean_xt RtlpCaptureStackLimits(
IN xbox::ulong_ptr_xt Ebp,
OUT xbox::ulong_ptr_xt* StackBegin,
OUT xbox::ulong_ptr_xt* StackEnd)
{
using namespace xbox;
PKTHREAD Thread = KeGetCurrentThread();
/* Don't even try at ISR level or later */
//if (KeGetCurrentIrql() > DISPATCH_LEVEL) return FALSE;
/* Start with defaults */
*StackBegin = reinterpret_cast<ulong_ptr_xt>(Thread->StackLimit);
*StackEnd = reinterpret_cast<ulong_ptr_xt>(Thread->StackBase);
/* Check if EBP is inside the stack */
if ((*StackBegin <= Ebp) && (Ebp <= *StackEnd)) {
/* Then make the stack start at EBP */
*StackBegin = Ebp;
}
else {
/* We're somewhere else entirely... use EBP for safety */
*StackBegin = Ebp;
*StackEnd = PAGE_ALIGN(*StackBegin);
}
/* Return success */
return TRUE;
}
// ******************************************************************
// * 0x0104 - RtlAnsiStringToUnicodeString()
// ******************************************************************
@ -237,7 +268,7 @@ XBSYSAPI EXPORTNUM(265) xbox::void_xt NTAPI xbox::RtlCaptureContext
mov ebx, [esp + 8] // ebx = ContextRecord;
mov [ebx + CONTEXT.Eax], eax // ContextRecord->Eax = eax;
mov eax, [esp] // eax = original value of ebx
mov eax, [esp] // eax = original value of ebx
mov [ebx + CONTEXT.Ebx], eax // ContextRecord->Ebx = original value of ebx
mov [ebx + CONTEXT.Ecx], ecx // ContextRecord->Ecx = ecx;
mov [ebx + CONTEXT.Edx], edx // ContextRecord->Edx = edx;
@ -263,6 +294,7 @@ XBSYSAPI EXPORTNUM(265) xbox::void_xt NTAPI xbox::RtlCaptureContext
// ******************************************************************
// * 0x010A - RtlCaptureStackBackTrace()
// ******************************************************************
// Source: ReactOS
XBSYSAPI EXPORTNUM(266) xbox::ushort_xt NTAPI xbox::RtlCaptureStackBackTrace
(
IN ulong_xt FramesToSkip,
@ -278,9 +310,48 @@ XBSYSAPI EXPORTNUM(266) xbox::ushort_xt NTAPI xbox::RtlCaptureStackBackTrace
LOG_FUNC_ARG_OUT(BackTraceHash)
LOG_FUNC_END;
LOG_UNIMPLEMENTED();
PVOID Frames[2 * 64];
ulong_xt FrameCount;
ulong_xt Hash = 0;
ushort_xt i;
RETURN(NULL);
/* Skip a frame for the caller */
FramesToSkip++;
/* Don't go past the limit */
if ((FramesToCapture + FramesToSkip) >= 128) {
return 0;
}
/* Do the back trace */
FrameCount = RtlWalkFrameChain(Frames, FramesToCapture + FramesToSkip, 0);
/* Make sure we're not skipping all of them */
if (FrameCount <= FramesToSkip) {
return 0;
}
/* Loop all the frames */
for (i = 0; i < FramesToCapture; i++) {
/* Don't go past the limit */
if ((FramesToSkip + i) >= FrameCount) {
break;
}
/* Save this entry and hash it */
BackTrace[i] = Frames[FramesToSkip + i];
Hash += reinterpret_cast<ulong_xt>(BackTrace[i]);
}
/* Write the hash */
if (BackTraceHash) {
*BackTraceHash = Hash;
}
/* Clear the other entries and return count */
RtlFillMemoryUlong(Frames, 128, 0);
RETURN(i);
}
// ******************************************************************
@ -1035,7 +1106,31 @@ XBSYSAPI EXPORTNUM(288) xbox::void_xt NTAPI xbox::RtlGetCallersAddress
LOG_FUNC_ARG_OUT(CallersCaller)
LOG_FUNC_END;
LOG_UNIMPLEMENTED();
/* Get the tow back trace address */
PVOID BackTrace[2];
ushort_xt FrameCount = RtlCaptureStackBackTrace(2, 2, &BackTrace[0], zeroptr);
/* Only if user want it */
if (CallersAddress != NULL) {
/* only when first frames exist */
if (FrameCount >= 1) {
*CallersAddress = BackTrace[0];
}
else {
*CallersAddress = zeroptr;
}
}
/* Only if user want it */
if (CallersCaller != NULL) {
/* only when second frames exist */
if (FrameCount >= 2) {
*CallersCaller = BackTrace[1];
}
else {
*CallersCaller = zeroptr;
}
}
}
// ******************************************************************
@ -2071,6 +2166,8 @@ XBSYSAPI EXPORTNUM(318) xbox::ushort_xt FASTCALL xbox::RtlUshortByteSwap
// ******************************************************************
// * 0x013F - RtlWalkFrameChain()
// ******************************************************************
// Source: ReactOS (modified to fit in xbox compatibility layer)
// NOTE: From xbox kernel, Flags input is not used.
XBSYSAPI EXPORTNUM(319) xbox::ulong_xt NTAPI xbox::RtlWalkFrameChain
(
OUT PVOID *Callers,
@ -2078,15 +2175,97 @@ XBSYSAPI EXPORTNUM(319) xbox::ulong_xt NTAPI xbox::RtlWalkFrameChain
IN ulong_xt Flags
)
{
ulong_ptr_xt Stack;
/* Get current EBP */
#if defined __GNUC__
__asm__("mov %%ebp, %0" : "=r" (Stack) : );
#elif defined(_MSC_VER)
__asm mov Stack, ebp
#endif
#if 0 // NOTE: Disabled due to __try/__except doesn't like this for some reason.
LOG_FUNC_BEGIN
LOG_FUNC_ARG_OUT(Callers)
LOG_FUNC_ARG(Count)
LOG_FUNC_ARG(Flags)
LOG_FUNC_END;
#endif
LOG_UNIMPLEMENTED();
/* Get the actual safe limits */
ulong_ptr_xt StackBegin, StackEnd;
RtlpCaptureStackLimits(Stack, &StackBegin, &StackEnd);
RETURN(NULL);
ulong_xt i = 0;
/* Use a SEH block for maximum protection */
__try {
/* Loop the frames */
boolean_xt StopSearch = FALSE;
for (i = 0; i < Count; i++) {
/*
* Leave if we're past the stack,
* if we're before the stack,
* or if we've reached ourselves.
*/
if ((Stack >= StackEnd) ||
(!i ? (Stack < StackBegin) : (Stack <= StackBegin)) ||
((StackEnd - Stack) < (2 * sizeof(ulong_ptr_xt))))
{
/* We're done or hit a bad address */
break;
}
/* Get new stack and EIP */
ulong_ptr_xt NewStack = *(ulong_ptr_xt*)Stack;
ulong_xt Eip = *(ulong_ptr_xt*)(Stack + sizeof(ulong_ptr_xt));
/* Check if the new pointer is above the old one and past the end */
if (!((Stack < NewStack) && (NewStack < StackEnd))) {
/* Stop searching after this entry */
StopSearch = TRUE;
}
/* Also make sure that the EIP isn't a stack address */
if ((StackBegin < Eip) && (Eip < StackEnd)) {
break;
}
/* Save this frame */
Callers[i] = reinterpret_cast<PVOID>(Eip);
/* Check if we should continue */
if (StopSearch)
{
/* Return the next index */
i++;
break;
}
/* Move to the next stack */
Stack = NewStack;
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
/* No index */
i = 0;
}
#ifndef ENABLE_KTHREAD_SWITCHING
// HACK: This is necessary to exclude our own PCSTProxy startup function.
if (i) {
ulong_xt Eip = reinterpret_cast<ulong_xt>(Callers[i - 1]);
// Check if the first call is outside of xbe's system memory.
// If so, force exclude it to conform with xbox kernel test suite's result.
// NOTE: This will always occur every time. As the thread's startup function address reside behind KSWITCHFRAME structure.
// Except we are currently using our host's local variable as xbox's stack storage.
if (Eip > g_SystemMaxMemory) {
i--;
Callers[i] = zeroptr;
}
}
#endif
return i;// RETURN(i);
}
// ******************************************************************

View File

@ -37,8 +37,8 @@
/* Global typedefs */
typedef uintptr_t VAddr;
typedef uintptr_t PAddr;
typedef xbox::ulong_ptr_xt VAddr;
typedef xbox::ulong_ptr_xt PAddr;
typedef uint32_t u32;
@ -125,17 +125,12 @@ typedef enum _MmLayout
#define CHIHIRO_PFN_ELEMENT(pfn) (&((PXBOX_PFN)CHIHIRO_PFN_ADDRESS)[pfn])
/* Common page calculations */
#define ROUND_UP_4K(size) (((size) + PAGE_MASK) & (~PAGE_MASK))
#define ROUND_UP(size, alignment) (((size) + (alignment - 1)) & (~(alignment - 1)))
#define ROUND_DOWN_4K(size) ((size) & (~PAGE_MASK))
#define ROUND_DOWN(size, alignment) ((size) & (~(alignment - 1)))
#define CHECK_ALIGNMENT(size, alignment) (((size) % (alignment)) == 0)
#define PAGES_SPANNED(Va, Size) ((ULONG)((((VAddr)(Va) & (PAGE_SIZE - 1)) + (Size) + (PAGE_SIZE - 1)) >> PAGE_SHIFT))
#define PAGES_SPANNED_LARGE(Va, Size) ((ULONG)((((VAddr)(Va) & (LARGE_PAGE_SIZE - 1)) + (Size) + (LARGE_PAGE_SIZE - 1)) >> LARGE_PAGE_SHIFT))
#define BYTE_OFFSET(Va) ((ULONG)((VAddr)(Va) & (PAGE_SIZE - 1)))
#define BYTE_OFFSET_LARGE(Va) ((ULONG)((VAddr)(Va) & (LARGE_PAGE_SIZE - 1)))
#define PAGE_END(Va) (((ULONG_PTR)(Va) & (PAGE_SIZE - 1)) == 0)
// Common page calculations
#define BYTE_OFFSET(Va) ((xbox::ulong_xt)((xbox::ulong_ptr_xt)(Va) & (PAGE_SIZE - 1)))
#define BYTE_OFFSET_LARGE(Va) ((xbox::ulong_xt)((xbox::ulong_ptr_xt)(Va) & (LARGE_PAGE_SIZE - 1)))
#define PAGE_END(Va) (((xbox::ulong_ptr_xt)(Va) & (PAGE_SIZE - 1)) == 0)
#define PAGES_SPANNED(Va, Size) ((xbox::ulong_xt)((((xbox::ulong_ptr_xt)(Va) & (PAGE_SIZE - 1)) + (Size) + (PAGE_SIZE - 1)) >> PAGE_SHIFT))
#define PAGES_SPANNED_LARGE(Va, Size) ((xbox::ulong_xt)((((xbox::ulong_ptr_xt)(Va) & (LARGE_PAGE_SIZE - 1)) + (Size) + (LARGE_PAGE_SIZE - 1)) >> LARGE_PAGE_SHIFT))
/* These macros check if the supplied address is inside a known range */