reactos/ntoskrnl/rtl/libsupp.c
jimtabor b784074c2f [0.4.13][WIN32SS][RTL] Fix regression CORE-16769 + BSOD CORE-13907 & CORE-14857
Thanks to Jim Tabor, Mark Jansen & Thomas Faber
By squashed backmerge of the following commits:
--------------
[NTUSER] Fix Strings and Format to Hooks

Allocate heap instead of data segment to be used for callbacks on user side.
Move and correct initial hook call out setup. Use it in more than one hook call.

This fixes issues with strings out of alignment and use of kernel pointers.

See CORE-13907 and CORE-16769. KsStudio still needs retested.

Small wow update.

cherry picked from commit 0.4.14-dev-1287-g
568b6d0558
--------------
[RTL] Introduce RtlpImageNtHeader,
which implements the required functionality.
ntdll and ntoskrnl now have a wrapper for this, with SEH.
This protects the function against malformed / bad images,
whilst still being able to use the code in freeldr et al.
Idea from Thomas.
CORE-14857

cherry picked from commit 0.4.14-dev-1255-g
177ae91bf6
--------------
[WIN32SS] Form Sanity to Hook Callbacks

Fix WH_CALLWNDPROC/RET data to user hook calls. See CORE-13019 and CORE-13907.

cherry picked from commit 0.4.14-dev-1241-g
915a5764a9
--------------
[USER32] Fix null return.

See CORE-16769.

cherry picked from commit 0.4.14-dev-1240-g
d8add40e89
2020-04-03 14:51:10 +02:00

827 lines
20 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: ntoskrnl/rtl/libsupp.c
* PURPOSE: RTL Support Routines
* PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
* Gunnar Dalsnes
*/
/* INCLUDES ******************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
#define TAG_ATMT 'TotA' /* Atom table */
#define TAG_RTHL 'LHtR' /* Heap Lock */
extern ULONG NtGlobalFlag;
typedef struct _RTL_RANGE_ENTRY
{
LIST_ENTRY Entry;
RTL_RANGE Range;
} RTL_RANGE_ENTRY, *PRTL_RANGE_ENTRY;
PAGED_LOOKASIDE_LIST RtlpRangeListEntryLookasideList;
SIZE_T RtlpAllocDeallocQueryBufferSize = 128;
/* FUNCTIONS *****************************************************************/
PVOID
NTAPI
RtlPcToFileHeader(
IN PVOID PcValue,
OUT PVOID *BaseOfImage)
{
PLDR_DATA_TABLE_ENTRY LdrEntry;
BOOLEAN InSystem;
/* Get the base for this file */
if ((ULONG_PTR)PcValue > (ULONG_PTR)MmHighestUserAddress)
{
/* We are in kernel */
*BaseOfImage = KiPcToFileHeader(PcValue, &LdrEntry, FALSE, &InSystem);
}
else
{
/* We are in user land */
*BaseOfImage = KiRosPcToUserFileHeader(PcValue, &LdrEntry);
}
return *BaseOfImage;
}
VOID
NTAPI
RtlInitializeRangeListPackage(VOID)
{
/* Setup the lookaside list for allocations (not used yet) */
ExInitializePagedLookasideList(&RtlpRangeListEntryLookasideList,
NULL,
NULL,
POOL_COLD_ALLOCATION,
sizeof(RTL_RANGE_ENTRY),
'elRR',
16);
}
BOOLEAN
NTAPI
RtlpCheckForActiveDebugger(VOID)
{
/* This check is meaningless in kernel-mode */
return FALSE;
}
BOOLEAN
NTAPI
RtlpSetInDbgPrint(VOID)
{
/* Nothing to set in kernel mode */
return FALSE;
}
VOID
NTAPI
RtlpClearInDbgPrint(VOID)
{
/* Nothing to clear in kernel mode */
}
KPROCESSOR_MODE
NTAPI
RtlpGetMode(VOID)
{
return KernelMode;
}
PVOID
NTAPI
RtlpAllocateMemory(ULONG Bytes,
ULONG Tag)
{
return ExAllocatePoolWithTag(PagedPool,
(SIZE_T)Bytes,
Tag);
}
#define TAG_USTR 'RTSU'
#define TAG_ASTR 'RTSA'
#define TAG_OSTR 'RTSO'
VOID
NTAPI
RtlpFreeMemory(PVOID Mem,
ULONG Tag)
{
if (Tag == TAG_ASTR || Tag == TAG_OSTR || Tag == TAG_USTR)
ExFreePool(Mem);
else
ExFreePoolWithTag(Mem, Tag);
}
/*
* @implemented
*/
VOID NTAPI
RtlAcquirePebLock(VOID)
{
}
/*
* @implemented
*/
VOID NTAPI
RtlReleasePebLock(VOID)
{
}
NTSTATUS
NTAPI
LdrShutdownThread(VOID)
{
return STATUS_SUCCESS;
}
PPEB
NTAPI
RtlGetCurrentPeb(VOID)
{
return ((PEPROCESS)(KeGetCurrentThread()->ApcState.Process))->Peb;
}
NTSTATUS
NTAPI
RtlDeleteHeapLock(IN OUT PHEAP_LOCK Lock)
{
ExDeleteResourceLite(&Lock->Resource);
ExFreePoolWithTag(Lock, TAG_RTHL);
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
RtlEnterHeapLock(IN OUT PHEAP_LOCK Lock, IN BOOLEAN Exclusive)
{
KeEnterCriticalRegion();
if (Exclusive)
ExAcquireResourceExclusiveLite(&Lock->Resource, TRUE);
else
ExAcquireResourceSharedLite(&Lock->Resource, TRUE);
return STATUS_SUCCESS;
}
BOOLEAN
NTAPI
RtlTryEnterHeapLock(IN OUT PHEAP_LOCK Lock, IN BOOLEAN Exclusive)
{
BOOLEAN Success;
KeEnterCriticalRegion();
if (Exclusive)
Success = ExAcquireResourceExclusiveLite(&Lock->Resource, FALSE);
else
Success = ExAcquireResourceSharedLite(&Lock->Resource, FALSE);
if (!Success)
KeLeaveCriticalRegion();
return Success;
}
NTSTATUS
NTAPI
RtlInitializeHeapLock(IN OUT PHEAP_LOCK *Lock)
{
PHEAP_LOCK HeapLock = ExAllocatePoolWithTag(NonPagedPool,
sizeof(HEAP_LOCK),
TAG_RTHL);
if (HeapLock == NULL)
return STATUS_NO_MEMORY;
ExInitializeResourceLite(&HeapLock->Resource);
*Lock = HeapLock;
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
RtlLeaveHeapLock(IN OUT PHEAP_LOCK Lock)
{
ExReleaseResourceLite(&Lock->Resource);
KeLeaveCriticalRegion();
return STATUS_SUCCESS;
}
struct _HEAP;
VOID
NTAPI
RtlpAddHeapToProcessList(struct _HEAP *Heap)
{
UNREFERENCED_PARAMETER(Heap);
}
VOID
NTAPI
RtlpRemoveHeapFromProcessList(struct _HEAP *Heap)
{
UNREFERENCED_PARAMETER(Heap);
}
VOID
RtlInitializeHeapManager(VOID)
{
}
#if DBG
VOID FASTCALL
CHECK_PAGED_CODE_RTL(char *file, int line)
{
if(KeGetCurrentIrql() > APC_LEVEL)
{
DbgPrint("%s:%i: Pagable code called at IRQL > APC_LEVEL (%u)\n", file, line, KeGetCurrentIrql());
ASSERT(FALSE);
}
}
#endif
VOID
NTAPI
RtlpSetHeapParameters(IN PRTL_HEAP_PARAMETERS Parameters)
{
/* Apply defaults for non-set parameters */
if (!Parameters->SegmentCommit) Parameters->SegmentCommit = MmHeapSegmentCommit;
if (!Parameters->SegmentReserve) Parameters->SegmentReserve = MmHeapSegmentReserve;
if (!Parameters->DeCommitFreeBlockThreshold) Parameters->DeCommitFreeBlockThreshold = MmHeapDeCommitFreeBlockThreshold;
if (!Parameters->DeCommitTotalFreeThreshold) Parameters->DeCommitTotalFreeThreshold = MmHeapDeCommitTotalFreeThreshold;
}
VOID
NTAPI
RtlpCheckLogException(IN PEXCEPTION_RECORD ExceptionRecord,
IN PCONTEXT ContextRecord,
IN PVOID ContextData,
IN ULONG Size)
{
/* Check the global flag */
if (NtGlobalFlag & FLG_ENABLE_EXCEPTION_LOGGING)
{
/* FIXME: Log this exception */
}
}
BOOLEAN
NTAPI
RtlpHandleDpcStackException(IN PEXCEPTION_REGISTRATION_RECORD RegistrationFrame,
IN ULONG_PTR RegistrationFrameEnd,
IN OUT PULONG_PTR StackLow,
IN OUT PULONG_PTR StackHigh)
{
PKPRCB Prcb;
ULONG_PTR DpcStack;
/* Check if we are at DISPATCH or higher */
if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
{
/* Get the PRCB and DPC Stack */
Prcb = KeGetCurrentPrcb();
DpcStack = (ULONG_PTR)Prcb->DpcStack;
/* Check if we are in a DPC and the stack matches */
if ((Prcb->DpcRoutineActive) &&
(RegistrationFrameEnd <= DpcStack) &&
((ULONG_PTR)RegistrationFrame >= DpcStack - KERNEL_STACK_SIZE))
{
/* Update the limits to the DPC Stack's */
*StackHigh = DpcStack;
*StackLow = DpcStack - KERNEL_STACK_SIZE;
return TRUE;
}
}
/* Not in DPC stack */
return FALSE;
}
#if !defined(_ARM_) && !defined(_AMD64_)
BOOLEAN
NTAPI
RtlpCaptureStackLimits(IN ULONG_PTR Ebp,
IN ULONG_PTR *StackBegin,
IN ULONG_PTR *StackEnd)
{
PKTHREAD Thread = KeGetCurrentThread();
/* Don't even try at ISR level or later */
if (KeGetCurrentIrql() > DISPATCH_LEVEL) return FALSE;
/* Start with defaults */
*StackBegin = Thread->StackLimit;
*StackEnd = (ULONG_PTR)Thread->StackBase;
/* Check if EBP is inside the stack */
if ((*StackBegin <= Ebp) && (Ebp <= *StackEnd))
{
/* Then make the stack start at EBP */
*StackBegin = Ebp;
}
else
{
/* Now we're going to assume we're on the DPC stack */
*StackEnd = (ULONG_PTR)(KeGetPcr()->Prcb->DpcStack);
*StackBegin = *StackEnd - KERNEL_STACK_SIZE;
/* Check if we seem to be on the DPC stack */
if ((*StackEnd) && (*StackBegin < Ebp) && (Ebp <= *StackEnd))
{
/* We're on the DPC stack */
*StackBegin = Ebp;
}
else
{
/* We're somewhere else entirely... use EBP for safety */
*StackBegin = Ebp;
*StackEnd = (ULONG_PTR)PAGE_ALIGN(*StackBegin);
}
}
/* Return success */
return TRUE;
}
/*
* @implemented
*/
ULONG
NTAPI
RtlWalkFrameChain(OUT PVOID *Callers,
IN ULONG Count,
IN ULONG Flags)
{
ULONG_PTR Stack, NewStack, StackBegin, StackEnd = 0;
ULONG Eip;
BOOLEAN Result, StopSearch = FALSE;
ULONG i = 0;
PETHREAD Thread = PsGetCurrentThread();
PTEB Teb;
PKTRAP_FRAME TrapFrame;
/* Get current EBP */
#if defined(_M_IX86)
#if defined __GNUC__
__asm__("mov %%ebp, %0" : "=r" (Stack) : );
#elif defined(_MSC_VER)
__asm mov Stack, ebp
#endif
#elif defined(_M_MIPS)
__asm__("move $sp, %0" : "=r" (Stack) : );
#elif defined(_M_PPC)
__asm__("mr %0,1" : "=r" (Stack) : );
#elif defined(_M_ARM)
__asm__("mov sp, %0" : "=r"(Stack) : );
#else
#error Unknown architecture
#endif
/* Set it as the stack begin limit as well */
StackBegin = (ULONG_PTR)Stack;
/* Check if we're called for non-logging mode */
if (!Flags)
{
/* Get the actual safe limits */
Result = RtlpCaptureStackLimits((ULONG_PTR)Stack,
&StackBegin,
&StackEnd);
if (!Result) return 0;
}
/* Use a SEH block for maximum protection */
_SEH2_TRY
{
/* Check if we want the user-mode stack frame */
if (Flags == 1)
{
/* Get the trap frame and TEB */
TrapFrame = KeGetTrapFrame(&Thread->Tcb);
Teb = Thread->Tcb.Teb;
/* Make sure we can trust the TEB and trap frame */
if (!(Teb) ||
(KeIsAttachedProcess()) ||
(KeGetCurrentIrql() >= DISPATCH_LEVEL))
{
/* Invalid or unsafe attempt to get the stack */
_SEH2_YIELD(return 0;)
}
/* Get the stack limits */
StackBegin = (ULONG_PTR)Teb->NtTib.StackLimit;
StackEnd = (ULONG_PTR)Teb->NtTib.StackBase;
#ifdef _M_IX86
Stack = TrapFrame->Ebp;
#elif defined(_M_PPC)
Stack = TrapFrame->Gpr1;
#else
#error Unknown architecture
#endif
/* Validate them */
if (StackEnd <= StackBegin) _SEH2_YIELD(return 0);
ProbeForRead((PVOID)StackBegin,
StackEnd - StackBegin,
sizeof(CHAR));
}
/* Loop the frames */
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))))
{
/* We're done or hit a bad address */
break;
}
/* Get new stack and EIP */
NewStack = *(PULONG_PTR)Stack;
Eip = *(PULONG_PTR)(Stack + sizeof(ULONG_PTR));
/* Check if the new pointer is above the oldone 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;
/* Check if we reached a user-mode address */
if (!(Flags) && !(Eip & 0x80000000)) break; // FIXME: 3GB breakage
/* Save this frame */
Callers[i] = (PVOID)Eip;
/* Check if we should continue */
if (StopSearch)
{
/* Return the next index */
i++;
break;
}
/* Move to the next stack */
Stack = NewStack;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* No index */
i = 0;
}
_SEH2_END;
/* Return frames parsed */
return i;
}
#endif
#if defined(_M_AMD64) || defined(_M_ARM)
VOID
NTAPI
RtlpGetStackLimits(
OUT PULONG_PTR LowLimit,
OUT PULONG_PTR HighLimit)
{
PKTHREAD CurrentThread = KeGetCurrentThread();
*HighLimit = (ULONG_PTR)CurrentThread->InitialStack;
*LowLimit = (ULONG_PTR)CurrentThread->StackLimit;
}
#endif
/* RTL Atom Tables ************************************************************/
NTSTATUS
RtlpInitAtomTableLock(PRTL_ATOM_TABLE AtomTable)
{
ExInitializeFastMutex(&AtomTable->FastMutex);
return STATUS_SUCCESS;
}
VOID
RtlpDestroyAtomTableLock(PRTL_ATOM_TABLE AtomTable)
{
}
BOOLEAN
RtlpLockAtomTable(PRTL_ATOM_TABLE AtomTable)
{
ExAcquireFastMutex(&AtomTable->FastMutex);
return TRUE;
}
VOID
RtlpUnlockAtomTable(PRTL_ATOM_TABLE AtomTable)
{
ExReleaseFastMutex(&AtomTable->FastMutex);
}
BOOLEAN
RtlpCreateAtomHandleTable(PRTL_ATOM_TABLE AtomTable)
{
AtomTable->ExHandleTable = ExCreateHandleTable(NULL);
return (AtomTable->ExHandleTable != NULL);
}
BOOLEAN
NTAPI
RtlpCloseHandleCallback(
IN PHANDLE_TABLE_ENTRY HandleTableEntry,
IN HANDLE Handle,
IN PVOID HandleTable)
{
/* Destroy and unlock the handle entry */
return ExDestroyHandle(HandleTable, Handle, HandleTableEntry);
}
VOID
RtlpDestroyAtomHandleTable(PRTL_ATOM_TABLE AtomTable)
{
if (AtomTable->ExHandleTable)
{
ExSweepHandleTable(AtomTable->ExHandleTable,
RtlpCloseHandleCallback,
AtomTable->ExHandleTable);
ExDestroyHandleTable(AtomTable->ExHandleTable, NULL);
AtomTable->ExHandleTable = NULL;
}
}
PRTL_ATOM_TABLE
RtlpAllocAtomTable(ULONG Size)
{
PRTL_ATOM_TABLE Table = ExAllocatePoolWithTag(NonPagedPool,
Size,
TAG_ATMT);
if (Table != NULL)
{
RtlZeroMemory(Table,
Size);
}
return Table;
}
VOID
RtlpFreeAtomTable(PRTL_ATOM_TABLE AtomTable)
{
ExFreePoolWithTag(AtomTable, TAG_ATMT);
}
PRTL_ATOM_TABLE_ENTRY
RtlpAllocAtomTableEntry(ULONG Size)
{
PRTL_ATOM_TABLE_ENTRY Entry;
Entry = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_ATMT);
if (Entry != NULL)
{
RtlZeroMemory(Entry, Size);
}
return Entry;
}
VOID
RtlpFreeAtomTableEntry(PRTL_ATOM_TABLE_ENTRY Entry)
{
ExFreePoolWithTag(Entry, TAG_ATMT);
}
VOID
RtlpFreeAtomHandle(PRTL_ATOM_TABLE AtomTable, PRTL_ATOM_TABLE_ENTRY Entry)
{
ExDestroyHandle(AtomTable->ExHandleTable,
(HANDLE)((ULONG_PTR)Entry->HandleIndex << 2),
NULL);
}
BOOLEAN
RtlpCreateAtomHandle(PRTL_ATOM_TABLE AtomTable, PRTL_ATOM_TABLE_ENTRY Entry)
{
HANDLE_TABLE_ENTRY ExEntry;
HANDLE Handle;
USHORT HandleIndex;
/* Initialize ex handle table entry */
ExEntry.Object = Entry;
ExEntry.GrantedAccess = 0x1; /* FIXME - valid handle */
/* Create ex handle */
Handle = ExCreateHandle(AtomTable->ExHandleTable,
&ExEntry);
if (!Handle) return FALSE;
/* Calculate HandleIndex (by getting rid of the first two bits) */
HandleIndex = (USHORT)((ULONG_PTR)Handle >> 2);
/* Index must be less than 0xC000 */
if (HandleIndex >= 0xC000)
{
/* Destroy ex handle */
ExDestroyHandle(AtomTable->ExHandleTable,
Handle,
NULL);
/* Return failure */
return FALSE;
}
/* Initialize atom table entry */
Entry->HandleIndex = HandleIndex;
Entry->Atom = 0xC000 + HandleIndex;
/* Return success */
return TRUE;
}
PRTL_ATOM_TABLE_ENTRY
RtlpGetAtomEntry(PRTL_ATOM_TABLE AtomTable, ULONG Index)
{
PHANDLE_TABLE_ENTRY ExEntry;
PRTL_ATOM_TABLE_ENTRY Entry = NULL;
/* NOTE: There's no need to explicitly enter a critical region because it's
guaranteed that we're in a critical region right now (as we hold
the atom table lock) */
ExEntry = ExMapHandleToPointer(AtomTable->ExHandleTable,
(HANDLE)((ULONG_PTR)Index << 2));
if (ExEntry != NULL)
{
Entry = ExEntry->Object;
ExUnlockHandleTableEntry(AtomTable->ExHandleTable,
ExEntry);
}
return Entry;
}
/* Ldr SEH-Protected access to IMAGE_NT_HEADERS */
/* Rtl SEH-Free version of this */
NTSTATUS
NTAPI
RtlpImageNtHeaderEx(
_In_ ULONG Flags,
_In_ PVOID Base,
_In_ ULONG64 Size,
_Out_ PIMAGE_NT_HEADERS *OutHeaders);
/*
* @implemented
* @note: This is here, so that we do not drag SEH into rosload, freeldr and bootmgfw
*/
NTSTATUS
NTAPI
RtlImageNtHeaderEx(
_In_ ULONG Flags,
_In_ PVOID Base,
_In_ ULONG64 Size,
_Out_ PIMAGE_NT_HEADERS *OutHeaders)
{
NTSTATUS Status;
/* Assume failure. This is also done in RtlpImageNtHeaderEx, but this is guarded by SEH. */
if (OutHeaders != NULL)
*OutHeaders = NULL;
_SEH2_TRY
{
Status = RtlpImageNtHeaderEx(Flags, Base, Size, OutHeaders);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Fail with the SEH error */
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
return Status;
}
/*
* Ldr Resource support code
*/
IMAGE_RESOURCE_DIRECTORY *find_entry_by_name( IMAGE_RESOURCE_DIRECTORY *dir,
LPCWSTR name, void *root,
int want_dir );
IMAGE_RESOURCE_DIRECTORY *find_entry_by_id( IMAGE_RESOURCE_DIRECTORY *dir,
USHORT id, void *root, int want_dir );
IMAGE_RESOURCE_DIRECTORY *find_first_entry( IMAGE_RESOURCE_DIRECTORY *dir,
void *root, int want_dir );
/**********************************************************************
* find_entry
*
* Find a resource entry
*/
NTSTATUS find_entry( PVOID BaseAddress, LDR_RESOURCE_INFO *info,
ULONG level, void **ret, int want_dir )
{
ULONG size;
void *root;
IMAGE_RESOURCE_DIRECTORY *resdirptr;
root = RtlImageDirectoryEntryToData( BaseAddress, TRUE, IMAGE_DIRECTORY_ENTRY_RESOURCE, &size );
if (!root) return STATUS_RESOURCE_DATA_NOT_FOUND;
if (size < sizeof(*resdirptr)) return STATUS_RESOURCE_DATA_NOT_FOUND;
resdirptr = root;
if (!level--) goto done;
if (!(*ret = find_entry_by_name( resdirptr, (LPCWSTR)info->Type, root, want_dir || level )))
return STATUS_RESOURCE_TYPE_NOT_FOUND;
if (!level--) return STATUS_SUCCESS;
resdirptr = *ret;
if (!(*ret = find_entry_by_name( resdirptr, (LPCWSTR)info->Name, root, want_dir || level )))
return STATUS_RESOURCE_NAME_NOT_FOUND;
if (!level--) return STATUS_SUCCESS;
if (level) return STATUS_INVALID_PARAMETER; /* level > 3 */
resdirptr = *ret;
if ((*ret = find_first_entry( resdirptr, root, want_dir ))) return STATUS_SUCCESS;
return STATUS_RESOURCE_DATA_NOT_FOUND;
done:
*ret = resdirptr;
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
RtlpSafeCopyMemory(
_Out_writes_bytes_all_(Length) VOID UNALIGNED *Destination,
_In_reads_bytes_(Length) CONST VOID UNALIGNED *Source,
_In_ SIZE_T Length)
{
_SEH2_TRY
{
RtlCopyMemory(Destination, Source, Length);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
return STATUS_SUCCESS;
}
BOOLEAN
NTAPI
RtlCallVectoredExceptionHandlers(_In_ PEXCEPTION_RECORD ExceptionRecord,
_In_ PCONTEXT Context)
{
/* In the kernel we don't have vectored exception handlers */
return FALSE;
}
VOID
NTAPI
RtlCallVectoredContinueHandlers(_In_ PEXCEPTION_RECORD ExceptionRecord,
_In_ PCONTEXT Context)
{
/* No vectored continue handlers either in kernel mode */
return;
}
/* EOF */