You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1843 lines
50 KiB
1843 lines
50 KiB
/*++
|
|
|
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
vspace.c
|
|
|
|
Abstract:
|
|
|
|
This module implements verification functions for
|
|
virtual address space management interfaces.
|
|
|
|
Author:
|
|
|
|
Silviu Calinoiu (SilviuC) 22-Feb-2001
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
|
|
#include "verifier.h"
|
|
#include "support.h"
|
|
#include "critsect.h"
|
|
#include "faults.h"
|
|
#include "vspace.h"
|
|
#include "public.h"
|
|
#include "logging.h"
|
|
|
|
//
|
|
// Internal functions declarations
|
|
//
|
|
|
|
VOID
|
|
AVrfpFreeVirtualMemNotify (
|
|
HANDLE ProcessHandle,
|
|
VERIFIER_DLL_FREEMEM_TYPE FreeMemType,
|
|
PVOID BaseAddress,
|
|
PSIZE_T RegionSize,
|
|
ULONG VirtualFreeType
|
|
);
|
|
|
|
NTSTATUS
|
|
AVrfpGetVadInformation (
|
|
PVOID Address,
|
|
PVOID * VadAddress,
|
|
PSIZE_T VadSize
|
|
);
|
|
|
|
NTSTATUS
|
|
AVrfpIsCurrentProcessHandle (
|
|
HANDLE ProcessHandle,
|
|
PLOGICAL IsCurrent
|
|
);
|
|
|
|
NTSTATUS
|
|
AVrfpVsTrackAddRegion (
|
|
ULONG_PTR Address,
|
|
ULONG_PTR Size
|
|
);
|
|
|
|
NTSTATUS
|
|
AVrfpVsTrackDeleteRegion (
|
|
ULONG_PTR Address
|
|
);
|
|
|
|
VOID
|
|
AVrfpVsTrackerLock (
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
AVrfpVsTrackerUnlock (
|
|
VOID
|
|
);
|
|
|
|
|
|
//NTSYSCALLAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
AVrfpNtAllocateVirtualMemory(
|
|
IN HANDLE ProcessHandle,
|
|
IN OUT PVOID *BaseAddress,
|
|
IN ULONG_PTR ZeroBits,
|
|
IN OUT PSIZE_T RegionSize,
|
|
IN ULONG AllocationType,
|
|
IN ULONG Protect
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
LOGICAL ShouldTrack = FALSE;
|
|
|
|
BUMP_COUNTER (CNT_VIRTUAL_ALLOC_CALLS);
|
|
|
|
if (SHOULD_FAULT_INJECT(CLS_VIRTUAL_ALLOC_APIS)) {
|
|
BUMP_COUNTER (CNT_VIRTUAL_ALLOC_FAILS);
|
|
CHECK_BREAK (BRK_VIRTUAL_ALLOC_FAIL);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
if ((AVrfpProvider.VerifierFlags & RTL_VRF_FLG_VIRTUAL_MEM_CHECKS) != 0) {
|
|
|
|
if (BaseAddress == NULL || RegionSize == NULL) {
|
|
|
|
VERIFIER_STOP (APPLICATION_VERIFIER_INVALID_ALLOCMEM | APPLICATION_VERIFIER_CONTINUABLE_BREAK,
|
|
"Incorrect virtual alloc call",
|
|
BaseAddress, "Pointer to allocation base address",
|
|
RegionSize, "Pointer to memory region size",
|
|
NULL, "",
|
|
NULL, "" );
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Allocate top-down for 64 bit systems or 3Gb systems.
|
|
//
|
|
|
|
if (*BaseAddress == NULL && AVrfpSysBasicInfo.MaximumUserModeAddress > (ULONG_PTR)0x80000000) {
|
|
|
|
AllocationType |= MEM_TOP_DOWN;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Figure out if this is an allocation that should go into the
|
|
// virtual space tracker.
|
|
//
|
|
|
|
if ((AVrfpProvider.VerifierFlags & RTL_VRF_FLG_VIRTUAL_SPACE_TRACKING) != 0) {
|
|
|
|
if ((AllocationType & (MEM_PHYSICAL | MEM_RESET)) == 0) {
|
|
|
|
if ((AllocationType & (MEM_RESERVE | MEM_COMMIT)) != 0) {
|
|
|
|
Status = AVrfpIsCurrentProcessHandle (ProcessHandle, &ShouldTrack);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
|
|
return Status;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Call the real function.
|
|
//
|
|
|
|
Status = NtAllocateVirtualMemory (ProcessHandle,
|
|
BaseAddress,
|
|
ZeroBits,
|
|
RegionSize,
|
|
AllocationType,
|
|
Protect);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
AVrfLogInTracker (AVrfVspaceTracker,
|
|
TRACK_VIRTUAL_ALLOCATE,
|
|
*BaseAddress,
|
|
(PVOID)*RegionSize,
|
|
(PVOID)(ULONG_PTR)AllocationType,
|
|
(PVOID)(ULONG_PTR)Protect,
|
|
_ReturnAddress());
|
|
|
|
//
|
|
// ShouldTrack is TRUE only if RTL_VRF_FLG_VIRTUAL_SPACE_TRACKING bit is
|
|
// set therefore there is no need to test this again.
|
|
//
|
|
|
|
if (ShouldTrack) {
|
|
|
|
PVOID VadAddress;
|
|
SIZE_T VadSize;
|
|
|
|
Status = AVrfpGetVadInformation (*BaseAddress,
|
|
&VadAddress,
|
|
&VadSize);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
AVrfpVsTrackerLock ();
|
|
AVrfpVsTrackAddRegion ((ULONG_PTR)VadAddress, VadSize);
|
|
AVrfpVsTrackerUnlock ();
|
|
}
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//NTSYSCALLAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
AVrfpNtFreeVirtualMemory(
|
|
IN HANDLE ProcessHandle,
|
|
IN OUT PVOID *BaseAddress,
|
|
IN OUT PSIZE_T RegionSize,
|
|
IN ULONG FreeType
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Protect ourselves against invalid calls to NtFreeVirtualMemory
|
|
// with a NULL RegionSize or BaseAddress pointers. Note that this will
|
|
// never happen if the caller is using the Win32 VirtualFree.
|
|
//
|
|
|
|
if (RegionSize == NULL || BaseAddress == NULL) {
|
|
|
|
if ((AVrfpProvider.VerifierFlags & RTL_VRF_FLG_VIRTUAL_MEM_CHECKS) != 0) {
|
|
|
|
VERIFIER_STOP (APPLICATION_VERIFIER_INVALID_FREEMEM | APPLICATION_VERIFIER_CONTINUABLE_BREAK,
|
|
"Freeing virtual memory block with invalid size or start address",
|
|
BaseAddress, "Pointer to allocation base address",
|
|
RegionSize, "Pointer to memory region size",
|
|
NULL, "",
|
|
NULL, "" );
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// One of MEM_DECOMMIT or MEM_RELEASE must be specified, but not both.
|
|
//
|
|
|
|
if (FreeType != MEM_DECOMMIT && FreeType != MEM_RELEASE) {
|
|
|
|
if ((AVrfpProvider.VerifierFlags & RTL_VRF_FLG_VIRTUAL_MEM_CHECKS) != 0) {
|
|
|
|
VERIFIER_STOP (APPLICATION_VERIFIER_INVALID_FREEMEM | APPLICATION_VERIFIER_CONTINUABLE_BREAK,
|
|
"Incorrect FreeType parameter for VirtualFree operation",
|
|
FreeType, "Incorrect value used by the application",
|
|
MEM_DECOMMIT, "Expected correct value 1",
|
|
MEM_RELEASE, "Expected correct value 2",
|
|
NULL, "" );
|
|
}
|
|
}
|
|
else {
|
|
|
|
AVrfpFreeVirtualMemNotify (ProcessHandle,
|
|
VerifierFreeMemTypeVirtualFree,
|
|
*BaseAddress,
|
|
RegionSize,
|
|
FreeType);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Call the real function.
|
|
//
|
|
|
|
Status = NtFreeVirtualMemory (ProcessHandle,
|
|
BaseAddress,
|
|
RegionSize,
|
|
FreeType);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
AVrfLogInTracker (AVrfVspaceTracker,
|
|
TRACK_VIRTUAL_FREE,
|
|
*BaseAddress,
|
|
(PVOID)*RegionSize,
|
|
(PVOID)(ULONG_PTR)FreeType,
|
|
NULL,
|
|
_ReturnAddress());
|
|
|
|
//
|
|
// If VS tracking is on check if this a free operation that should
|
|
// be tracked.
|
|
//
|
|
|
|
if ((AVrfpProvider.VerifierFlags & RTL_VRF_FLG_VIRTUAL_SPACE_TRACKING) != 0) {
|
|
|
|
//
|
|
// If this is a VS release in the current process we will try to track
|
|
// it. If we got an error while figuring out if this is a handle for
|
|
// the current process we just skip this free. The VS tracker is
|
|
// resilient to alloc/free misses.
|
|
//
|
|
|
|
if ((FreeType & MEM_RELEASE) != 0) {
|
|
|
|
LOGICAL SameProcess;
|
|
NTSTATUS IsCurrentProcessStatus;
|
|
|
|
IsCurrentProcessStatus = AVrfpIsCurrentProcessHandle (ProcessHandle,
|
|
&SameProcess);
|
|
|
|
if (NT_SUCCESS(IsCurrentProcessStatus)) {
|
|
|
|
if (SameProcess) {
|
|
|
|
AVrfpVsTrackerLock ();
|
|
AVrfpVsTrackDeleteRegion ((ULONG_PTR)(*BaseAddress));
|
|
AVrfpVsTrackerUnlock ();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//NTSYSCALLAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
AVrfpNtMapViewOfSection(
|
|
IN HANDLE SectionHandle,
|
|
IN HANDLE ProcessHandle,
|
|
IN OUT PVOID *BaseAddress,
|
|
IN ULONG_PTR ZeroBits,
|
|
IN SIZE_T CommitSize,
|
|
IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
|
|
IN OUT PSIZE_T ViewSize,
|
|
IN SECTION_INHERIT InheritDisposition,
|
|
IN ULONG AllocationType,
|
|
IN ULONG Protect
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
BUMP_COUNTER (CNT_MAP_VIEW_CALLS);
|
|
|
|
if (SHOULD_FAULT_INJECT(CLS_MAP_VIEW_APIS)) {
|
|
BUMP_COUNTER (CNT_MAP_VIEW_FAILS);
|
|
CHECK_BREAK (BRK_MAP_VIEW_FAIL);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
if ((AVrfpProvider.VerifierFlags & RTL_VRF_FLG_VIRTUAL_MEM_CHECKS) != 0) {
|
|
|
|
if (BaseAddress == NULL || ViewSize == NULL) {
|
|
|
|
VERIFIER_STOP (APPLICATION_VERIFIER_INVALID_MAPVIEW | APPLICATION_VERIFIER_CONTINUABLE_BREAK,
|
|
"Incorrect map view call",
|
|
BaseAddress, "Pointer to mapping base address",
|
|
ViewSize, "Pointer to view size",
|
|
NULL, "",
|
|
NULL, "" );
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Allocate top-down for 64 bit systems or 3Gb systems.
|
|
//
|
|
|
|
if (*BaseAddress == NULL && AVrfpSysBasicInfo.MaximumUserModeAddress > (ULONG_PTR)0x80000000) {
|
|
|
|
AllocationType |= MEM_TOP_DOWN;
|
|
}
|
|
}
|
|
}
|
|
|
|
Status = NtMapViewOfSection (SectionHandle,
|
|
ProcessHandle,
|
|
BaseAddress,
|
|
ZeroBits,
|
|
CommitSize,
|
|
SectionOffset,
|
|
ViewSize,
|
|
InheritDisposition,
|
|
AllocationType,
|
|
Protect);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
AVrfLogInTracker (AVrfVspaceTracker,
|
|
TRACK_MAP_VIEW_OF_SECTION,
|
|
*BaseAddress,
|
|
(PVOID)*ViewSize,
|
|
(PVOID)(ULONG_PTR)AllocationType,
|
|
(PVOID)(ULONG_PTR)Protect,
|
|
_ReturnAddress());
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//NTSYSCALLAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
AVrfpNtUnmapViewOfSection(
|
|
IN HANDLE ProcessHandle,
|
|
IN PVOID BaseAddress
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
AVrfpFreeVirtualMemNotify (ProcessHandle,
|
|
VerifierFreeMemTypeUnmap,
|
|
BaseAddress,
|
|
NULL,
|
|
0);
|
|
|
|
//
|
|
// Unmap the memory.
|
|
//
|
|
|
|
Status = NtUnmapViewOfSection (ProcessHandle,
|
|
BaseAddress);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
AVrfLogInTracker (AVrfVspaceTracker,
|
|
TRACK_UNMAP_VIEW_OF_SECTION,
|
|
BaseAddress,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
_ReturnAddress());
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//NTSYSCALLAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
AVrfpNtCreateSection (
|
|
OUT PHANDLE SectionHandle,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
|
|
IN PLARGE_INTEGER MaximumSize OPTIONAL,
|
|
IN ULONG SectionPageProtection,
|
|
IN ULONG AllocationAttributes,
|
|
IN HANDLE FileHandle OPTIONAL
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
Status = NtCreateSection (SectionHandle,
|
|
DesiredAccess,
|
|
ObjectAttributes,
|
|
MaximumSize,
|
|
SectionPageProtection,
|
|
AllocationAttributes,
|
|
FileHandle);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//NTSYSCALLAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
AVrfpNtOpenSection(
|
|
OUT PHANDLE SectionHandle,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN POBJECT_ATTRIBUTES ObjectAttributes
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
Status = NtOpenSection (SectionHandle,
|
|
DesiredAccess,
|
|
ObjectAttributes);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
VOID
|
|
AVrfpFreeVirtualMemNotify (
|
|
HANDLE ProcessHandle,
|
|
VERIFIER_DLL_FREEMEM_TYPE FreeMemType,
|
|
PVOID BaseAddress,
|
|
PSIZE_T RegionSize,
|
|
ULONG VirtualFreeType
|
|
)
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine is called when a portion of virtual space gets freed (free
|
|
or unmap). It will make some sanity checks of the free operation and then
|
|
it will call the common `memory free' notification routine (the one
|
|
called for any free: dll unload, heap free, etc.).
|
|
|
|
Parameters:
|
|
|
|
ProcessHandle: process handle.
|
|
|
|
FreeMemType: type of free. The function is called only with
|
|
VerifierFreeMemTypeVirtualFree or VerifierFreeMemTypeVirtualUnmap.
|
|
|
|
BaseAddress: start address.
|
|
|
|
RegionSize: region size.
|
|
|
|
VirtualFreeType: type of free operation requested by VirtualFree or UnmapView.
|
|
|
|
Return value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PVOID FreedBaseAddress;
|
|
SIZE_T FreedSize;
|
|
SIZE_T InfoLength;
|
|
MEMORY_BASIC_INFORMATION MemoryInformation;
|
|
LOGICAL IsCurrentProcessHandle;
|
|
|
|
//
|
|
// Query the size of the allocation and verify that the memory
|
|
// is not free already.
|
|
//
|
|
|
|
FreedBaseAddress = PAGE_ALIGN( BaseAddress );
|
|
|
|
Status = NtQueryVirtualMemory (ProcessHandle,
|
|
FreedBaseAddress,
|
|
MemoryBasicInformation,
|
|
&MemoryInformation,
|
|
sizeof (MemoryInformation),
|
|
&InfoLength);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
|
|
if (AVrfpProvider.VerifierDebug != 0) {
|
|
|
|
DbgPrint ("AVrfpFreeVirtualMemNotify: NtQueryVirtualMemory( %p ) failed %x\n",
|
|
FreedBaseAddress,
|
|
Status);
|
|
}
|
|
}
|
|
else {
|
|
|
|
if (MemoryInformation.State == MEM_FREE) {
|
|
|
|
//
|
|
// We are trying to free memory that is already freed.
|
|
// This can indicate a nasty bug in the app because the current thread
|
|
// is probably using a stale pointer and this memory could have been
|
|
// reused for something else...
|
|
//
|
|
|
|
if ((AVrfpProvider.VerifierFlags & RTL_VRF_FLG_VIRTUAL_MEM_CHECKS) != 0) {
|
|
|
|
VERIFIER_STOP (APPLICATION_VERIFIER_INVALID_FREEMEM | APPLICATION_VERIFIER_CONTINUABLE_BREAK,
|
|
"Trying to free virtual memory block that is already free",
|
|
BaseAddress, "Memory block address",
|
|
NULL, "",
|
|
NULL, "",
|
|
NULL, "" );
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Find out if we are freeing memory in the current process
|
|
// or in another process. For the cross-process case we are not
|
|
// trying to catch any other possible bugs because we can get confused
|
|
// if the current process is wow64 and the target is ia64, etc.
|
|
//
|
|
|
|
Status = AVrfpIsCurrentProcessHandle (ProcessHandle,
|
|
&IsCurrentProcessHandle);
|
|
|
|
if (NT_SUCCESS(Status) && IsCurrentProcessHandle) {
|
|
|
|
//
|
|
// For VirtualFree (MEM_RELEASE, RegionSize == 0) or UnmapViewOfFile
|
|
// the whole VAD will be freed so we will use its size.
|
|
//
|
|
|
|
if ((FreeMemType == VerifierFreeMemTypeUnmap) ||
|
|
((FreeMemType == VerifierFreeMemTypeVirtualFree) &&
|
|
(((VirtualFreeType & MEM_RELEASE) != 0) && *RegionSize == 0))) {
|
|
|
|
FreedSize = MemoryInformation.RegionSize;
|
|
}
|
|
else {
|
|
|
|
ASSERT (RegionSize != NULL);
|
|
FreedSize = *RegionSize;
|
|
}
|
|
|
|
//
|
|
// Sanity checks for the block start address and size.
|
|
// These checks can be integrated in AVrfpFreeMemNotify
|
|
// but we want to make sure we are not using values that
|
|
// don't make sense in the FreedSize computation below.
|
|
//
|
|
|
|
if ((AVrfpSysBasicInfo.MaximumUserModeAddress <= (ULONG_PTR)FreedBaseAddress) ||
|
|
((AVrfpSysBasicInfo.MaximumUserModeAddress - (ULONG_PTR)FreedBaseAddress) < FreedSize)) {
|
|
|
|
if ((AVrfpProvider.VerifierFlags & RTL_VRF_FLG_VIRTUAL_MEM_CHECKS) != 0) {
|
|
|
|
VERIFIER_STOP (APPLICATION_VERIFIER_INVALID_FREEMEM | APPLICATION_VERIFIER_CONTINUABLE_BREAK,
|
|
"Freeing virtual memory block with invalid size or start address",
|
|
FreedBaseAddress, "Memory block address",
|
|
FreedSize, "Memory region size",
|
|
NULL, "",
|
|
NULL, "" );
|
|
}
|
|
}
|
|
else {
|
|
|
|
FreedSize = (PCHAR)BaseAddress + FreedSize - (PCHAR)FreedBaseAddress;
|
|
FreedSize = ROUND_UP( FreedSize, PAGE_SIZE );
|
|
|
|
//
|
|
// Perform the rest of the checks, common for all memory free operations.
|
|
// E.g.:
|
|
// - is this memory block part of the current thread's stack?
|
|
// - do we have active critical sections inside this memory block?
|
|
//
|
|
|
|
AVrfpFreeMemNotify (FreeMemType,
|
|
FreedBaseAddress,
|
|
FreedSize,
|
|
NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AVrfpIsCurrentProcessHandle (
|
|
HANDLE ProcessHandle,
|
|
PLOGICAL IsCurrent
|
|
)
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine figures out if a handle represents the current process'
|
|
handle. This means it is either a pseudohandle or a handle obtained
|
|
by calling OpenProcess().
|
|
|
|
Parameters:
|
|
|
|
ProcessHandle: handle to figure out.
|
|
|
|
IsCurrent: address of a boolean to pass back the result.
|
|
|
|
Return value:
|
|
|
|
STATUS_SUCCESS if the function managed to put a meaningful value in
|
|
`*IsCurrent'. Various status errors otherwise.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PROCESS_BASIC_INFORMATION BasicInfo;
|
|
|
|
if (NtCurrentProcess() == ProcessHandle) {
|
|
*IsCurrent = TRUE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
Status = NtQueryInformationProcess (ProcessHandle,
|
|
ProcessBasicInformation,
|
|
&BasicInfo,
|
|
sizeof(BasicInfo),
|
|
NULL);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if (BasicInfo.UniqueProcessId == (ULONG_PTR)(NtCurrentTeb()->ClientId.UniqueProcess)) {
|
|
|
|
*IsCurrent = TRUE;
|
|
}
|
|
else {
|
|
|
|
*IsCurrent = FALSE;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////// Virtual space trackker support
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// Unexpected frees/allocs happen when one operation happens in a tracked
|
|
// dll and the pair operation happens in ntdll.dll, kernel mode or
|
|
// cross-process. For example the allocation is made by a kernel mode
|
|
// component and the free is done in some dll. The virtual space tracker
|
|
// must be resilient to these situations in order to work for any type of
|
|
// process.
|
|
//
|
|
|
|
RTL_CRITICAL_SECTION AVrfpVsTrackLock;
|
|
LIST_ENTRY AVrfpVsTrackList;
|
|
|
|
LONG AVrfpVsTrackRegionCount;
|
|
SIZE_T AVrfpVsTrackMemoryTotal;
|
|
|
|
LOGICAL AVrfpVsTrackDisabled;
|
|
|
|
|
|
VOID
|
|
AVrfpVsTrackerLock (
|
|
VOID
|
|
)
|
|
{
|
|
RtlEnterCriticalSection (&AVrfpVsTrackLock);
|
|
}
|
|
|
|
VOID
|
|
AVrfpVsTrackerUnlock (
|
|
VOID
|
|
)
|
|
{
|
|
RtlLeaveCriticalSection (&AVrfpVsTrackLock);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AVrfpVsTrackInitialize (
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine initializes virtual space tracker structures.
|
|
|
|
Parameters:
|
|
|
|
None.
|
|
|
|
Return value:
|
|
|
|
STATUS_SUCCESS if successful. Various status errors otherwise.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
InitializeListHead (&AVrfpVsTrackList);
|
|
|
|
Status = RtlInitializeCriticalSection (&AVrfpVsTrackLock);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AVrfpVsTrackAddRegion (
|
|
ULONG_PTR Address,
|
|
ULONG_PTR Size
|
|
)
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine adds a new virtual space region to the VS tracker. If there is
|
|
already a region having exactly the same characteristics (address, size)
|
|
the function does not do anything and returns successfully.
|
|
|
|
The function is called with the VS track lock acquired.
|
|
|
|
Parameters:
|
|
|
|
Address: start address of the new virtual space region.
|
|
|
|
Size: size of the new virtual space region.
|
|
|
|
Return value:
|
|
|
|
STATUS_SUCCESS if successful.
|
|
|
|
--*/
|
|
{
|
|
PAVRF_VSPACE_REGION Region;
|
|
PLIST_ENTRY Current;
|
|
PAVRF_VSPACE_REGION NewRegion;
|
|
LOGICAL ClashFound;
|
|
|
|
if (AVrfpVsTrackDisabled) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Due to the fact that only virtual space operations coming from
|
|
// DLLs (except ntdll.dll) are hooked this routine must be resilient
|
|
// to various failures. For example if we try to add a region that
|
|
// clashes with an existing region within the tracker it probably means
|
|
// the free for the existing region has been missed.
|
|
//
|
|
|
|
ClashFound = FALSE;
|
|
|
|
Current = AVrfpVsTrackList.Flink;
|
|
|
|
NewRegion = NULL;
|
|
|
|
while (Current != &AVrfpVsTrackList) {
|
|
|
|
Region = CONTAINING_RECORD (Current,
|
|
AVRF_VSPACE_REGION,
|
|
List);
|
|
|
|
if (Address < Region->Address + Region->Size) {
|
|
|
|
if (Address + Size > Region->Address) {
|
|
|
|
//
|
|
// We will recycle `Region' since we missed its free.
|
|
//
|
|
|
|
AVrfpVsTrackRegionCount -= 1;
|
|
AVrfpVsTrackMemoryTotal -= Region->Size;
|
|
|
|
ClashFound = TRUE;
|
|
NewRegion = Region;
|
|
|
|
break;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// We will add a new region in front of `Region'.
|
|
//
|
|
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Move on to the next region in the list.
|
|
//
|
|
|
|
Current = Current->Flink;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We need to create a new region before `Region' if
|
|
// NewRegion is null.
|
|
//
|
|
|
|
if (NewRegion == NULL) {
|
|
|
|
NewRegion = AVrfpAllocate (sizeof *NewRegion);
|
|
}
|
|
|
|
if (NewRegion == NULL) {
|
|
|
|
//
|
|
// Well, we could not allocate a new VS tracker node. Since add/delete
|
|
// are resilient to these misses we will let it go.
|
|
//
|
|
|
|
return STATUS_NO_MEMORY;
|
|
|
|
}
|
|
|
|
//
|
|
// We fill the new virtual space region with information
|
|
// and then return.
|
|
//
|
|
|
|
NewRegion->Address = Address;
|
|
NewRegion->Size = Size;
|
|
|
|
RtlCaptureStackBackTrace (3,
|
|
MAX_TRACE_DEPTH,
|
|
NewRegion->Trace,
|
|
NULL);
|
|
|
|
AVrfpVsTrackRegionCount += 1;
|
|
AVrfpVsTrackMemoryTotal += NewRegion->Size;
|
|
|
|
if ((AVrfpProvider.VerifierDebug & VRFP_DEBUG_SHOW_VSPACE_TRACKING)) {
|
|
DbgPrint ("AVRF: adding virtual space region @ %p (size %p) \n", Address, Size);
|
|
}
|
|
|
|
if (Current != &AVrfpVsTrackList) {
|
|
|
|
//
|
|
// We will add the new region before the current region in the list
|
|
// traversal if this is a new region to be inserted and we do not
|
|
// just recycle a clashing region.
|
|
//
|
|
// (Current->Blink)
|
|
// <== (NewRegion->List)
|
|
// Current
|
|
//
|
|
|
|
if (ClashFound == FALSE) {
|
|
|
|
NewRegion->List.Flink = Current;
|
|
NewRegion->List.Blink = Current->Blink;
|
|
|
|
Current->Blink->Flink = &(NewRegion->List);
|
|
Current->Blink = &(NewRegion->List);
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// If we finished the list of regions then this must be the
|
|
// last region.
|
|
//
|
|
|
|
InsertTailList (Current, &(NewRegion->List));
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AVrfpVsTrackDeleteRegion (
|
|
ULONG_PTR Address
|
|
)
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine deletes a virtual space region from the tracker assuming
|
|
there is a region starting exactly as the same address as parameter
|
|
`Address'. If there is not an exact match an error is returned.
|
|
|
|
The function is called with the VS track lock acquired.
|
|
|
|
Parameters:
|
|
|
|
Address: start address of the region that must be deleted from the tracker.
|
|
|
|
Return value:
|
|
|
|
STATUS_SUCCES if the virtual region has been successfully deleted.
|
|
|
|
--*/
|
|
{
|
|
PAVRF_VSPACE_REGION Region;
|
|
PLIST_ENTRY Current;
|
|
|
|
if (AVrfpVsTrackDisabled) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
Current = AVrfpVsTrackList.Flink;
|
|
|
|
while (Current != &AVrfpVsTrackList) {
|
|
|
|
Region = CONTAINING_RECORD (Current,
|
|
AVRF_VSPACE_REGION,
|
|
List);
|
|
|
|
if (Address >= Region->Address + Region->Size) {
|
|
|
|
//
|
|
// Move on to the next region in the list.
|
|
//
|
|
|
|
Current = Current->Flink;
|
|
}
|
|
else if (Address >= Region->Address) {
|
|
|
|
//
|
|
// Any region clashing with this one will be deleted.
|
|
//
|
|
|
|
if ((AVrfpProvider.VerifierDebug & VRFP_DEBUG_SHOW_VSPACE_TRACKING)) {
|
|
|
|
DbgPrint ("AVRF: deleting virtual space region @ %p (size %p) \n",
|
|
Region->Address, Region->Size);
|
|
}
|
|
|
|
AVrfpVsTrackRegionCount -= 1;
|
|
AVrfpVsTrackMemoryTotal -= Region->Size;
|
|
|
|
RemoveEntryList (&(Region->List));
|
|
|
|
AVrfpFree (Region);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Virtual space tracker is sorted so there is no chance to find
|
|
// a region containing the address any more.
|
|
//
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AVrfpGetVadInformation (
|
|
PVOID Address,
|
|
PVOID * VadAddress,
|
|
PSIZE_T VadSize
|
|
)
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine takes an arbitrary address in a virtual space region
|
|
containing private memory and finds out the start address
|
|
and size of the VAD containing it.
|
|
|
|
If the address points into some other type of memory (free, mapped, etc.)
|
|
the function will return an error.
|
|
|
|
The tricky part in the implementation is that a private VAD can have various
|
|
portions committed or decommitted so a simple VirtualQuery() will not give
|
|
all the information.
|
|
|
|
Parameters:
|
|
|
|
Address: arbitrary address.
|
|
|
|
VadAddress: pointer to variable where start address of the VAD region
|
|
will be written.
|
|
|
|
VadSize: pointer to variable where region size of the VAD will be
|
|
written.
|
|
|
|
Return value:
|
|
|
|
STATUS_SUCCESS if the VAD contained private memory and the start address
|
|
and size have been written.
|
|
|
|
--*/
|
|
{
|
|
MEMORY_BASIC_INFORMATION MemoryInfo;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Query the size of the allocation.
|
|
//
|
|
|
|
Status = NtQueryVirtualMemory (NtCurrentProcess (),
|
|
Address,
|
|
MemoryBasicInformation,
|
|
&MemoryInfo,
|
|
sizeof MemoryInfo,
|
|
NULL);
|
|
|
|
if (! NT_SUCCESS (Status) ) {
|
|
|
|
//
|
|
// For this case only we disable the VS tracker for good.
|
|
// We do this so that the process can continue to run even if
|
|
// the tracking infrastructure cannot be used any more.
|
|
//
|
|
|
|
AVrfpVsTrackDisabled = TRUE;
|
|
|
|
return Status;
|
|
}
|
|
|
|
if (MemoryInfo.Type != MEM_PRIVATE) {
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
*VadAddress = MemoryInfo.AllocationBase;
|
|
*VadSize = MemoryInfo.RegionSize;
|
|
Address = *VadAddress;
|
|
|
|
do {
|
|
|
|
Address = (PVOID)((ULONG_PTR)Address + MemoryInfo.RegionSize);
|
|
|
|
Status = NtQueryVirtualMemory (NtCurrentProcess (),
|
|
Address,
|
|
MemoryBasicInformation,
|
|
&MemoryInfo,
|
|
sizeof MemoryInfo,
|
|
NULL);
|
|
|
|
if (! NT_SUCCESS (Status) ) {
|
|
|
|
//
|
|
// For this case only we disable the VS tracker for good.
|
|
// We do this so that the process can continue to run even if
|
|
// the tracking infrastructure cannot be used any more.
|
|
//
|
|
|
|
AVrfpVsTrackDisabled = TRUE;
|
|
|
|
return Status;
|
|
}
|
|
|
|
if (*VadAddress == MemoryInfo.AllocationBase) {
|
|
*VadSize += MemoryInfo.RegionSize;
|
|
}
|
|
|
|
} while (*VadAddress == MemoryInfo.AllocationBase);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AVrfpVsTrackDeleteRegionContainingAddress (
|
|
PVOID Address
|
|
)
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine takes an arbitrary address and tries to delete the virtual
|
|
region containing it from the VS tracker.
|
|
|
|
If the address points into some other type of memory (free, mapped, etc.)
|
|
the function will return an error.
|
|
|
|
Parameters:
|
|
|
|
Address: arbitrary address.
|
|
|
|
Return value:
|
|
|
|
STATUS_SUCCESS if the virtual region has been deleted.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PVOID VadAddress;
|
|
SIZE_T VadSize;
|
|
|
|
if ((AVrfpProvider.VerifierFlags & RTL_VRF_FLG_VIRTUAL_SPACE_TRACKING) == 0) {
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
if (AVrfpVsTrackDisabled) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
Status = AVrfpGetVadInformation (Address,
|
|
&VadAddress,
|
|
&VadSize);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
if ((AVrfpProvider.VerifierDebug & VRFP_DEBUG_SHOW_VSPACE_TRACKING)) {
|
|
DbgPrint ("AVRF: deleting stack @ %p \n", VadAddress);
|
|
}
|
|
|
|
AVrfpVsTrackerLock ();
|
|
AVrfpVsTrackDeleteRegion ((ULONG_PTR)VadAddress);
|
|
AVrfpVsTrackerUnlock ();
|
|
}
|
|
else {
|
|
|
|
if ((AVrfpProvider.VerifierDebug & VRFP_DEBUG_SHOW_VSPACE_TRACKING)) {
|
|
DbgPrint ("AVRF: failed to find a stack @ %p (%X)\n", VadAddress, Status);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////// IsBadPtr checks
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
ULONG
|
|
AVrfpProbeMemExceptionFilter (
|
|
IN ULONG ExceptionCode,
|
|
IN PVOID ExceptionRecord,
|
|
IN CONST VOID *Address
|
|
)
|
|
{
|
|
VERIFIER_STOP (APPLICATION_VERIFIER_UNEXPECTED_EXCEPTION | APPLICATION_VERIFIER_CONTINUABLE_BREAK,
|
|
"unexpected exception raised while probing memory",
|
|
ExceptionCode, "Exception code.",
|
|
((PEXCEPTION_POINTERS)ExceptionRecord)->ExceptionRecord, "Exception record. Use .exr to display it.",
|
|
((PEXCEPTION_POINTERS)ExceptionRecord)->ContextRecord, "Context record. Use .cxr to display it.",
|
|
Address, "Memory address");
|
|
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|
|
|
|
BOOL
|
|
AVrfpVerifyReadAccess (
|
|
IN CONST VOID *UserStartAddress,
|
|
IN UINT_PTR UserSize,
|
|
IN CONST VOID *RegionStartAddress,
|
|
OUT PUINT_PTR RegionSize
|
|
)
|
|
{
|
|
PVOID BaseAddress;
|
|
BOOL Success;
|
|
NTSTATUS Status;
|
|
SIZE_T InfoLength;
|
|
MEMORY_BASIC_INFORMATION MemoryInformation;
|
|
|
|
//
|
|
// Assume success and block size == page size.
|
|
// We will simply return these values in case NtQueryVirtualMemory fails
|
|
// because that could happen in low memory conditions, etc.
|
|
//
|
|
|
|
Success = TRUE;
|
|
*RegionSize = AVrfpSysBasicInfo.PageSize;
|
|
|
|
BaseAddress = PAGE_ALIGN (RegionStartAddress);
|
|
|
|
//
|
|
// Sanity check for the based address.
|
|
//
|
|
|
|
if (AVrfpSysBasicInfo.MaximumUserModeAddress <= (ULONG_PTR)BaseAddress) {
|
|
|
|
VERIFIER_STOP (APPLICATION_VERIFIER_PROBE_INVALID_ADDRESS | APPLICATION_VERIFIER_CONTINUABLE_BREAK,
|
|
"Probing invalid address",
|
|
UserStartAddress, "Start address",
|
|
UserSize, "Memory block size",
|
|
BaseAddress, "Invalid address",
|
|
NULL, "" );
|
|
|
|
Success = FALSE;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Query the size of the allocation and verify that the memory
|
|
// is not free already.
|
|
//
|
|
|
|
Status = NtQueryVirtualMemory (NtCurrentProcess (),
|
|
BaseAddress,
|
|
MemoryBasicInformation,
|
|
&MemoryInformation,
|
|
sizeof (MemoryInformation),
|
|
&InfoLength);
|
|
|
|
if (NT_SUCCESS (Status)) {
|
|
|
|
if (MemoryInformation.State & MEM_FREE) {
|
|
|
|
//
|
|
// Probing free memory!
|
|
//
|
|
|
|
VERIFIER_STOP (APPLICATION_VERIFIER_PROBE_FREE_MEM | APPLICATION_VERIFIER_CONTINUABLE_BREAK,
|
|
"Probing free memory",
|
|
UserStartAddress, "Start address",
|
|
UserSize, "Memory block size",
|
|
BaseAddress, "Address of free memory page",
|
|
NULL, "" );
|
|
|
|
Success = FALSE;
|
|
}
|
|
else if (MemoryInformation.AllocationProtect & PAGE_GUARD) {
|
|
|
|
//
|
|
// Probing a guard page, probably part of a stack!
|
|
//
|
|
|
|
VERIFIER_STOP (APPLICATION_VERIFIER_PROBE_GUARD_PAGE | APPLICATION_VERIFIER_CONTINUABLE_BREAK,
|
|
"Probing guard page",
|
|
UserStartAddress, "Start address",
|
|
UserSize, "Memory block size",
|
|
BaseAddress, "Address of guard page",
|
|
NULL, "" );
|
|
|
|
Success = FALSE;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Everything seems to be OK. Return to the caller the number of bytes
|
|
// to skip up to the next memory region.
|
|
//
|
|
|
|
ASSERT ((MemoryInformation.RegionSize % AVrfpSysBasicInfo.PageSize) == 0);
|
|
*RegionSize = MemoryInformation.RegionSize;
|
|
}
|
|
}
|
|
}
|
|
|
|
return Success;
|
|
}
|
|
|
|
BOOL
|
|
AVrfpProbeMemoryBlockChecks (
|
|
IN CONST VOID *UserBaseAddress,
|
|
IN UINT_PTR UserSize
|
|
)
|
|
{
|
|
PBYTE EndAddress;
|
|
PBYTE StartAddress;
|
|
ULONG PageSize;
|
|
BOOL Success;
|
|
UINT_PTR RegionSize;
|
|
|
|
Success = TRUE;
|
|
|
|
PageSize = AVrfpSysBasicInfo.PageSize;
|
|
|
|
//
|
|
// If the structure has zero length, then there is nothing to probe.
|
|
//
|
|
|
|
if (UserSize != 0) {
|
|
|
|
if (UserBaseAddress == NULL) {
|
|
|
|
VERIFIER_STOP (APPLICATION_VERIFIER_PROBE_NULL | APPLICATION_VERIFIER_CONTINUABLE_BREAK,
|
|
"Probing NULL address",
|
|
NULL, "",
|
|
NULL, "",
|
|
NULL, "",
|
|
NULL, "" );
|
|
|
|
Success = FALSE;
|
|
}
|
|
else {
|
|
|
|
StartAddress = (PBYTE)UserBaseAddress;
|
|
EndAddress = StartAddress + UserSize - 1;
|
|
|
|
if (EndAddress < StartAddress) {
|
|
|
|
VERIFIER_STOP (APPLICATION_VERIFIER_PROBE_INVALID_START_OR_SIZE | APPLICATION_VERIFIER_CONTINUABLE_BREAK,
|
|
"Probing memory block with invalid start address or size",
|
|
StartAddress, "Start address",
|
|
UserSize, "Memory block size",
|
|
NULL, "",
|
|
NULL, "" );
|
|
|
|
Success = FALSE;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Truncate the start and end to page size alignment
|
|
// and verify every page in this memory block.
|
|
//
|
|
|
|
StartAddress = PAGE_ALIGN (StartAddress);
|
|
EndAddress = PAGE_ALIGN (EndAddress);
|
|
|
|
while (StartAddress <= EndAddress) {
|
|
|
|
Success = AVrfpVerifyReadAccess (UserBaseAddress,
|
|
UserSize,
|
|
StartAddress,
|
|
&RegionSize);
|
|
|
|
if (Success != FALSE) {
|
|
|
|
ASSERT ((RegionSize % PageSize) == 0);
|
|
|
|
if (RegionSize <= (UINT_PTR)(EndAddress - StartAddress)) {
|
|
|
|
StartAddress = StartAddress + RegionSize;
|
|
}
|
|
else {
|
|
|
|
StartAddress = StartAddress + PageSize;
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// We have detected a problem already - bail out.
|
|
//
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return Success;
|
|
}
|
|
|
|
//WINBASEAPI
|
|
BOOL
|
|
WINAPI
|
|
AVrfpIsBadReadPtr(
|
|
CONST VOID *lp,
|
|
UINT_PTR cb
|
|
)
|
|
{
|
|
typedef BOOL (WINAPI * FUNCTION_TYPE) (CONST VOID *, UINT_PTR);
|
|
FUNCTION_TYPE Function;
|
|
|
|
if ((AVrfpProvider.VerifierFlags & RTL_VRF_FLG_VIRTUAL_MEM_CHECKS) != 0) {
|
|
|
|
AVrfpProbeMemoryBlockChecks (lp,
|
|
cb);
|
|
}
|
|
|
|
//
|
|
// Call the original funtion.
|
|
//
|
|
|
|
Function = AVRFP_GET_ORIGINAL_EXPORT (AVrfpKernel32Thunks,
|
|
AVRF_INDEX_KERNEL32_ISBADREADPTR);
|
|
|
|
return (*Function) (lp, cb);
|
|
}
|
|
|
|
|
|
//WINBASEAPI
|
|
BOOL
|
|
WINAPI
|
|
AVrfpIsBadHugeReadPtr(
|
|
CONST VOID *lp,
|
|
UINT_PTR cb
|
|
)
|
|
{
|
|
typedef BOOL (WINAPI * FUNCTION_TYPE) (CONST VOID *, UINT_PTR);
|
|
FUNCTION_TYPE Function;
|
|
|
|
if ((AVrfpProvider.VerifierFlags & RTL_VRF_FLG_VIRTUAL_MEM_CHECKS) != 0) {
|
|
|
|
AVrfpProbeMemoryBlockChecks (lp,
|
|
cb);
|
|
}
|
|
|
|
//
|
|
// Call the original funtion.
|
|
//
|
|
|
|
Function = AVRFP_GET_ORIGINAL_EXPORT (AVrfpKernel32Thunks,
|
|
AVRF_INDEX_KERNEL32_ISBADHUGEREADPTR);
|
|
|
|
return (*Function) (lp, cb);
|
|
}
|
|
|
|
//WINBASEAPI
|
|
BOOL
|
|
WINAPI
|
|
AVrfpIsBadWritePtr(
|
|
LPVOID lp,
|
|
UINT_PTR cb
|
|
)
|
|
{
|
|
typedef BOOL (WINAPI * FUNCTION_TYPE) (LPVOID , UINT_PTR);
|
|
FUNCTION_TYPE Function;
|
|
|
|
if ((AVrfpProvider.VerifierFlags & RTL_VRF_FLG_VIRTUAL_MEM_CHECKS) != 0) {
|
|
|
|
AVrfpProbeMemoryBlockChecks (lp,
|
|
cb);
|
|
}
|
|
|
|
//
|
|
// Call the original funtion.
|
|
//
|
|
|
|
Function = AVRFP_GET_ORIGINAL_EXPORT (AVrfpKernel32Thunks,
|
|
AVRF_INDEX_KERNEL32_ISBADWRITEPTR);
|
|
|
|
return (*Function) (lp, cb);
|
|
}
|
|
|
|
//WINBASEAPI
|
|
BOOL
|
|
WINAPI
|
|
AVrfpIsBadHugeWritePtr(
|
|
LPVOID lp,
|
|
UINT_PTR cb
|
|
)
|
|
{
|
|
typedef BOOL (WINAPI * FUNCTION_TYPE) (LPVOID , UINT_PTR);
|
|
FUNCTION_TYPE Function;
|
|
|
|
if ((AVrfpProvider.VerifierFlags & RTL_VRF_FLG_VIRTUAL_MEM_CHECKS) != 0) {
|
|
|
|
AVrfpProbeMemoryBlockChecks (lp,
|
|
cb);
|
|
}
|
|
|
|
//
|
|
// Call the original funtion.
|
|
//
|
|
|
|
Function = AVRFP_GET_ORIGINAL_EXPORT (AVrfpKernel32Thunks,
|
|
AVRF_INDEX_KERNEL32_ISBADHUGEWRITEPTR);
|
|
|
|
return (*Function) (lp, cb);
|
|
}
|
|
|
|
//WINBASEAPI
|
|
BOOL
|
|
WINAPI
|
|
AVrfpIsBadCodePtr(
|
|
FARPROC lpfn
|
|
)
|
|
{
|
|
typedef BOOL (WINAPI * FUNCTION_TYPE) (FARPROC);
|
|
FUNCTION_TYPE Function;
|
|
|
|
if ((AVrfpProvider.VerifierFlags & RTL_VRF_FLG_VIRTUAL_MEM_CHECKS) != 0) {
|
|
|
|
AVrfpProbeMemoryBlockChecks (lpfn,
|
|
1);
|
|
}
|
|
|
|
//
|
|
// Call the original funtion.
|
|
//
|
|
|
|
Function = AVRFP_GET_ORIGINAL_EXPORT (AVrfpKernel32Thunks,
|
|
AVRF_INDEX_KERNEL32_ISBADCODEPTR);
|
|
|
|
return (*Function) (lpfn);
|
|
}
|
|
|
|
|
|
//WINBASEAPI
|
|
BOOL
|
|
WINAPI
|
|
AVrfpIsBadStringPtrA(
|
|
LPCSTR lpsz,
|
|
UINT_PTR cchMax
|
|
)
|
|
{
|
|
typedef BOOL (WINAPI * FUNCTION_TYPE) (LPCSTR , UINT_PTR);
|
|
FUNCTION_TYPE Function;
|
|
ULONG PageSize;
|
|
BOOL FoundNull;
|
|
BOOL Success;
|
|
LPCSTR StartAddress;
|
|
LPCSTR EndAddress;
|
|
CHAR Character;
|
|
|
|
PageSize = AVrfpSysBasicInfo.PageSize;
|
|
FoundNull = FALSE;
|
|
|
|
StartAddress = lpsz;
|
|
EndAddress = lpsz + cchMax - 1;
|
|
|
|
while (StartAddress <= EndAddress && FoundNull == FALSE) {
|
|
|
|
//
|
|
// Verify read access to the current page.
|
|
//
|
|
|
|
Success = AVrfpProbeMemoryBlockChecks (StartAddress,
|
|
sizeof (CHAR));
|
|
|
|
if (Success == FALSE) {
|
|
|
|
//
|
|
// We have detected a problem already - bail out.
|
|
//
|
|
|
|
break;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Skip all the bytes up to the next page
|
|
// or the NULL string terminator.
|
|
//
|
|
|
|
while (TRUE) {
|
|
|
|
//
|
|
// Read the currect character, while protecting
|
|
// ourselves against a possible exception (alignment, etc).
|
|
//
|
|
|
|
try {
|
|
|
|
Character = *StartAddress;
|
|
}
|
|
except (AVrfpProbeMemExceptionFilter (_exception_code(), _exception_info(), StartAddress)) {
|
|
|
|
//
|
|
// We have detected a problem already - bail out.
|
|
//
|
|
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// If we have found the NULL terminator we are done.
|
|
//
|
|
|
|
if (Character == 0) {
|
|
|
|
FoundNull = TRUE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Go to the next character. If that is at the beginning
|
|
// of a new page we have to check it's attributes.
|
|
//
|
|
|
|
StartAddress += 1;
|
|
|
|
if (StartAddress > EndAddress) {
|
|
|
|
//
|
|
// We have reached the max length of the buffer.
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
if (((ULONG_PTR)StartAddress % PageSize) < sizeof (CHAR)) {
|
|
|
|
//
|
|
// New page.
|
|
//
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Done:
|
|
|
|
//
|
|
// Call the original funtion.
|
|
//
|
|
|
|
Function = AVRFP_GET_ORIGINAL_EXPORT (AVrfpKernel32Thunks,
|
|
AVRF_INDEX_KERNEL32_ISBADSTRINGPTRA);
|
|
|
|
return (*Function) (lpsz, cchMax);
|
|
}
|
|
|
|
//WINBASEAPI
|
|
BOOL
|
|
WINAPI
|
|
AVrfpIsBadStringPtrW(
|
|
LPCWSTR lpsz,
|
|
UINT_PTR cchMax
|
|
)
|
|
{
|
|
typedef BOOL (WINAPI * FUNCTION_TYPE) (LPCWSTR , UINT_PTR);
|
|
FUNCTION_TYPE Function;
|
|
ULONG PageSize;
|
|
BOOL FoundNull;
|
|
BOOL Success;
|
|
LPCWSTR StartAddress;
|
|
LPCWSTR EndAddress;
|
|
WCHAR Character;
|
|
|
|
PageSize = AVrfpSysBasicInfo.PageSize;
|
|
FoundNull = FALSE;
|
|
|
|
StartAddress = lpsz;
|
|
EndAddress = lpsz + cchMax - 1;
|
|
|
|
while (StartAddress <= EndAddress && FoundNull == FALSE) {
|
|
|
|
//
|
|
// Verify read access to the current page.
|
|
//
|
|
|
|
Success = AVrfpProbeMemoryBlockChecks (StartAddress,
|
|
sizeof (WCHAR));
|
|
|
|
if (Success == FALSE) {
|
|
|
|
//
|
|
// We have detected a problem already - bail out.
|
|
//
|
|
|
|
break;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Skip all the bytes up to the next page
|
|
// or the NULL string terminator.
|
|
//
|
|
|
|
while (TRUE) {
|
|
|
|
//
|
|
// Read the currect character, while protecting
|
|
// ourselves against a possible exception (alignment, etc).
|
|
//
|
|
|
|
try {
|
|
|
|
Character = *StartAddress;
|
|
}
|
|
except (AVrfpProbeMemExceptionFilter (_exception_code(), _exception_info(), StartAddress)) {
|
|
|
|
//
|
|
// We have detected a problem already - bail out.
|
|
//
|
|
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// If we have found the NULL terminator we are done.
|
|
//
|
|
|
|
if (Character == 0) {
|
|
|
|
FoundNull = TRUE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Go to the next character. If that is at the beginning
|
|
// of a new page we have to check it's attributes.
|
|
//
|
|
|
|
StartAddress += 1;
|
|
|
|
if (StartAddress > EndAddress) {
|
|
|
|
//
|
|
// We have reached the max length of the buffer.
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
if (((ULONG_PTR)StartAddress % PageSize) < sizeof (WCHAR)) {
|
|
|
|
//
|
|
// New page.
|
|
//
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Done:
|
|
|
|
//
|
|
// Call the original funtion.
|
|
//
|
|
|
|
Function = AVRFP_GET_ORIGINAL_EXPORT (AVrfpKernel32Thunks,
|
|
AVRF_INDEX_KERNEL32_ISBADSTRINGPTRW);
|
|
|
|
return (*Function) (lpsz, cchMax);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
/////////////////////////////////////////// VirtualFree sanity checks
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
VOID
|
|
AVrfVirtualFreeSanityChecks (
|
|
IN SIZE_T dwSize,
|
|
IN DWORD dwFreeType
|
|
)
|
|
{
|
|
//
|
|
// The Win32 layer only allows MEM_RELEASE with Size == 0.
|
|
//
|
|
|
|
if (dwFreeType == MEM_RELEASE && dwSize != 0) {
|
|
|
|
VERIFIER_STOP (APPLICATION_VERIFIER_INVALID_FREEMEM | APPLICATION_VERIFIER_CONTINUABLE_BREAK,
|
|
"Incorrect Size parameter for VirtualFree (MEM_RELEASE) operation",
|
|
dwSize, "Incorrect size used by the application",
|
|
0, "Expected correct size",
|
|
NULL, "",
|
|
NULL, "" );
|
|
}
|
|
}
|
|
|
|
//WINBASEAPI
|
|
BOOL
|
|
WINAPI
|
|
AVrfpVirtualFree(
|
|
IN LPVOID lpAddress,
|
|
IN SIZE_T dwSize,
|
|
IN DWORD dwFreeType
|
|
)
|
|
{
|
|
typedef BOOL (WINAPI * FUNCTION_TYPE) (LPVOID , SIZE_T, DWORD);
|
|
FUNCTION_TYPE Function;
|
|
|
|
if ((AVrfpProvider.VerifierFlags & RTL_VRF_FLG_VIRTUAL_MEM_CHECKS) != 0) {
|
|
|
|
AVrfVirtualFreeSanityChecks (dwSize, dwFreeType);
|
|
}
|
|
|
|
//
|
|
// Call the original funtion.
|
|
//
|
|
|
|
Function = AVRFP_GET_ORIGINAL_EXPORT (AVrfpKernel32Thunks,
|
|
AVRF_INDEX_KERNEL32_VIRTUALFREE);
|
|
|
|
return (*Function) (lpAddress, dwSize, dwFreeType);
|
|
}
|
|
|
|
//WINBASEAPI
|
|
BOOL
|
|
WINAPI
|
|
AVrfpVirtualFreeEx(
|
|
IN HANDLE hProcess,
|
|
IN LPVOID lpAddress,
|
|
IN SIZE_T dwSize,
|
|
IN DWORD dwFreeType
|
|
)
|
|
{
|
|
typedef BOOL (WINAPI * FUNCTION_TYPE) (HANDLE, LPVOID , SIZE_T, DWORD);
|
|
FUNCTION_TYPE Function;
|
|
|
|
if ((AVrfpProvider.VerifierFlags & RTL_VRF_FLG_VIRTUAL_MEM_CHECKS) != 0) {
|
|
|
|
AVrfVirtualFreeSanityChecks (dwSize, dwFreeType);
|
|
}
|
|
|
|
//
|
|
// Call the original funtion.
|
|
//
|
|
|
|
Function = AVRFP_GET_ORIGINAL_EXPORT (AVrfpKernel32Thunks,
|
|
AVRF_INDEX_KERNEL32_VIRTUALFREEEX);
|
|
|
|
return (*Function) (hProcess, lpAddress, dwSize, dwFreeType);
|
|
}
|
|
|