3712 lines
92 KiB
C
3712 lines
92 KiB
C
/*++
|
||
|
||
Copyright (c) 1999 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
altperm.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the routines to support 4K pages on IA64.
|
||
|
||
An alternate set of permissions is kept that are on 4K boundaries.
|
||
Permissions are kept for all memory, not just split pages
|
||
and the information is updated on any call to NtVirtualProtect()
|
||
and NtAllocateVirtualMemory().
|
||
|
||
Author:
|
||
|
||
Koichi Yamada 18-Aug-1998
|
||
Landy Wang (landyw) 02-June-1997
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "mi.h"
|
||
|
||
#if defined(_MIALT4K_)
|
||
|
||
ULONG
|
||
MiFindProtectionForNativePte (
|
||
PVOID VirtualAddress
|
||
);
|
||
|
||
VOID
|
||
MiFillZeroFor4kPage (
|
||
IN PVOID BaseAddress,
|
||
IN PEPROCESS Process
|
||
);
|
||
|
||
VOID
|
||
MiResetAccessBitForNativePtes (
|
||
IN PVOID StartVirtual,
|
||
IN PVOID EndVirtual,
|
||
IN PEPROCESS Process
|
||
);
|
||
|
||
LOGICAL
|
||
MiIsSplitPage (
|
||
IN PVOID Virtual
|
||
);
|
||
|
||
VOID
|
||
MiCopyOnWriteFor4kPage (
|
||
PVOID VirtualAddress
|
||
);
|
||
|
||
VOID
|
||
MiCheckDemandZeroCopyOnWriteFor4kPage (
|
||
PVOID VirtualAddress,
|
||
PEPROCESS Process
|
||
);
|
||
|
||
VOID
|
||
MiCheckVirtualAddressFor4kPage (
|
||
PVOID VirtualAddress,
|
||
PEPROCESS Process
|
||
);
|
||
|
||
LOGICAL
|
||
MiIsNativeGuardPage (
|
||
IN PVOID VirtualAddress
|
||
);
|
||
|
||
VOID
|
||
MiSetNativePteProtection (
|
||
IN PVOID VirtualAddress,
|
||
IN ULONGLONG NewPteProtection,
|
||
IN LOGICAL PageIsSplit,
|
||
IN PEPROCESS CurrentProcess
|
||
);
|
||
|
||
extern PMMPTE MmPteHit;
|
||
|
||
NTSTATUS
|
||
MmX86Fault (
|
||
IN ULONG_PTR FaultStatus,
|
||
IN PVOID VirtualAddress,
|
||
IN KPROCESSOR_MODE PreviousMode,
|
||
IN PVOID TrapInformation
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called by the kernel on data or instruction
|
||
access faults if CurrentProcess->Wow64Process is non-NULL and the
|
||
faulting address is within the first 2GB.
|
||
|
||
This routine determines the type of fault by checking the alternate
|
||
4Kb granular page table and calls MmAccessFault() if necessary to
|
||
handle the page fault or the write fault.
|
||
|
||
Arguments:
|
||
|
||
FaultStatus - Supplies fault status information bits.
|
||
|
||
VirtualAddress - Supplies the virtual address which caused the fault.
|
||
|
||
PreviousMode - Supplies the mode (kernel or user) in which the fault
|
||
occurred.
|
||
|
||
TrapInformation - Opaque information about the trap, interpreted by the
|
||
kernel, not Mm. Needed to allow fast interlocked access
|
||
to operate correctly.
|
||
|
||
Return Value:
|
||
|
||
Returns the status of the fault handling operation. Can be one of:
|
||
- Success.
|
||
- Access Violation.
|
||
- Guard Page Violation.
|
||
- In-page Error.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, APCs disabled.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i;
|
||
PMMPTE PointerAltPte;
|
||
PMMPTE PointerAltPteForNativePage;
|
||
MMPTE AltPteContents;
|
||
PMMPTE PointerPte;
|
||
PMMPTE PointerPde;
|
||
ULONGLONG NewPteProtection;
|
||
LOGICAL FillZero;
|
||
LOGICAL PageIsSplit;
|
||
LOGICAL SharedPageFault;
|
||
LOGICAL NativeGuardPage;
|
||
PEPROCESS CurrentProcess;
|
||
PWOW64_PROCESS Wow64Process;
|
||
KIRQL PreviousIrql;
|
||
KIRQL OldIrql;
|
||
NTSTATUS status;
|
||
ULONG OriginalProtection;
|
||
ULONGLONG ProtectionMaskOriginal;
|
||
PMMPTE ProtoPte;
|
||
PMMPFN Pfn1;
|
||
PVOID OriginalVirtualAddress;
|
||
|
||
ASSERT (VirtualAddress < (PVOID)MM_MAX_WOW64_ADDRESS);
|
||
|
||
PreviousIrql = KeGetCurrentIrql ();
|
||
|
||
if (PreviousIrql > APC_LEVEL) {
|
||
return MmAccessFault (FaultStatus,
|
||
VirtualAddress,
|
||
PreviousMode,
|
||
TrapInformation);
|
||
}
|
||
|
||
NewPteProtection = 0;
|
||
FillZero = FALSE;
|
||
PageIsSplit = FALSE;
|
||
SharedPageFault = FALSE;
|
||
NativeGuardPage = FALSE;
|
||
PointerAltPteForNativePage = NULL;
|
||
OriginalVirtualAddress = VirtualAddress;
|
||
|
||
CurrentProcess = PsGetCurrentProcess ();
|
||
|
||
Wow64Process = CurrentProcess->Wow64Process;
|
||
|
||
PointerPte = MiGetPteAddress (VirtualAddress);
|
||
PointerAltPte = MiGetAltPteAddress (VirtualAddress);
|
||
|
||
#if DBG
|
||
if (PointerPte == MmPteHit) {
|
||
DbgPrint ("MM: PTE hit at %p\n", MmPteHit);
|
||
DbgBreakPoint ();
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Acquire the alternate table mutex, also blocking APCs.
|
||
//
|
||
|
||
LOCK_ALTERNATE_TABLE (Wow64Process);
|
||
|
||
//
|
||
// If a fork operation is in progress and the faulting thread
|
||
// is not the thread performing the fork operation, block until
|
||
// the fork is completed.
|
||
//
|
||
|
||
if (CurrentProcess->ForkInProgress != NULL) {
|
||
|
||
UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
KeLowerIrql (PreviousIrql);
|
||
|
||
LOCK_WS (CurrentProcess);
|
||
|
||
if (MiWaitForForkToComplete (CurrentProcess, FALSE) == FALSE) {
|
||
ASSERT (FALSE);
|
||
}
|
||
|
||
UNLOCK_WS (CurrentProcess);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Check to see if the protection is registered in the alternate entry.
|
||
//
|
||
|
||
if (MI_CHECK_BIT (Wow64Process->AltPermBitmap,
|
||
MI_VA_TO_VPN(VirtualAddress)) == 0) {
|
||
|
||
MiCheckVirtualAddressFor4kPage (VirtualAddress, CurrentProcess);
|
||
}
|
||
|
||
//
|
||
// Read the alternate PTE contents.
|
||
//
|
||
|
||
AltPteContents = *PointerAltPte;
|
||
|
||
//
|
||
// Check to see if the alternate entry is no access.
|
||
//
|
||
|
||
if (AltPteContents.u.Alt.NoAccess != 0) {
|
||
|
||
//
|
||
// This 4KB page is no access.
|
||
//
|
||
|
||
status = STATUS_ACCESS_VIOLATION;
|
||
|
||
#if DBG
|
||
if (MmDebug & MM_DBG_STOP_ON_ACCVIO) {
|
||
DbgPrint ("MM:access violation - %p\n",OriginalVirtualAddress);
|
||
MiFormatPte (PointerPte);
|
||
DbgBreakPoint ();
|
||
}
|
||
#endif
|
||
goto return_status;
|
||
}
|
||
|
||
//
|
||
// Check to see if the alternate entry is empty or if anyone has made any
|
||
// commitments for the shared pages.
|
||
//
|
||
|
||
if ((AltPteContents.u.Long == 0) ||
|
||
((AltPteContents.u.Alt.Commit == 0) && (AltPteContents.u.Alt.Private == 0))) {
|
||
//
|
||
// If empty, get the protection information and fill the entry.
|
||
//
|
||
|
||
LOCK_WS (CurrentProcess);
|
||
|
||
ProtoPte = MiCheckVirtualAddress (VirtualAddress, &OriginalProtection);
|
||
|
||
if (ProtoPte != NULL) {
|
||
|
||
if (OriginalProtection == MM_UNKNOWN_PROTECTION) {
|
||
|
||
if (!MI_IS_PHYSICAL_ADDRESS(ProtoPte)) {
|
||
PointerPde = MiGetPteAddress (ProtoPte);
|
||
LOCK_PFN (OldIrql);
|
||
if (PointerPde->u.Hard.Valid == 0) {
|
||
MiMakeSystemAddressValidPfn (ProtoPte);
|
||
}
|
||
Pfn1 = MI_PFN_ELEMENT (PointerPde->u.Hard.PageFrameNumber);
|
||
MI_ADD_LOCKED_PAGE_CHARGE(Pfn1, 28);
|
||
Pfn1->u3.e2.ReferenceCount += 1;
|
||
ASSERT (Pfn1->u3.e2.ReferenceCount > 1);
|
||
UNLOCK_PFN (OldIrql);
|
||
}
|
||
else {
|
||
Pfn1 = NULL;
|
||
}
|
||
|
||
OriginalProtection =
|
||
MiMakeProtectionMask (MiGetPageProtection (ProtoPte,
|
||
CurrentProcess,
|
||
FALSE));
|
||
|
||
//
|
||
// Unlock the page containing the prototype PTEs.
|
||
//
|
||
|
||
if (Pfn1 != NULL) {
|
||
ASSERT (!MI_IS_PHYSICAL_ADDRESS(ProtoPte));
|
||
LOCK_PFN (OldIrql);
|
||
ASSERT (Pfn1->u3.e2.ReferenceCount > 1);
|
||
MI_REMOVE_LOCKED_PAGE_CHARGE(Pfn1, 29);
|
||
Pfn1->u3.e2.ReferenceCount -= 1;
|
||
UNLOCK_PFN (OldIrql);
|
||
}
|
||
}
|
||
|
||
UNLOCK_WS (CurrentProcess);
|
||
|
||
if (OriginalProtection == MM_INVALID_PROTECTION) {
|
||
status = STATUS_ACCESS_VIOLATION;
|
||
#if DBG
|
||
if (MmDebug & MM_DBG_STOP_ON_ACCVIO) {
|
||
DbgPrint ("MM:access violation - %p\n",OriginalVirtualAddress);
|
||
MiFormatPte (PointerPte);
|
||
DbgBreakPoint ();
|
||
}
|
||
#endif
|
||
goto return_status;
|
||
}
|
||
|
||
if (OriginalProtection != MM_NOACCESS) {
|
||
|
||
ProtectionMaskOriginal = MiMakeProtectionAteMask (OriginalProtection);
|
||
|
||
SharedPageFault = TRUE;
|
||
ProtectionMaskOriginal |= MM_ATE_COMMIT;
|
||
|
||
AltPteContents.u.Long = ProtectionMaskOriginal;
|
||
AltPteContents.u.Alt.Protection = OriginalProtection;
|
||
|
||
//
|
||
// Atomically update the PTE.
|
||
//
|
||
|
||
PointerAltPte->u.Long = AltPteContents.u.Long;
|
||
}
|
||
}
|
||
else {
|
||
UNLOCK_WS (CurrentProcess);
|
||
}
|
||
}
|
||
|
||
if (AltPteContents.u.Alt.Commit == 0) {
|
||
|
||
//
|
||
// If the page is not committed, return an STATUS_ACCESS_VIOLATION.
|
||
//
|
||
|
||
status = STATUS_ACCESS_VIOLATION;
|
||
#if DBG
|
||
if (MmDebug & MM_DBG_STOP_ON_ACCVIO) {
|
||
DbgPrint ("MM:access violation - %p\n",OriginalVirtualAddress);
|
||
MiFormatPte (PointerPte);
|
||
DbgBreakPoint ();
|
||
}
|
||
#endif
|
||
goto return_status;
|
||
}
|
||
|
||
//
|
||
// Check whether the faulting page is split into 4k pages.
|
||
//
|
||
|
||
PageIsSplit = MiIsSplitPage (VirtualAddress);
|
||
|
||
//
|
||
// Get the real protection for the native PTE.
|
||
//
|
||
|
||
NewPteProtection = MiFindProtectionForNativePte (VirtualAddress);
|
||
|
||
//
|
||
// Set the Protection for the native PTE
|
||
//
|
||
|
||
MiSetNativePteProtection (VirtualAddress,
|
||
NewPteProtection,
|
||
PageIsSplit,
|
||
CurrentProcess);
|
||
|
||
|
||
//
|
||
// Check the indirect PTE reference case. If so, set the protection for
|
||
// the indirect PTE too.
|
||
//
|
||
|
||
if (AltPteContents.u.Alt.PteIndirect != 0) {
|
||
|
||
PointerPte = (PMMPTE)(AltPteContents.u.Alt.PteOffset + PTE_UBASE);
|
||
|
||
VirtualAddress = MiGetVirtualAddressMappedByPte (PointerPte);
|
||
|
||
NewPteProtection = AltPteContents.u.Long & ALT_PROTECTION_MASK;
|
||
|
||
if (AltPteContents.u.Alt.CopyOnWrite != 0) {
|
||
NewPteProtection |= MM_PTE_COPY_ON_WRITE_MASK;
|
||
}
|
||
|
||
MiSetNativePteProtection (VirtualAddress,
|
||
NewPteProtection,
|
||
FALSE,
|
||
CurrentProcess);
|
||
}
|
||
|
||
//
|
||
// Since we release the AltTable lock before calling MmAccessFault,
|
||
// there is a chance that two threads may execute concurrently inside
|
||
// MmAccessFault, which would yield bad results since the initial native
|
||
// PTE for the page has only READ protection on it. So if two threads
|
||
// fault on the same address, one of them will execute through all of
|
||
// this routine, however the other one will just return STATUS_SUCCESS
|
||
// which will cause another fault to happen in which the protections
|
||
// will be fixed on the native page.
|
||
//
|
||
// Note that in addition to the dual thread case there is also the case
|
||
// of a single thread which also has an overlapped I/O pending (for example)
|
||
// which can trigger an APC completion memory copy to the same page.
|
||
// Protect against this by remaining at APC_LEVEL until clearing the
|
||
// inpage in progress in the alternate PTE.
|
||
//
|
||
|
||
if (MI_ALT_PTE_IN_PAGE_IN_PROGRESS (PointerAltPte) != 0) {
|
||
|
||
//
|
||
// Release the Alt PTE lock
|
||
//
|
||
|
||
UNLOCK_ALTERNATE_TABLE (Wow64Process);
|
||
|
||
//
|
||
// Flush the TB as MiSetNativePteProtection may have edited the PTE.
|
||
//
|
||
|
||
KiFlushSingleTb (TRUE, OriginalVirtualAddress);
|
||
|
||
//
|
||
// Delay execution so that if this is a high priority thread,
|
||
// it won't starve the other thread (that's doing the actual inpage)
|
||
// as it may be running at a lower priority.
|
||
//
|
||
|
||
KeDelayExecutionThread (KernelMode, FALSE, (PLARGE_INTEGER)&MmShortTime);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// The faulting 4kb page must be a valid page, but we need to resolve it
|
||
// on a case by case basis.
|
||
//
|
||
|
||
ASSERT (AltPteContents.u.Long != 0);
|
||
ASSERT (AltPteContents.u.Alt.Commit != 0);
|
||
|
||
if (AltPteContents.u.Alt.Accessed == 0) {
|
||
|
||
//
|
||
// When PointerAte->u.Hard.Accessed is zero, there are 4 possibilities:
|
||
//
|
||
// 1. Lowest Protection
|
||
// 2. 4kb Demand Zero
|
||
// 3. GUARD page fault
|
||
// 4. This 4kb page is no access, but the other 4K page(s) within
|
||
// the native page has accessible permissions.
|
||
//
|
||
|
||
if (AltPteContents.u.Alt.FillZero != 0) {
|
||
|
||
//
|
||
// Schedule it later.
|
||
//
|
||
|
||
FillZero = TRUE;
|
||
}
|
||
|
||
if ((AltPteContents.u.Alt.Protection & MM_GUARD_PAGE) != 0) {
|
||
goto CheckGuardPage;
|
||
}
|
||
|
||
if (FillZero == FALSE) {
|
||
|
||
//
|
||
// This 4kb page has permission set to no access.
|
||
//
|
||
|
||
status = STATUS_ACCESS_VIOLATION;
|
||
#if DBG
|
||
if (MmDebug & MM_DBG_STOP_ON_ACCVIO) {
|
||
DbgPrint ("MM:access violation - %p\n",OriginalVirtualAddress);
|
||
MiFormatPte (PointerPte);
|
||
DbgBreakPoint ();
|
||
}
|
||
#endif
|
||
|
||
goto return_status;
|
||
}
|
||
}
|
||
|
||
if (MI_FAULT_STATUS_INDICATES_EXECUTION(FaultStatus)) {
|
||
|
||
//
|
||
// Execute permission is already given to IA32 by setting it in
|
||
// MI_MAKE_VALID_PTE().
|
||
//
|
||
|
||
}
|
||
else if (MI_FAULT_STATUS_INDICATES_WRITE(FaultStatus)) {
|
||
|
||
//
|
||
// Check to see if this is a copy-on-write page.
|
||
//
|
||
|
||
if (AltPteContents.u.Alt.CopyOnWrite != 0) {
|
||
|
||
//
|
||
// Let MmAccessFault() perform the copy-on-write.
|
||
//
|
||
|
||
status = MmAccessFault (FaultStatus,
|
||
VirtualAddress,
|
||
PreviousMode,
|
||
TrapInformation);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
MiCopyOnWriteFor4kPage (OriginalVirtualAddress);
|
||
}
|
||
|
||
goto return_status;
|
||
}
|
||
|
||
if (AltPteContents.u.Hard.Write == 0) {
|
||
status = STATUS_ACCESS_VIOLATION;
|
||
#if DBG
|
||
if (MmDebug & MM_DBG_STOP_ON_ACCVIO) {
|
||
DbgPrint ("MM:access violation - %p\n",OriginalVirtualAddress);
|
||
MiFormatPte (PointerPte);
|
||
DbgBreakPoint ();
|
||
}
|
||
#endif
|
||
goto return_status;
|
||
}
|
||
}
|
||
|
||
CheckGuardPage:
|
||
|
||
//
|
||
// Indicate that we have begun updating the PTE for this page.
|
||
// Subsequent faults on this native page will be restarted.
|
||
// This should happen only if the PTE isn't valid.
|
||
//
|
||
|
||
PointerAltPteForNativePage = MiGetAltPteAddress (PAGE_ALIGN (VirtualAddress));
|
||
|
||
for (i = 0; i < SPLITS_PER_PAGE; i += 1) {
|
||
PointerAltPteForNativePage->u.Alt.InPageInProgress = TRUE;
|
||
PointerAltPteForNativePage += 1;
|
||
}
|
||
|
||
//
|
||
// Let MmAccessFault() perform an inpage, dirty-bit setting, etc.
|
||
//
|
||
// Release the alternate table mutex but stay at APC_LEVEL to prevent an
|
||
// incoming APC that references the same page from deadlocking this thread.
|
||
// It is safe to drop below APC_LEVEL only after the in progress bit in
|
||
// the alternate PTE has been cleared.
|
||
//
|
||
|
||
Wow64Process->AlternateTableAcquiredUnsafe = MI_MUTEX_ACQUIRED_UNSAFE;
|
||
|
||
UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
|
||
status = MmAccessFault (FaultStatus,
|
||
VirtualAddress,
|
||
PreviousMode,
|
||
TrapInformation);
|
||
|
||
LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
|
||
for (i = 0; i < SPLITS_PER_PAGE; i += 1) {
|
||
PointerAltPteForNativePage -= 1;
|
||
PointerAltPteForNativePage->u.Alt.InPageInProgress = FALSE;
|
||
}
|
||
|
||
AltPteContents = *PointerAltPte;
|
||
|
||
if ((AltPteContents.u.Alt.Protection & MM_GUARD_PAGE) != 0) {
|
||
|
||
AltPteContents = *PointerAltPte;
|
||
AltPteContents.u.Alt.Protection &= ~MM_GUARD_PAGE;
|
||
AltPteContents.u.Alt.Accessed = 1;
|
||
|
||
PointerAltPte->u.Long = AltPteContents.u.Long;
|
||
|
||
if ((status != STATUS_PAGE_FAULT_GUARD_PAGE) &&
|
||
(status != STATUS_STACK_OVERFLOW)) {
|
||
|
||
UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
KeLowerIrql (PreviousIrql);
|
||
|
||
status = MiCheckForUserStackOverflow (VirtualAddress);
|
||
|
||
KeRaiseIrql (APC_LEVEL, &PreviousIrql);
|
||
LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
}
|
||
}
|
||
else if (status == STATUS_GUARD_PAGE_VIOLATION) {
|
||
|
||
//
|
||
// Native PTE has the guard bit set, but the AltPte
|
||
// doesn't have it.
|
||
//
|
||
|
||
NativeGuardPage = MiIsNativeGuardPage (VirtualAddress);
|
||
|
||
if (NativeGuardPage == TRUE) {
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
|
||
}
|
||
else if ((SharedPageFault == TRUE) && (status == STATUS_ACCESS_VIOLATION)) {
|
||
|
||
PointerAltPte->u.Alt.Commit = 0;
|
||
}
|
||
|
||
return_status:
|
||
|
||
KiFlushSingleTb (TRUE, OriginalVirtualAddress);
|
||
|
||
if (FillZero == TRUE) {
|
||
MiFillZeroFor4kPage (VirtualAddress, CurrentProcess);
|
||
}
|
||
|
||
UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
KeLowerIrql (PreviousIrql);
|
||
|
||
return status;
|
||
}
|
||
|
||
ULONG
|
||
MiFindProtectionForNativePte (
|
||
IN PVOID VirtualAddress
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function finds the protection for the native PTE.
|
||
|
||
Arguments:
|
||
|
||
VirtualAddress - Supplies a virtual address to be examined for
|
||
the protection of the PTE.
|
||
|
||
Return Value:
|
||
|
||
The protection code.
|
||
|
||
Environment:
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i;
|
||
ULONG ProtectionCode;
|
||
PMMPTE PointerAltPte;
|
||
MMPTE AltPteContents;
|
||
|
||
ProtectionCode = 0;
|
||
|
||
PointerAltPte = MiGetAltPteAddress (PAGE_ALIGN(VirtualAddress));
|
||
|
||
for (i = 0; i < SPLITS_PER_PAGE; i += 1) {
|
||
|
||
AltPteContents.u.Long = PointerAltPte->u.Long;
|
||
|
||
if (AltPteContents.u.Alt.PteIndirect == 0) {
|
||
ProtectionCode |= (PointerAltPte->u.Long & ALT_PROTECTION_MASK);
|
||
}
|
||
|
||
PointerAltPte += 1;
|
||
}
|
||
|
||
return ProtectionCode;
|
||
}
|
||
|
||
|
||
//
|
||
// Define and initialize the protection conversion table for the
|
||
// Alternate Permision Table Entries.
|
||
//
|
||
|
||
ULONGLONG MmProtectToAteMask[32] = {
|
||
MM_PTE_NOACCESS | MM_ATE_NOACCESS,
|
||
MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK,
|
||
MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK,
|
||
MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK,
|
||
MM_PTE_EXECUTE_READWRITE | MM_PTE_ACCESS_MASK,
|
||
MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK | MM_ATE_COPY_ON_WRITE,
|
||
MM_PTE_EXECUTE_READWRITE | MM_PTE_ACCESS_MASK,
|
||
MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK | MM_ATE_COPY_ON_WRITE,
|
||
MM_PTE_NOACCESS | MM_ATE_NOACCESS,
|
||
MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK,
|
||
MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK,
|
||
MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK,
|
||
MM_PTE_EXECUTE_READWRITE | MM_PTE_ACCESS_MASK,
|
||
MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK | MM_ATE_COPY_ON_WRITE,
|
||
MM_PTE_EXECUTE_READWRITE | MM_PTE_ACCESS_MASK,
|
||
MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK | MM_ATE_COPY_ON_WRITE,
|
||
MM_PTE_NOACCESS | MM_ATE_NOACCESS,
|
||
MM_PTE_EXECUTE_READ,
|
||
MM_PTE_EXECUTE_READ,
|
||
MM_PTE_EXECUTE_READ,
|
||
MM_PTE_EXECUTE_READWRITE,
|
||
MM_PTE_EXECUTE_READ | MM_ATE_COPY_ON_WRITE,
|
||
MM_PTE_EXECUTE_READWRITE,
|
||
MM_PTE_EXECUTE_READ | MM_ATE_COPY_ON_WRITE,
|
||
MM_PTE_NOACCESS | MM_ATE_NOACCESS,
|
||
MM_PTE_EXECUTE_READ,
|
||
MM_PTE_EXECUTE_READ,
|
||
MM_PTE_EXECUTE_READ,
|
||
MM_PTE_EXECUTE_READWRITE,
|
||
MM_PTE_EXECUTE_READ | MM_ATE_COPY_ON_WRITE,
|
||
MM_PTE_EXECUTE_READWRITE,
|
||
MM_PTE_EXECUTE_READ | MM_ATE_COPY_ON_WRITE
|
||
};
|
||
|
||
#define MiMakeProtectionAteMask(NewProtect) MmProtectToAteMask[NewProtect]
|
||
|
||
VOID
|
||
MiProtectFor4kPage (
|
||
IN PVOID Base,
|
||
IN SIZE_T Size,
|
||
IN ULONG NewProtect,
|
||
IN ULONG Flags,
|
||
IN PEPROCESS Process
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sets the permissions on the alternate bitmap (based on
|
||
4K page sizes). The base and size are assumed to be aligned for
|
||
4K pages already.
|
||
|
||
Arguments:
|
||
|
||
Base - Supplies the base address (assumed to be 4K aligned already).
|
||
|
||
Size - Supplies the size to be protected (assumed to be 4K aligned already).
|
||
|
||
NewProtect - Supplies the protection for the new pages.
|
||
|
||
Flags - Supplies the alternate table entry request flags.
|
||
|
||
Process - Supplies a pointer to the process in which to create the
|
||
protections on the alternate table.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
Kernel mode. Address creation mutex held at APC_LEVEL.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i;
|
||
RTL_BITMAP BitMap;
|
||
ULONG NumberOfPtes;
|
||
ULONG Starting4KVpn;
|
||
PVOID Starting4KAddress;
|
||
PVOID Ending4KAddress;
|
||
PVOID VirtualAddress;
|
||
ULONG NewProtectNotCopy;
|
||
ULONGLONG ProtectionMask;
|
||
ULONGLONG ProtectionMaskNotCopy;
|
||
PMMPTE StartAltPte;
|
||
PMMPTE EndAltPte;
|
||
PMMPTE StartAltPte0;
|
||
PMMPTE EndAltPte0;
|
||
PWOW64_PROCESS Wow64Process;
|
||
PVOID Virtual[MM_MAXIMUM_FLUSH_COUNT];
|
||
MMPTE AltPteContents;
|
||
MMPTE TempAltPte;
|
||
|
||
Starting4KAddress = Base;
|
||
Ending4KAddress = (PCHAR)Base + Size - 1;
|
||
|
||
//
|
||
// If the addresses are not WOW64 then nothing needs to be done here.
|
||
//
|
||
|
||
if ((Starting4KAddress >= (PVOID)MM_MAX_WOW64_ADDRESS) ||
|
||
(Ending4KAddress >= (PVOID)MM_MAX_WOW64_ADDRESS)) {
|
||
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Set up the protection to be used for this range of addresses.
|
||
//
|
||
|
||
ProtectionMask = MiMakeProtectionAteMask (NewProtect);
|
||
|
||
if ((NewProtect & MM_COPY_ON_WRITE_MASK) == MM_COPY_ON_WRITE_MASK) {
|
||
NewProtectNotCopy = NewProtect & ~MM_PROTECTION_COPY_MASK;
|
||
ProtectionMaskNotCopy = MiMakeProtectionAteMask (NewProtectNotCopy);
|
||
}
|
||
else {
|
||
NewProtectNotCopy = NewProtect;
|
||
ProtectionMaskNotCopy = ProtectionMask;
|
||
}
|
||
|
||
if (Flags & ALT_COMMIT) {
|
||
ProtectionMask |= MM_ATE_COMMIT;
|
||
ProtectionMaskNotCopy |= MM_ATE_COMMIT;
|
||
}
|
||
|
||
//
|
||
// Get the entry in the table for each of these addresses.
|
||
//
|
||
|
||
StartAltPte = MiGetAltPteAddress (Starting4KAddress);
|
||
EndAltPte = MiGetAltPteAddress (Ending4KAddress);
|
||
|
||
NumberOfPtes = (ULONG) ADDRESS_AND_SIZE_TO_SPAN_PAGES (Starting4KAddress,
|
||
(ULONG_PTR)Ending4KAddress -
|
||
(ULONG_PTR)Starting4KAddress);
|
||
ASSERT (NumberOfPtes != 0);
|
||
|
||
//
|
||
// Ensure the proper native TB entries get flushed.
|
||
//
|
||
|
||
if (NumberOfPtes < MM_MAXIMUM_FLUSH_COUNT) {
|
||
|
||
VirtualAddress = PAGE_ALIGN (Starting4KAddress);
|
||
|
||
for (i = 0; i < NumberOfPtes; i += 1) {
|
||
Virtual[i] = (PVOID)VirtualAddress;
|
||
VirtualAddress = (PVOID)((ULONG_PTR)VirtualAddress + PAGE_SIZE);
|
||
}
|
||
}
|
||
|
||
StartAltPte0 = MiGetAltPteAddress (PAGE_ALIGN (Starting4KAddress));
|
||
EndAltPte0 = MiGetAltPteAddress ((ULONG_PTR)PAGE_ALIGN(Ending4KAddress)+PAGE_SIZE-1);
|
||
|
||
Wow64Process = Process->Wow64Process;
|
||
|
||
Starting4KVpn = (ULONG) MI_VA_TO_VPN (Starting4KAddress);
|
||
|
||
//
|
||
// Acquire the mutex guarding the alternate page table.
|
||
//
|
||
|
||
LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
|
||
if (!(Flags & ALT_ALLOCATE) &&
|
||
(MI_CHECK_BIT(Wow64Process->AltPermBitmap, Starting4KVpn) == 0)) {
|
||
|
||
UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Change all of the protections.
|
||
//
|
||
|
||
while (StartAltPte <= EndAltPte) {
|
||
|
||
AltPteContents.u.Long = StartAltPte->u.Long;
|
||
|
||
TempAltPte.u.Long = ProtectionMask;
|
||
TempAltPte.u.Alt.Protection = NewProtect;
|
||
|
||
if (!(Flags & ALT_ALLOCATE)) {
|
||
|
||
if (AltPteContents.u.Alt.Private != 0) {
|
||
|
||
//
|
||
// If it is already private, don't make it writecopy.
|
||
//
|
||
|
||
TempAltPte.u.Long = ProtectionMaskNotCopy;
|
||
TempAltPte.u.Alt.Protection = NewProtectNotCopy;
|
||
|
||
//
|
||
// Private is sticky bit
|
||
//
|
||
|
||
TempAltPte.u.Alt.Private = 1;
|
||
}
|
||
|
||
if (AltPteContents.u.Alt.FillZero != 0) {
|
||
|
||
TempAltPte.u.Alt.Accessed = 0;
|
||
TempAltPte.u.Alt.FillZero = 1;
|
||
}
|
||
|
||
//
|
||
// Leave the other sticky attribute bits
|
||
//
|
||
|
||
TempAltPte.u.Alt.Lock = AltPteContents.u.Alt.Lock;
|
||
TempAltPte.u.Alt.PteIndirect = AltPteContents.u.Alt.PteIndirect;
|
||
TempAltPte.u.Alt.PteOffset = AltPteContents.u.Alt.PteOffset;
|
||
}
|
||
|
||
if (Flags & ALT_CHANGE) {
|
||
|
||
//
|
||
// If it is a change request, make commit sticky.
|
||
//
|
||
|
||
TempAltPte.u.Alt.Commit = AltPteContents.u.Alt.Commit;
|
||
}
|
||
|
||
//
|
||
// Atomic PTE update.
|
||
//
|
||
|
||
StartAltPte->u.Long = TempAltPte.u.Long;
|
||
|
||
StartAltPte += 1;
|
||
}
|
||
|
||
if (Flags & ALT_ALLOCATE) {
|
||
|
||
//
|
||
// Fill the empty Alt PTE as NoAccess ATE at the end.
|
||
//
|
||
|
||
while (EndAltPte <= EndAltPte0) {
|
||
|
||
if (EndAltPte->u.Long == 0) {
|
||
|
||
TempAltPte.u.Long = EndAltPte->u.Long;
|
||
TempAltPte.u.Alt.NoAccess = 1;
|
||
|
||
//
|
||
// Atomic PTE update.
|
||
//
|
||
|
||
EndAltPte->u.Long = TempAltPte.u.Long;
|
||
}
|
||
|
||
EndAltPte += 1;
|
||
}
|
||
|
||
//
|
||
// Update the permission bitmap.
|
||
//
|
||
// Initialize the bitmap inline for speed.
|
||
//
|
||
|
||
BitMap.SizeOfBitMap = MM_MAX_WOW64_ADDRESS >> PTI_SHIFT;
|
||
BitMap.Buffer = Wow64Process->AltPermBitmap;
|
||
|
||
RtlSetBits (&BitMap, Starting4KVpn, NumberOfPtes);
|
||
}
|
||
|
||
MiResetAccessBitForNativePtes (Starting4KAddress, Ending4KAddress, Process);
|
||
|
||
if (NumberOfPtes < MM_MAXIMUM_FLUSH_COUNT) {
|
||
|
||
KeFlushMultipleTb (NumberOfPtes,
|
||
&Virtual[0],
|
||
TRUE,
|
||
TRUE,
|
||
NULL,
|
||
ZeroPte.u.Flush);
|
||
}
|
||
else {
|
||
KeFlushEntireTb (TRUE, TRUE);
|
||
}
|
||
|
||
UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
}
|
||
|
||
VOID
|
||
MiProtectMapFileFor4kPage (
|
||
IN PVOID Base,
|
||
IN SIZE_T Size,
|
||
IN ULONG NewProtect,
|
||
IN SIZE_T CommitSize,
|
||
IN PMMPTE PointerPte,
|
||
IN PMMPTE LastPte,
|
||
IN PEPROCESS Process
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sets the permissions on the alternate bitmap (based on
|
||
4K page sizes). The base and size are assumed to be aligned for
|
||
4K pages already.
|
||
|
||
Arguments:
|
||
|
||
Base - Supplies the base address (assumed to be 4K aligned already).
|
||
|
||
Size - Supplies the size to be protected (assumed to be 4K aligned already).
|
||
|
||
NewProtect - Supplies the protection for the new pages.
|
||
|
||
CommitSize - Supplies the commit size.
|
||
|
||
PointerPte - Supplies the starting PTE.
|
||
|
||
LastPte - Supplies the last PTE.
|
||
|
||
Process - Supplies a pointer to the process in which to create the
|
||
protections on the alternate table.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
Kernel mode. Address creation mutex held at APC_LEVEL.
|
||
|
||
--*/
|
||
|
||
{
|
||
RTL_BITMAP BitMap;
|
||
PVOID Starting4KAddress;
|
||
PVOID Ending4KAddress;
|
||
ULONGLONG ProtectionMask;
|
||
PMMPTE StartAltPte;
|
||
PMMPTE EndAltPte;
|
||
PMMPTE EndAltPte0;
|
||
PWOW64_PROCESS Wow64Process;
|
||
MMPTE TempAltPte;
|
||
PMMPTE LastCommitPte;
|
||
|
||
Wow64Process = Process->Wow64Process;
|
||
Starting4KAddress = Base;
|
||
Ending4KAddress = (PCHAR)Base + Size - 1;
|
||
|
||
//
|
||
// If the addresses are not WOW64 then nothing needs to be done here.
|
||
//
|
||
|
||
if ((Starting4KAddress >= (PVOID)MM_MAX_WOW64_ADDRESS) ||
|
||
(Ending4KAddress >= (PVOID)MM_MAX_WOW64_ADDRESS)) {
|
||
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Set up the protection to be used for this range of addresses.
|
||
//
|
||
|
||
ProtectionMask = MiMakeProtectionAteMask (NewProtect);
|
||
|
||
//
|
||
// Get the entry in the table for each of these addresses.
|
||
//
|
||
|
||
StartAltPte = MiGetAltPteAddress (Starting4KAddress);
|
||
EndAltPte = MiGetAltPteAddress (Ending4KAddress);
|
||
EndAltPte0 = MiGetAltPteAddress((ULONG_PTR)PAGE_ALIGN(Ending4KAddress)+PAGE_SIZE-1);
|
||
|
||
LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
|
||
ExAcquireFastMutexUnsafe (&MmSectionCommitMutex);
|
||
|
||
//
|
||
// And then change all of the protections.
|
||
//
|
||
|
||
LastCommitPte = PointerPte + BYTES_TO_PAGES(CommitSize);
|
||
|
||
TempAltPte.u.Long = ProtectionMask;
|
||
TempAltPte.u.Alt.Protection = NewProtect;
|
||
|
||
while (StartAltPte <= EndAltPte) {
|
||
|
||
if (PointerPte < LastCommitPte) {
|
||
TempAltPte.u.Alt.Commit = 1;
|
||
}
|
||
else if ((PointerPte <= LastPte) && (PointerPte->u.Long != 0)) {
|
||
TempAltPte.u.Alt.Commit = 1;
|
||
}
|
||
else {
|
||
TempAltPte.u.Alt.Commit = 0;
|
||
}
|
||
|
||
//
|
||
// Atomic PTE update.
|
||
//
|
||
|
||
StartAltPte->u.Long = TempAltPte.u.Long;
|
||
|
||
StartAltPte += 1;
|
||
|
||
if (((ULONG_PTR)StartAltPte & ((SPLITS_PER_PAGE * sizeof(MMPTE))-1)) == 0) {
|
||
PointerPte += 1;
|
||
}
|
||
}
|
||
|
||
ExReleaseFastMutexUnsafe (&MmSectionCommitMutex);
|
||
|
||
//
|
||
// Fill the empty Alt PTE as NoAccess ATE at the end.
|
||
//
|
||
|
||
while (EndAltPte <= EndAltPte0) {
|
||
|
||
if (EndAltPte->u.Long == 0) {
|
||
|
||
TempAltPte.u.Long = EndAltPte->u.Long;
|
||
TempAltPte.u.Alt.NoAccess = 1;
|
||
|
||
//
|
||
// Atomic PTE size update.
|
||
//
|
||
|
||
EndAltPte->u.Long = TempAltPte.u.Long;
|
||
}
|
||
|
||
EndAltPte += 1;
|
||
}
|
||
|
||
//
|
||
// Initialize the bitmap inline for speed.
|
||
//
|
||
|
||
BitMap.SizeOfBitMap = MM_MAX_WOW64_ADDRESS >> PTI_SHIFT;
|
||
BitMap.Buffer = Wow64Process->AltPermBitmap;
|
||
|
||
RtlSetBits (&BitMap,
|
||
(ULONG) MI_VA_TO_VPN (Base),
|
||
(ULONG) (MI_VA_TO_VPN (Ending4KAddress) - MI_VA_TO_VPN (Base) + 1));
|
||
|
||
UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
}
|
||
|
||
VOID
|
||
MiProtectImageFileFor4kPage (
|
||
IN PVOID Base,
|
||
IN SIZE_T Size,
|
||
IN PMMPTE PointerPte,
|
||
IN PEPROCESS Process
|
||
)
|
||
|
||
/*++
|
||
|
||
Environment:
|
||
|
||
Kernel mode. Address creation mutex held at APC_LEVEL.
|
||
|
||
--*/
|
||
|
||
{
|
||
RTL_BITMAP BitMap;
|
||
PVOID Starting4KAddress;
|
||
PVOID Ending4KAddress;
|
||
ULONGLONG ProtectionMask;
|
||
PMMPTE StartAltPte;
|
||
PMMPTE EndAltPte;
|
||
PMMPTE EndAltPte0;
|
||
PWOW64_PROCESS Wow64Process;
|
||
MMPTE TempAltPte;
|
||
MMPTE TempPte;
|
||
ULONG NewProtect;
|
||
KIRQL OldIrql;
|
||
|
||
Wow64Process = Process->Wow64Process;
|
||
Starting4KAddress = Base;
|
||
Ending4KAddress = (PCHAR)Base + Size - 1;
|
||
|
||
//
|
||
// If the addresses are not WOW64 then nothing needs to be done here.
|
||
//
|
||
|
||
if ((Starting4KAddress >= (PVOID)MM_MAX_WOW64_ADDRESS) ||
|
||
(Ending4KAddress >= (PVOID)MM_MAX_WOW64_ADDRESS)) {
|
||
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Get the entry in the table for each of these addresses.
|
||
//
|
||
|
||
StartAltPte = MiGetAltPteAddress (Starting4KAddress);
|
||
EndAltPte = MiGetAltPteAddress (Ending4KAddress);
|
||
EndAltPte0 = MiGetAltPteAddress((ULONG_PTR)PAGE_ALIGN(Ending4KAddress)+PAGE_SIZE-1);
|
||
|
||
LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
|
||
//
|
||
// And then change all of the protections.
|
||
//
|
||
|
||
while (StartAltPte <= EndAltPte) {
|
||
|
||
//
|
||
// Get the original protection information from the prototype PTEs.
|
||
//
|
||
|
||
LOCK_WS_UNSAFE (Process);
|
||
|
||
LOCK_PFN (OldIrql);
|
||
MiMakeSystemAddressValidPfnWs (PointerPte, Process);
|
||
TempPte = *PointerPte;
|
||
UNLOCK_PFN (OldIrql);
|
||
|
||
NewProtect =
|
||
MiMakeProtectionMask(MiGetPageProtection(&TempPte, Process, TRUE));
|
||
|
||
ASSERT (NewProtect != MM_INVALID_PROTECTION);
|
||
|
||
UNLOCK_WS_UNSAFE (Process);
|
||
|
||
//
|
||
// If demand-zero and copy-on-write, remove copy-on-write.
|
||
//
|
||
|
||
if ((!IS_PTE_NOT_DEMAND_ZERO(TempPte)) &&
|
||
(TempPte.u.Soft.Protection & MM_COPY_ON_WRITE_MASK)) {
|
||
NewProtect = NewProtect & ~MM_PROTECTION_COPY_MASK;
|
||
}
|
||
|
||
ProtectionMask = MiMakeProtectionAteMask (NewProtect);
|
||
ProtectionMask |= MM_ATE_COMMIT;
|
||
|
||
TempAltPte.u.Long = ProtectionMask;
|
||
TempAltPte.u.Alt.Protection = NewProtect;
|
||
|
||
if ((NewProtect & MM_PROTECTION_COPY_MASK) == 0) {
|
||
|
||
//
|
||
// If the copy-on-write is removed, make it private.
|
||
//
|
||
|
||
TempAltPte.u.Alt.Private = 1;
|
||
}
|
||
|
||
//
|
||
// Atomic PTE update.
|
||
//
|
||
|
||
MiFillMemoryPte (StartAltPte,
|
||
SPLITS_PER_PAGE * sizeof(MMPTE),
|
||
TempAltPte.u.Long);
|
||
|
||
StartAltPte += SPLITS_PER_PAGE;
|
||
|
||
PointerPte += 1;
|
||
}
|
||
|
||
//
|
||
// Fill the empty Alt PTE as NoAccess ATE at the end.
|
||
//
|
||
|
||
while (EndAltPte <= EndAltPte0) {
|
||
|
||
if (EndAltPte->u.Long == 0) {
|
||
|
||
TempAltPte.u.Long = EndAltPte->u.Long;
|
||
TempAltPte.u.Alt.NoAccess = 1;
|
||
|
||
//
|
||
// Atomic PTE size update.
|
||
//
|
||
|
||
EndAltPte->u.Long = TempAltPte.u.Long;
|
||
}
|
||
|
||
EndAltPte += 1;
|
||
}
|
||
|
||
//
|
||
// Initialize the bitmap inline for speed.
|
||
//
|
||
|
||
BitMap.SizeOfBitMap = MM_MAX_WOW64_ADDRESS >> PTI_SHIFT;
|
||
BitMap.Buffer = Wow64Process->AltPermBitmap;
|
||
|
||
RtlSetBits (&BitMap,
|
||
(ULONG) MI_VA_TO_VPN (Base),
|
||
(ULONG) (MI_VA_TO_VPN (Ending4KAddress) - MI_VA_TO_VPN (Base) + 1));
|
||
|
||
UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
}
|
||
|
||
VOID
|
||
MiReleaseFor4kPage (
|
||
IN PVOID StartVirtual,
|
||
IN PVOID EndVirtual,
|
||
IN PEPROCESS Process
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function releases a region of pages within the virtual address
|
||
space of the subject process.
|
||
|
||
Arguments:
|
||
|
||
StartVirtual - Supplies the start address of the region of pages
|
||
to be released.
|
||
|
||
EndVirtual - Supplies the end address of the region of pages to be released.
|
||
|
||
Process - Supplies a pointer to the process in which to release the
|
||
region of pages.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
Kernel mode. Address creation mutex held at APC_LEVEL.
|
||
|
||
--*/
|
||
|
||
{
|
||
RTL_BITMAP BitMap;
|
||
PMMPTE StartAltPte;
|
||
PMMPTE EndAltPte;
|
||
MMPTE TempAltPte;
|
||
ULONG_PTR VirtualAddress;
|
||
PVOID OriginalStartVa, OriginalEndVa;
|
||
ULONG i;
|
||
PWOW64_PROCESS Wow64Process;
|
||
PVOID Virtual[MM_MAXIMUM_FLUSH_COUNT];
|
||
ULONG FlushCount;
|
||
PFN_NUMBER NumberOfAltPtes;
|
||
ULONG NumberOfPtes;
|
||
|
||
ASSERT(StartVirtual <= EndVirtual);
|
||
|
||
FlushCount = 0;
|
||
OriginalStartVa = StartVirtual;
|
||
OriginalEndVa = EndVirtual;
|
||
Wow64Process = Process->Wow64Process;
|
||
|
||
StartAltPte = MiGetAltPteAddress (StartVirtual);
|
||
EndAltPte = MiGetAltPteAddress (EndVirtual);
|
||
NumberOfAltPtes = EndAltPte - StartAltPte + 1;
|
||
|
||
TempAltPte.u.Long = 0;
|
||
TempAltPte.u.Alt.NoAccess = 1;
|
||
TempAltPte.u.Alt.FillZero = 1;
|
||
|
||
StartVirtual = PAGE_ALIGN(StartVirtual);
|
||
|
||
VirtualAddress = (ULONG_PTR)StartVirtual;
|
||
|
||
NumberOfPtes = (ULONG) ADDRESS_AND_SIZE_TO_SPAN_PAGES (StartVirtual,
|
||
(ULONG_PTR)EndVirtual -
|
||
(ULONG_PTR)StartVirtual);
|
||
ASSERT (NumberOfPtes != 0);
|
||
|
||
//
|
||
// Ensure the proper native TB entries get flushed.
|
||
//
|
||
|
||
if (NumberOfPtes < MM_MAXIMUM_FLUSH_COUNT) {
|
||
|
||
for (i = 0; i < NumberOfPtes; i += 1) {
|
||
Virtual[i] = (PVOID)VirtualAddress;
|
||
VirtualAddress += PAGE_SIZE;
|
||
}
|
||
|
||
VirtualAddress = (ULONG_PTR)StartVirtual;
|
||
}
|
||
|
||
LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
|
||
MiFillMemoryPte (StartAltPte,
|
||
NumberOfAltPtes * sizeof(MMPTE),
|
||
TempAltPte.u.Long);
|
||
|
||
StartAltPte += NumberOfAltPtes;
|
||
|
||
while (VirtualAddress <= (ULONG_PTR)EndVirtual) {
|
||
|
||
StartAltPte = MiGetAltPteAddress(VirtualAddress);
|
||
TempAltPte = *StartAltPte;
|
||
|
||
i = 0;
|
||
|
||
//
|
||
// Note that this check must be made as the ATE fill above may not
|
||
// have begun on a native page boundary and this scan always does.
|
||
//
|
||
|
||
while (TempAltPte.u.Long == StartAltPte->u.Long) {
|
||
i += 1;
|
||
if (i == SPLITS_PER_PAGE) {
|
||
while (i != 0) {
|
||
StartAltPte->u.Long = 0;
|
||
StartAltPte -= 1;
|
||
i -= 1;
|
||
}
|
||
break;
|
||
}
|
||
StartAltPte += 1;
|
||
}
|
||
|
||
VirtualAddress += PAGE_SIZE;
|
||
}
|
||
|
||
MiResetAccessBitForNativePtes (StartVirtual, EndVirtual, Process);
|
||
|
||
//
|
||
// Mark the native released pages as non-split so they re-synced
|
||
// at MmX86Fault() time. NOTE: StartVirtual should be aligned on
|
||
// the native page size before executing this code.
|
||
//
|
||
|
||
if (BYTE_OFFSET (OriginalStartVa) != 0) {
|
||
|
||
if (MiArePreceding4kPagesAllocated (OriginalStartVa) != FALSE) {
|
||
|
||
StartVirtual = PAGE_ALIGN ((ULONG_PTR)StartVirtual + PAGE_SIZE);
|
||
}
|
||
}
|
||
|
||
EndVirtual = (PVOID) ((ULONG_PTR)EndVirtual | (PAGE_SIZE - 1));
|
||
|
||
if (BYTE_OFFSET (OriginalEndVa) != (PAGE_SIZE - 1)) {
|
||
|
||
if (MiAreFollowing4kPagesAllocated (OriginalEndVa) != FALSE) {
|
||
|
||
EndVirtual = (PVOID) ((ULONG_PTR)EndVirtual - PAGE_SIZE);
|
||
}
|
||
}
|
||
|
||
if (StartVirtual < EndVirtual) {
|
||
|
||
//
|
||
// Initialize the bitmap inline for speed.
|
||
//
|
||
|
||
BitMap.SizeOfBitMap = MM_MAX_WOW64_ADDRESS >> PTI_SHIFT;
|
||
BitMap.Buffer = Wow64Process->AltPermBitmap;
|
||
|
||
RtlClearBits (&BitMap,
|
||
(ULONG) MI_VA_TO_VPN (StartVirtual),
|
||
(ULONG) (MI_VA_TO_VPN (EndVirtual) - MI_VA_TO_VPN (StartVirtual) + 1));
|
||
}
|
||
|
||
if (NumberOfPtes < MM_MAXIMUM_FLUSH_COUNT) {
|
||
|
||
KeFlushMultipleTb (FlushCount,
|
||
&Virtual[0],
|
||
TRUE,
|
||
TRUE,
|
||
NULL,
|
||
ZeroPte.u.Flush);
|
||
}
|
||
else {
|
||
KeFlushEntireTb (TRUE, TRUE);
|
||
}
|
||
|
||
UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
}
|
||
|
||
VOID
|
||
MiDecommitFor4kPage (
|
||
IN PVOID StartVirtual,
|
||
IN PVOID EndVirtual,
|
||
IN PEPROCESS Process
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function decommits a region of pages within the virtual address
|
||
space of a subject process.
|
||
|
||
Arguments:
|
||
|
||
StartVirtual - Supplies the start address of the region of pages
|
||
to be decommitted.
|
||
|
||
EndVirtual - Supplies the end address of the region of the pages
|
||
to be decommitted.
|
||
|
||
Process - Supplies a pointer to the process in which to decommit a
|
||
a region of pages.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
Address space mutex held at APC_LEVEL.
|
||
|
||
--*/
|
||
|
||
{
|
||
PMMPTE StartAltPte;
|
||
PMMPTE EndAltPte;
|
||
MMPTE TempAltPte;
|
||
ULONG_PTR VirtualAddress;
|
||
PWOW64_PROCESS Wow64Process;
|
||
PVOID Virtual[MM_MAXIMUM_FLUSH_COUNT];
|
||
ULONG i;
|
||
ULONG NumberOfPtes;
|
||
|
||
Wow64Process = Process->Wow64Process;
|
||
|
||
ASSERT(StartVirtual <= EndVirtual);
|
||
|
||
StartAltPte = MiGetAltPteAddress (StartVirtual);
|
||
EndAltPte = MiGetAltPteAddress (EndVirtual);
|
||
|
||
NumberOfPtes = (ULONG) ADDRESS_AND_SIZE_TO_SPAN_PAGES (StartVirtual,
|
||
(ULONG_PTR)EndVirtual -
|
||
(ULONG_PTR)StartVirtual);
|
||
ASSERT (NumberOfPtes != 0);
|
||
|
||
//
|
||
// Ensure the proper native TB entries get flushed.
|
||
//
|
||
|
||
if (NumberOfPtes < MM_MAXIMUM_FLUSH_COUNT) {
|
||
|
||
VirtualAddress = (ULONG_PTR)StartVirtual;
|
||
|
||
for (i = 0; i < NumberOfPtes; i += 1) {
|
||
Virtual[i] = (PVOID)VirtualAddress;
|
||
VirtualAddress += PAGE_SIZE;
|
||
}
|
||
}
|
||
|
||
LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
|
||
while (StartAltPte <= EndAltPte) {
|
||
|
||
TempAltPte.u.Long = StartAltPte->u.Long;
|
||
TempAltPte.u.Alt.Commit = 0;
|
||
TempAltPte.u.Alt.Accessed = 0;
|
||
TempAltPte.u.Alt.FillZero = 1;
|
||
|
||
//
|
||
// Atomic PTE update.
|
||
//
|
||
|
||
StartAltPte->u.Long = TempAltPte.u.Long;
|
||
|
||
StartAltPte += 1;
|
||
}
|
||
|
||
//
|
||
// Flush the TB.
|
||
//
|
||
|
||
MiResetAccessBitForNativePtes (StartVirtual, EndVirtual, Process);
|
||
|
||
if (NumberOfPtes < MM_MAXIMUM_FLUSH_COUNT) {
|
||
|
||
KeFlushMultipleTb (NumberOfPtes,
|
||
&Virtual[0],
|
||
TRUE,
|
||
TRUE,
|
||
NULL,
|
||
ZeroPte.u.Flush);
|
||
}
|
||
else {
|
||
KeFlushEntireTb (TRUE, TRUE);
|
||
}
|
||
|
||
UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
}
|
||
|
||
|
||
VOID
|
||
MiDeleteFor4kPage (
|
||
IN PVOID StartVirtual,
|
||
IN PVOID EndVirtual,
|
||
IN PEPROCESS Process
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function deletes a region of pages within the virtual address
|
||
space of the subject process.
|
||
|
||
Arguments:
|
||
|
||
StartVirtual - Supplies the start address of the region of pages
|
||
to be deleted.
|
||
|
||
EndVirtual - Supplies the end address of the region of pages
|
||
to be deleted.
|
||
|
||
Process - Supplies a pointer to the process in which to delete
|
||
the region of pages.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
Kernel mode. Address creation mutex held at APC_LEVEL.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i;
|
||
ULONG NumberOfPtes;
|
||
RTL_BITMAP BitMap;
|
||
PMMPTE EndAltPte;
|
||
PMMPTE StartAltPte;
|
||
ULONG_PTR VirtualAddress;
|
||
PWOW64_PROCESS Wow64Process;
|
||
PFN_NUMBER NumberOfAltPtes;
|
||
PVOID Virtual[MM_MAXIMUM_FLUSH_COUNT];
|
||
|
||
Wow64Process = Process->Wow64Process;
|
||
|
||
ASSERT(StartVirtual <= EndVirtual);
|
||
|
||
StartAltPte = MiGetAltPteAddress (StartVirtual);
|
||
EndAltPte = MiGetAltPteAddress (EndVirtual);
|
||
|
||
NumberOfAltPtes = EndAltPte - StartAltPte + 1;
|
||
|
||
VirtualAddress = (ULONG_PTR)StartVirtual;
|
||
|
||
NumberOfPtes = (ULONG) ADDRESS_AND_SIZE_TO_SPAN_PAGES (StartVirtual,
|
||
(ULONG_PTR)EndVirtual -
|
||
(ULONG_PTR)StartVirtual);
|
||
ASSERT (NumberOfPtes != 0);
|
||
|
||
//
|
||
// Ensure the proper native TB entries get flushed.
|
||
//
|
||
|
||
if (NumberOfPtes < MM_MAXIMUM_FLUSH_COUNT) {
|
||
|
||
VirtualAddress = (ULONG_PTR)StartVirtual;
|
||
|
||
for (i = 0; i < NumberOfPtes; i += 1) {
|
||
Virtual[i] = (PVOID)VirtualAddress;
|
||
VirtualAddress += PAGE_SIZE;
|
||
}
|
||
}
|
||
|
||
LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
|
||
MiFillMemoryPte (StartAltPte,
|
||
NumberOfAltPtes * sizeof(MMPTE),
|
||
ZeroPte.u.Long);
|
||
|
||
//
|
||
// StartVirtual and EndVirtual are already aligned to the native
|
||
// PAGE_SIZE so no need to readjust them before removing the split markers.
|
||
//
|
||
// Initialize the bitmap inline for speed.
|
||
//
|
||
|
||
BitMap.SizeOfBitMap = MM_MAX_WOW64_ADDRESS >> PTI_SHIFT;
|
||
BitMap.Buffer = Wow64Process->AltPermBitmap;
|
||
|
||
RtlClearBits (&BitMap,
|
||
(ULONG) MI_VA_TO_VPN (StartVirtual),
|
||
(ULONG) (MI_VA_TO_VPN (EndVirtual) - MI_VA_TO_VPN (StartVirtual) + 1));
|
||
|
||
MiResetAccessBitForNativePtes (StartVirtual, EndVirtual, Process);
|
||
|
||
if (NumberOfPtes < MM_MAXIMUM_FLUSH_COUNT) {
|
||
|
||
KeFlushMultipleTb (NumberOfPtes,
|
||
&Virtual[0],
|
||
TRUE,
|
||
TRUE,
|
||
NULL,
|
||
ZeroPte.u.Flush);
|
||
}
|
||
else {
|
||
KeFlushEntireTb (TRUE, TRUE);
|
||
}
|
||
|
||
UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
}
|
||
|
||
LOGICAL
|
||
MiIsSplitPage (
|
||
IN PVOID Virtual
|
||
)
|
||
{
|
||
PMMPTE AltPte;
|
||
MMPTE PteContents;
|
||
ULONG i;
|
||
|
||
Virtual = PAGE_ALIGN(Virtual);
|
||
AltPte = MiGetAltPteAddress(Virtual);
|
||
PteContents = *AltPte;
|
||
|
||
for (i = 0; i < SPLITS_PER_PAGE; i += 1) {
|
||
|
||
if ((AltPte->u.Long != 0) &&
|
||
((AltPte->u.Alt.Commit == 0) ||
|
||
(AltPte->u.Alt.Accessed == 0) ||
|
||
(AltPte->u.Alt.CopyOnWrite != 0) ||
|
||
(AltPte->u.Alt.PteIndirect != 0) ||
|
||
(AltPte->u.Alt.FillZero != 0))) {
|
||
|
||
//
|
||
// If it is a NoAccess, FillZero or Guard page, CopyOnWrite,
|
||
// mark it as a split page.
|
||
//
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
if (PteContents.u.Long != AltPte->u.Long) {
|
||
|
||
//
|
||
// If the next 4kb page is different from the 1st 4k page
|
||
// the page is split.
|
||
//
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
AltPte += 1;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
LOGICAL
|
||
MiArePreceding4kPagesAllocated (
|
||
IN PVOID VirtualAddress
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function checks to see if the specified virtual address contains any
|
||
preceding 4k allocations within the native page.
|
||
|
||
Arguments:
|
||
|
||
VirtualAddress - Supplies the virtual address to check.
|
||
|
||
Return Value:
|
||
|
||
TRUE if the address has preceding 4k pages, FALSE if not.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, address creation mutex held, APCs disabled.
|
||
|
||
--*/
|
||
|
||
{
|
||
PMMPTE AltPte;
|
||
PMMPTE AltPteEnd;
|
||
|
||
ASSERT (BYTE_OFFSET (VirtualAddress) != 0);
|
||
|
||
AltPte = MiGetAltPteAddress (PAGE_ALIGN(VirtualAddress));
|
||
AltPteEnd = MiGetAltPteAddress (VirtualAddress);
|
||
|
||
//
|
||
// No need to hold the AltPte mutex as the address space mutex
|
||
// is held which prevents allocation or deletion of the AltPte entries
|
||
// inside the table.
|
||
//
|
||
|
||
while (AltPte != AltPteEnd) {
|
||
|
||
if ((AltPte->u.Long == 0) ||
|
||
((AltPte->u.Alt.NoAccess == 1) && (AltPte->u.Alt.Protection != MM_NOACCESS))) {
|
||
|
||
//
|
||
// The page's alternate PTE hasn't been allocated yet to the process
|
||
// or it's marked no access.
|
||
//
|
||
|
||
NOTHING;
|
||
}
|
||
else {
|
||
return TRUE;
|
||
}
|
||
|
||
AltPte += 1;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
LOGICAL
|
||
MiAreFollowing4kPagesAllocated (
|
||
IN PVOID VirtualAddress
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function checks to see if the specified virtual address contains any
|
||
following 4k allocations within the native page.
|
||
|
||
Arguments:
|
||
|
||
VirtualAddress - Supplies the virtual address to check.
|
||
|
||
Return Value:
|
||
|
||
TRUE if the address has following 4k pages, FALSE if not.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, address creation mutex held, APCs disabled.
|
||
|
||
--*/
|
||
|
||
{
|
||
PMMPTE AltPte;
|
||
PMMPTE AltPteEnd;
|
||
|
||
ASSERT (BYTE_OFFSET (VirtualAddress) != 0);
|
||
|
||
AltPteEnd = MiGetAltPteAddress (PAGE_ALIGN ((ULONG_PTR)VirtualAddress + PAGE_SIZE));
|
||
|
||
AltPte = MiGetAltPteAddress (VirtualAddress) + 1;
|
||
|
||
ASSERT (AltPte < AltPteEnd);
|
||
|
||
//
|
||
// No need to hold the AltPte mutex as the address space mutex
|
||
// is held which prevents allocation or deletion of the AltPte entries
|
||
// inside the table.
|
||
//
|
||
|
||
while (AltPte != AltPteEnd) {
|
||
|
||
if ((AltPte->u.Long == 0) ||
|
||
((AltPte->u.Alt.NoAccess == 1) && (AltPte->u.Alt.Protection != MM_NOACCESS))) {
|
||
|
||
//
|
||
// The page's alternate PTE hasn't been allocated yet to the process
|
||
// or it's marked no access.
|
||
//
|
||
|
||
NOTHING;
|
||
}
|
||
else {
|
||
return TRUE;
|
||
}
|
||
|
||
AltPte += 1;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
VOID
|
||
MiResetAccessBitForNativePtes (
|
||
IN PVOID StartVirtual,
|
||
IN PVOID EndVirtual,
|
||
IN PEPROCESS Process
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function resets the access bit of the native PTEs if the bitmap
|
||
indicates it is a split page.
|
||
|
||
Arguments:
|
||
|
||
StartVirtual - Supplies the start address of the region of pages
|
||
to be inspected.
|
||
|
||
EndVirtual - Supplies the end address of the region of the pages
|
||
to be inspected.
|
||
|
||
Bitmap - Supplies the pointer to the process.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
Alternate table mutex held at APC_LEVEL.
|
||
|
||
--*/
|
||
|
||
{
|
||
PMMPTE PointerPte;
|
||
PMMPTE PointerPde;
|
||
PMMPTE PointerPpe;
|
||
LOGICAL FirstTime;
|
||
ULONG Waited;
|
||
PWOW64_PROCESS Wow64Process;
|
||
|
||
Wow64Process = Process->Wow64Process;
|
||
|
||
StartVirtual = PAGE_ALIGN(StartVirtual);
|
||
|
||
PointerPte = MiGetPteAddress (StartVirtual);
|
||
|
||
FirstTime = TRUE;
|
||
|
||
LOCK_WS_UNSAFE (Process);
|
||
|
||
while (StartVirtual <= EndVirtual) {
|
||
|
||
if ((FirstTime == TRUE) || MiIsPteOnPdeBoundary (PointerPte)) {
|
||
|
||
PointerPde = MiGetPteAddress (PointerPte);
|
||
PointerPpe = MiGetPdeAddress (PointerPte);
|
||
|
||
if (MiDoesPpeExistAndMakeValid (PointerPpe,
|
||
Process,
|
||
FALSE,
|
||
&Waited) == FALSE) {
|
||
|
||
//
|
||
// This page directory parent entry is empty,
|
||
// go to the next one.
|
||
//
|
||
|
||
PointerPpe += 1;
|
||
PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
|
||
PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
|
||
StartVirtual = MiGetVirtualAddressMappedByPte (PointerPte);
|
||
continue;
|
||
}
|
||
|
||
if (MiDoesPdeExistAndMakeValid (PointerPde,
|
||
Process,
|
||
FALSE,
|
||
&Waited) == FALSE) {
|
||
|
||
|
||
//
|
||
// This page directory entry is empty,
|
||
// go to the next one.
|
||
//
|
||
|
||
PointerPde += 1;
|
||
PointerPte = MiGetVirtualAddressMappedByPte(PointerPde);
|
||
StartVirtual = MiGetVirtualAddressMappedByPte(PointerPte);
|
||
continue;
|
||
}
|
||
|
||
FirstTime = FALSE;
|
||
|
||
}
|
||
|
||
if ((MI_CHECK_BIT(Wow64Process->AltPermBitmap, MI_VA_TO_VPN(StartVirtual))) &&
|
||
((PointerPte->u.Hard.Valid != 0) && (PointerPte->u.Hard.Accessed != 0))) {
|
||
|
||
PointerPte->u.Hard.Accessed = 0;
|
||
|
||
}
|
||
|
||
PointerPte += 1;
|
||
StartVirtual = (PVOID)((ULONG_PTR)StartVirtual + PAGE_SIZE);
|
||
}
|
||
|
||
UNLOCK_WS_UNSAFE (Process);
|
||
}
|
||
|
||
VOID
|
||
MiQueryRegionFor4kPage (
|
||
IN PVOID BaseAddress,
|
||
IN PVOID EndAddress,
|
||
IN OUT PSIZE_T RegionSize,
|
||
IN OUT PULONG RegionState,
|
||
IN OUT PULONG RegionProtect,
|
||
IN PEPROCESS Process
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function checks the size of the region which has the same memory
|
||
state.
|
||
|
||
Arguments:
|
||
|
||
BaseAddress - Supplies the base address of the region of pages
|
||
to be queried.
|
||
|
||
EndAddress - Supplies the end of address of the region of pages
|
||
to be queried.
|
||
|
||
RegionSize - Supplies the original region size. Returns a region
|
||
size for 4k pages if different.
|
||
|
||
RegionState - Supplies the original region state. Returns a region
|
||
state for 4k pages if different.
|
||
|
||
RegionProtect - Supplies the original protection. Returns a protection
|
||
for 4k pages if different.
|
||
|
||
Process - Supplies a pointer to the process to be queried.
|
||
|
||
Return Value:
|
||
|
||
Returns the size of the region.
|
||
|
||
Environment:
|
||
|
||
Kernel mode. Address creation mutex held at APC_LEVEL.
|
||
|
||
--*/
|
||
|
||
{
|
||
PMMPTE AltPte;
|
||
MMPTE AltContents;
|
||
PVOID Va;
|
||
PWOW64_PROCESS Wow64Process;
|
||
|
||
//
|
||
// If above the Wow64 max address, just return.
|
||
//
|
||
|
||
if (((UINT_PTR) BaseAddress >= MM_MAX_WOW64_ADDRESS) ||
|
||
((UINT_PTR) EndAddress >= MM_MAX_WOW64_ADDRESS)) {
|
||
|
||
return;
|
||
}
|
||
|
||
AltPte = MiGetAltPteAddress (BaseAddress);
|
||
|
||
Wow64Process = Process->Wow64Process;
|
||
|
||
LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
|
||
if (MI_CHECK_BIT (Wow64Process->AltPermBitmap,
|
||
MI_VA_TO_VPN(BaseAddress)) == 0) {
|
||
|
||
UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
return;
|
||
}
|
||
|
||
AltContents.u.Long = AltPte->u.Long;
|
||
|
||
if (AltContents.u.Long == 0) {
|
||
UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
return;
|
||
}
|
||
|
||
*RegionProtect = MI_CONVERT_FROM_PTE_PROTECTION(AltContents.u.Alt.Protection);
|
||
|
||
if (AltContents.u.Alt.Commit != 0) {
|
||
|
||
*RegionState = MEM_COMMIT;
|
||
}
|
||
else {
|
||
|
||
if ((AltPte->u.Long == 0) ||
|
||
((AltPte->u.Alt.NoAccess == 1) && (AltPte->u.Alt.Protection != MM_NOACCESS))) {
|
||
*RegionState = MEM_FREE;
|
||
*RegionProtect = PAGE_NOACCESS;
|
||
} else {
|
||
*RegionState = MEM_RESERVE;
|
||
*RegionProtect = 0;
|
||
}
|
||
}
|
||
|
||
Va = BaseAddress;
|
||
|
||
while ((ULONG_PTR)Va < (ULONG_PTR)EndAddress) {
|
||
|
||
Va = (PVOID)((ULONG_PTR)Va + PAGE_4K);
|
||
AltPte += 1;
|
||
|
||
if ((AltPte->u.Alt.Protection != AltContents.u.Alt.Protection) ||
|
||
(AltPte->u.Alt.Commit != AltContents.u.Alt.Commit)) {
|
||
|
||
//
|
||
// The state for this address does not match, calculate
|
||
// size and return.
|
||
//
|
||
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
|
||
*RegionSize = (SIZE_T)((ULONG_PTR)Va - (ULONG_PTR)BaseAddress);
|
||
}
|
||
|
||
ULONG
|
||
MiQueryProtectionFor4kPage (
|
||
IN PVOID BaseAddress,
|
||
IN PEPROCESS Process
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function queries the protection for a specified 4k page.
|
||
|
||
Arguments:
|
||
|
||
BaseAddress - Supplies a base address of the 4k page.
|
||
|
||
Process - Supplies a pointer to the relevant process.
|
||
|
||
Return Value:
|
||
|
||
Returns the protection of the 4k page.
|
||
|
||
Environment:
|
||
|
||
Kernel mode. Address creation mutex held at APC_LEVEL.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG Protection;
|
||
PMMPTE PointerAltPte;
|
||
PWOW64_PROCESS Wow64Process;
|
||
|
||
Wow64Process = Process->Wow64Process;
|
||
|
||
PointerAltPte = MiGetAltPteAddress (BaseAddress);
|
||
|
||
Protection = 0;
|
||
|
||
LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
|
||
if (MI_CHECK_BIT(Wow64Process->AltPermBitmap,
|
||
MI_VA_TO_VPN(BaseAddress)) != 0) {
|
||
|
||
Protection = (ULONG)PointerAltPte->u.Alt.Protection;
|
||
}
|
||
|
||
UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
|
||
return Protection;
|
||
}
|
||
|
||
//
|
||
// Note 1 is added to the charge to account for the page table page.
|
||
//
|
||
|
||
#define MI_ALTERNATE_PAGE_TABLE_CHARGE ((((MM_MAX_WOW64_ADDRESS >> PAGE_4K_SHIFT) * sizeof (MMPTE)) >> PAGE_SHIFT) + 1)
|
||
|
||
|
||
NTSTATUS
|
||
MiInitializeAlternateTable (
|
||
IN PEPROCESS Process
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function initializes the alternate table for the specified process.
|
||
|
||
Arguments:
|
||
|
||
Process - Supplies a pointer to the process to initialize the alternate
|
||
table for.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS.
|
||
|
||
Environment:
|
||
|
||
--*/
|
||
|
||
{
|
||
PULONG AltTablePointer;
|
||
PWOW64_PROCESS Wow64Process;
|
||
|
||
//
|
||
// Charge commitment now for the alternate PTE table pages as they will
|
||
// need to be dynamically created later at fault time.
|
||
//
|
||
|
||
if (MiChargeCommitment (MI_ALTERNATE_PAGE_TABLE_CHARGE, NULL) == FALSE) {
|
||
return STATUS_COMMITMENT_LIMIT;
|
||
}
|
||
|
||
AltTablePointer = (PULONG)ExAllocatePoolWithTag (NonPagedPool,
|
||
(MM_MAX_WOW64_ADDRESS >> PTI_SHIFT)/8,
|
||
'AlmM');
|
||
|
||
if (AltTablePointer == NULL) {
|
||
MiReturnCommitment (MI_ALTERNATE_PAGE_TABLE_CHARGE);
|
||
return STATUS_NO_MEMORY;
|
||
}
|
||
|
||
RtlZeroMemory (AltTablePointer, (MM_MAX_WOW64_ADDRESS >> PTI_SHIFT)/8);
|
||
|
||
Wow64Process = Process->Wow64Process;
|
||
|
||
Wow64Process->AltPermBitmap = AltTablePointer;
|
||
|
||
ExInitializeFastMutex (&Wow64Process->AlternateTableLock);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
VOID
|
||
MiDuplicateAlternateTable (
|
||
IN PEPROCESS CurrentProcess,
|
||
IN PEPROCESS ProcessToInitialize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function duplicates the alternate table bitmap and the alternate PTEs
|
||
themselves for the specified process.
|
||
|
||
Arguments:
|
||
|
||
Process - Supplies a pointer to the process whose alternate information
|
||
should be copied.
|
||
|
||
ProcessToInitialize - Supplies a pointer to the target process who should
|
||
receive the new alternate information.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, APCs disabled, working set and address space mutex
|
||
and ForkInProgress flag held.
|
||
|
||
--*/
|
||
|
||
{
|
||
PVOID Source;
|
||
KAPC_STATE ApcState;
|
||
PMMPTE PointerPte;
|
||
PMMPTE PointerAltPte;
|
||
PMMPTE PointerPde;
|
||
ULONG_PTR Va;
|
||
ULONG i;
|
||
ULONG j;
|
||
ULONG Waited;
|
||
KIRQL OldIrql;
|
||
|
||
//
|
||
// It's not necessary to acquire the alternate table mutex since both the
|
||
// address space and ForkInProgress resources are held on entry.
|
||
//
|
||
|
||
RtlCopyMemory (ProcessToInitialize->Wow64Process->AltPermBitmap,
|
||
CurrentProcess->Wow64Process->AltPermBitmap,
|
||
(MM_MAX_WOW64_ADDRESS >> PTI_SHIFT)/8);
|
||
|
||
//
|
||
// Since the PPE for the Alternate Table is shared with hyperspace,
|
||
// we can assume it is always present without performing
|
||
// MiDoesPpeExistAndMakeValid().
|
||
//
|
||
|
||
PointerPde = MiGetPdeAddress (ALT4KB_PERMISSION_TABLE_START);
|
||
PointerPte = MiGetPteAddress (ALT4KB_PERMISSION_TABLE_START);
|
||
|
||
Va = (ULONG_PTR)ALT4KB_PERMISSION_TABLE_START;
|
||
|
||
LOCK_PFN (OldIrql);
|
||
|
||
while (Va < (ULONG_PTR) ALT4KB_PERMISSION_TABLE_END) {
|
||
|
||
while (MiDoesPdeExistAndMakeValid (PointerPde,
|
||
CurrentProcess,
|
||
TRUE,
|
||
&Waited) == FALSE) {
|
||
|
||
//
|
||
// This page directory entry is empty, go to the next one.
|
||
//
|
||
|
||
PointerPde += 1;
|
||
PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
|
||
Va = (ULONG_PTR)MiGetVirtualAddressMappedByPte (PointerPte);
|
||
|
||
if (Va > (ULONG_PTR) ALT4KB_PERMISSION_TABLE_END) {
|
||
UNLOCK_PFN (OldIrql);
|
||
return;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Duplicate any addresses that exist in the parent, bringing them
|
||
// in from disk or materializing them as necessary. Note the
|
||
// KSEG address is used for each parent address to avoid allocating
|
||
// a system PTE for this mapping as this routine cannot fail (the
|
||
// overall fork is too far along to tolerate a failure).
|
||
//
|
||
|
||
for (i = 0; i < PTE_PER_PAGE; i += 1) {
|
||
|
||
if (PointerPte->u.Long != 0) {
|
||
|
||
if (MiDoesPdeExistAndMakeValid (PointerPte,
|
||
CurrentProcess,
|
||
TRUE,
|
||
&Waited) == TRUE) {
|
||
|
||
UNLOCK_PFN (OldIrql);
|
||
|
||
ASSERT (PointerPte->u.Hard.Valid == 1);
|
||
|
||
Source = KSEG_ADDRESS (PointerPte->u.Hard.PageFrameNumber);
|
||
|
||
KeStackAttachProcess (&ProcessToInitialize->Pcb, &ApcState);
|
||
|
||
RtlCopyMemory ((PVOID)Va, Source, PAGE_SIZE);
|
||
|
||
//
|
||
// Eliminate any bits that should NOT be copied.
|
||
//
|
||
|
||
PointerAltPte = (PMMPTE) Va;
|
||
|
||
for (j = 0; j < PTE_PER_PAGE; j += 1) {
|
||
if (PointerAltPte->u.Alt.InPageInProgress == 1) {
|
||
PointerAltPte->u.Alt.InPageInProgress = 0;
|
||
}
|
||
PointerAltPte += 1;
|
||
}
|
||
|
||
KeUnstackDetachProcess (&ApcState);
|
||
|
||
LOCK_PFN (OldIrql);
|
||
}
|
||
}
|
||
|
||
Va += PAGE_SIZE;
|
||
PointerPte += 1;
|
||
}
|
||
|
||
PointerPde += 1;
|
||
PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
|
||
Va = (ULONG_PTR)MiGetVirtualAddressMappedByPte (PointerPte);
|
||
}
|
||
|
||
UNLOCK_PFN (OldIrql);
|
||
|
||
//
|
||
// Initialize the child's 32-bit PEB to be the same as the parent's.
|
||
//
|
||
|
||
ProcessToInitialize->Wow64Process->Wow64 = CurrentProcess->Wow64Process->Wow64;
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
MiDeleteAlternateTable (
|
||
IN PEPROCESS Process
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function deletes the alternate table for the specified process.
|
||
|
||
Arguments:
|
||
|
||
Process - Supplies a pointer to the process to delete the alternate
|
||
table for.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, APCs disabled, working set mutex held.
|
||
|
||
--*/
|
||
|
||
{
|
||
PMMPTE PointerPte;
|
||
PMMPTE PointerPde;
|
||
ULONG_PTR Va;
|
||
ULONG_PTR TempVa;
|
||
ULONG i;
|
||
ULONG Waited;
|
||
MMPTE_FLUSH_LIST PteFlushList;
|
||
PWOW64_PROCESS Wow64Process;
|
||
KIRQL OldIrql;
|
||
|
||
Wow64Process = Process->Wow64Process;
|
||
|
||
if (Wow64Process->AltPermBitmap == NULL) {
|
||
|
||
//
|
||
// This is only NULL (and Wow64Process non-NULL) if a memory allocation
|
||
// failed during process creation.
|
||
//
|
||
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Since the PPE for the Alternate Table is shared with hyperspace,
|
||
// we can assume it is always present without performing
|
||
// MiDoesPpeExistAndMakeValid().
|
||
//
|
||
|
||
PointerPde = MiGetPdeAddress (ALT4KB_PERMISSION_TABLE_START);
|
||
PointerPte = MiGetPteAddress (ALT4KB_PERMISSION_TABLE_START);
|
||
|
||
Va = (ULONG_PTR)ALT4KB_PERMISSION_TABLE_START;
|
||
|
||
PteFlushList.Count = 0;
|
||
|
||
LOCK_PFN (OldIrql);
|
||
|
||
while (Va < (ULONG_PTR) ALT4KB_PERMISSION_TABLE_END) {
|
||
|
||
while (MiDoesPdeExistAndMakeValid (PointerPde,
|
||
Process,
|
||
TRUE,
|
||
&Waited) == FALSE) {
|
||
|
||
//
|
||
// This page directory entry is empty, go to the next one.
|
||
//
|
||
|
||
PointerPde += 1;
|
||
PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
|
||
Va = (ULONG_PTR)MiGetVirtualAddressMappedByPte (PointerPte);
|
||
|
||
if (Va > (ULONG_PTR) ALT4KB_PERMISSION_TABLE_END) {
|
||
goto delete_end;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Delete the PTE entries mapping the Alternate Table.
|
||
//
|
||
|
||
TempVa = Va;
|
||
for (i = 0; i < PTE_PER_PAGE; i += 1) {
|
||
|
||
if (PointerPte->u.Long != 0) {
|
||
|
||
if (IS_PTE_NOT_DEMAND_ZERO (*PointerPte)) {
|
||
|
||
MiDeletePte (PointerPte,
|
||
(PVOID)TempVa,
|
||
TRUE,
|
||
Process,
|
||
NULL,
|
||
&PteFlushList);
|
||
} else {
|
||
|
||
*PointerPte = ZeroPte;
|
||
}
|
||
|
||
}
|
||
|
||
TempVa += PAGE_4K;
|
||
PointerPte += 1;
|
||
}
|
||
|
||
//
|
||
// Delete the PDE entry mapping the Alternate Table.
|
||
//
|
||
|
||
TempVa = (ULONG_PTR)MiGetVirtualAddressMappedByPte(PointerPde);
|
||
|
||
MiDeletePte (PointerPde,
|
||
(PVOID)TempVa,
|
||
TRUE,
|
||
Process,
|
||
NULL,
|
||
&PteFlushList);
|
||
|
||
MiFlushPteList (&PteFlushList, FALSE, ZeroPte);
|
||
|
||
PointerPde += 1;
|
||
PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
|
||
Va = (ULONG_PTR)MiGetVirtualAddressMappedByPte (PointerPte);
|
||
}
|
||
|
||
delete_end:
|
||
|
||
MiFlushPteList (&PteFlushList, FALSE, ZeroPte);
|
||
|
||
UNLOCK_PFN (OldIrql);
|
||
|
||
ExFreePool (Wow64Process->AltPermBitmap);
|
||
|
||
Wow64Process->AltPermBitmap = NULL;
|
||
|
||
MiReturnCommitment (MI_ALTERNATE_PAGE_TABLE_CHARGE);
|
||
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
MiFillZeroFor4kPage (
|
||
IN PVOID VirtualAddress,
|
||
IN PEPROCESS Process
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function zeroes the specified 4k page.
|
||
|
||
Arguments:
|
||
|
||
VirtualAddress - Supplies the base address of the 4k page.
|
||
|
||
Process - Supplies the relevant process.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
Alternate PTE mutex held.
|
||
|
||
--*/
|
||
|
||
{
|
||
PVOID BaseAddress;
|
||
PMMPTE PointerAltPte;
|
||
PMMPTE PointerPde;
|
||
PMMPTE PointerPpe;
|
||
PMMPTE PointerPte;
|
||
MMPTE TempAltContents;
|
||
MMPTE PteContents;
|
||
ULONG Waited;
|
||
PWOW64_PROCESS Wow64Process;
|
||
|
||
Wow64Process = Process->Wow64Process;
|
||
|
||
PointerAltPte = MiGetAltPteAddress (VirtualAddress);
|
||
|
||
PointerPte = MiGetPteAddress(VirtualAddress);
|
||
PointerPde = MiGetPdeAddress(VirtualAddress);
|
||
PointerPpe = MiGetPpeAddress(VirtualAddress);
|
||
|
||
do {
|
||
|
||
if (PointerAltPte->u.Alt.FillZero == 0) {
|
||
|
||
//
|
||
// Another thread has already completed the zero operation.
|
||
//
|
||
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Make the PPE and PDE valid as well as the
|
||
// page table for the original PTE. This guarantees
|
||
// TB forward progress for the TB indirect fault.
|
||
//
|
||
|
||
LOCK_WS_UNSAFE (Process);
|
||
|
||
if (MiDoesPpeExistAndMakeValid (PointerPpe,
|
||
Process,
|
||
FALSE,
|
||
&Waited) == FALSE) {
|
||
PteContents.u.Long = 0;
|
||
}
|
||
else if (MiDoesPdeExistAndMakeValid (PointerPde,
|
||
Process,
|
||
FALSE,
|
||
&Waited) == FALSE) {
|
||
|
||
PteContents.u.Long = 0;
|
||
}
|
||
else {
|
||
|
||
//
|
||
// Now it is safe to read PointerPte.
|
||
//
|
||
|
||
PteContents = *PointerPte;
|
||
}
|
||
|
||
//
|
||
// The alternate PTE may have been trimmed during the period prior to
|
||
// the working set mutex being acquired or when it was released prior
|
||
// to being reacquired.
|
||
//
|
||
|
||
if (MmIsAddressValid (PointerAltPte) == TRUE) {
|
||
break;
|
||
}
|
||
|
||
UNLOCK_WS_UNSAFE (Process);
|
||
|
||
} while (TRUE);
|
||
|
||
TempAltContents.u.Long = PointerAltPte->u.Long;
|
||
|
||
if (PteContents.u.Hard.Valid != 0) {
|
||
|
||
BaseAddress = KSEG_ADDRESS(PteContents.u.Hard.PageFrameNumber);
|
||
|
||
BaseAddress =
|
||
(PVOID)((ULONG_PTR)BaseAddress +
|
||
((ULONG_PTR)PAGE_4K_ALIGN(VirtualAddress) & (PAGE_SIZE-1)));
|
||
|
||
RtlZeroMemory(BaseAddress, PAGE_4K);
|
||
|
||
UNLOCK_WS_UNSAFE (Process);
|
||
|
||
TempAltContents.u.Alt.FillZero = 0;
|
||
TempAltContents.u.Alt.Accessed = 1;
|
||
}
|
||
else {
|
||
|
||
UNLOCK_WS_UNSAFE (Process);
|
||
|
||
TempAltContents.u.Alt.Accessed = 0;
|
||
}
|
||
|
||
PointerAltPte->u.Long = TempAltContents.u.Long;
|
||
}
|
||
|
||
VOID
|
||
MiRemoveAliasedVadsApc (
|
||
IN PKAPC Apc,
|
||
OUT PKNORMAL_ROUTINE *NormalRoutine,
|
||
IN OUT PVOID NormalContext,
|
||
IN OUT PVOID *SystemArgument1,
|
||
IN OUT PVOID *SystemArgument2
|
||
)
|
||
{
|
||
ULONG i;
|
||
PALIAS_VAD_INFO2 AliasBase;
|
||
PEPROCESS Process;
|
||
PALIAS_VAD_INFO AliasInformation;
|
||
|
||
UNREFERENCED_PARAMETER (Apc);
|
||
UNREFERENCED_PARAMETER (NormalContext);
|
||
UNREFERENCED_PARAMETER (SystemArgument2);
|
||
|
||
Process = PsGetCurrentProcess ();
|
||
|
||
AliasInformation = (PALIAS_VAD_INFO) *SystemArgument1;
|
||
AliasBase = (PALIAS_VAD_INFO2)(AliasInformation + 1);
|
||
|
||
LOCK_ADDRESS_SPACE (Process);
|
||
|
||
for (i = 0; i < AliasInformation->NumberOfEntries; i += 1) {
|
||
|
||
ASSERT (AliasBase->BaseAddress < _2gb);
|
||
|
||
MiUnsecureVirtualMemory (AliasBase->SecureHandle, TRUE);
|
||
|
||
MiUnmapViewOfSection (Process,
|
||
(PVOID) (ULONG_PTR)AliasBase->BaseAddress,
|
||
TRUE);
|
||
|
||
AliasBase += 1;
|
||
}
|
||
|
||
UNLOCK_ADDRESS_SPACE (Process);
|
||
|
||
ExFreePool (AliasInformation);
|
||
|
||
//
|
||
// Clear the normal routine so this routine doesn't get called again
|
||
// for the same request.
|
||
//
|
||
|
||
*NormalRoutine = NULL;
|
||
}
|
||
|
||
VOID
|
||
MiRemoveAliasedVads (
|
||
IN PEPROCESS Process,
|
||
IN PMMVAD Vad
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function removes all aliased VADs spawned earlier from the
|
||
argument VAD.
|
||
|
||
Arguments:
|
||
|
||
Process - Supplies the EPROCESS pointer to the current process.
|
||
|
||
Vad - Supplies a pointer to the VAD describing the range being removed.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, address creation and working set mutexes held, APCs disabled.
|
||
|
||
--*/
|
||
|
||
{
|
||
PALIAS_VAD_INFO AliasInformation;
|
||
|
||
ASSERT (Process->Wow64Process != NULL);
|
||
|
||
AliasInformation = ((PMMVAD_LONG)Vad)->AliasInformation;
|
||
|
||
ASSERT (AliasInformation != NULL);
|
||
|
||
if ((Process->Flags & PS_PROCESS_FLAGS_VM_DELETED) == 0) {
|
||
|
||
//
|
||
// This process is still alive so queue an APC to delete each aliased
|
||
// VAD. This is because the deletion must also get rid of page table
|
||
// commitment which requires that it search (and modify) VAD trees,
|
||
// etc - but the address space mutex is already held and the caller
|
||
// is not generally prepared for all this to change at this point.
|
||
//
|
||
|
||
KeInitializeApc (&AliasInformation->Apc,
|
||
&PsGetCurrentThread()->Tcb,
|
||
OriginalApcEnvironment,
|
||
(PKKERNEL_ROUTINE) MiRemoveAliasedVadsApc,
|
||
NULL,
|
||
(PKNORMAL_ROUTINE) MiRemoveAliasedVadsApc,
|
||
KernelMode,
|
||
(PVOID) AliasInformation);
|
||
|
||
KeInsertQueueApc (&AliasInformation->Apc, AliasInformation, NULL, 0);
|
||
}
|
||
else {
|
||
|
||
//
|
||
// This process is exiting so all the VADs are being rundown anyway.
|
||
// Just free the pool and let normal rundown handle the aliases.
|
||
//
|
||
|
||
ExFreePool (AliasInformation);
|
||
}
|
||
}
|
||
|
||
PVOID
|
||
MiDuplicateAliasVadList (
|
||
IN PMMVAD Vad
|
||
)
|
||
{
|
||
SIZE_T AliasInfoSize;
|
||
PALIAS_VAD_INFO AliasInfo;
|
||
PALIAS_VAD_INFO NewAliasInfo;
|
||
|
||
AliasInfo = ((PMMVAD_LONG)Vad)->AliasInformation;
|
||
|
||
ASSERT (AliasInfo != NULL);
|
||
|
||
AliasInfoSize = sizeof (ALIAS_VAD_INFO) + AliasInfo->MaximumEntries * sizeof (ALIAS_VAD_INFO2);
|
||
|
||
NewAliasInfo = ExAllocatePoolWithTag (NonPagedPool,
|
||
AliasInfoSize,
|
||
'AdaV');
|
||
|
||
if (NewAliasInfo != NULL) {
|
||
RtlCopyMemory (NewAliasInfo, AliasInfo, AliasInfoSize);
|
||
}
|
||
|
||
return NewAliasInfo;
|
||
}
|
||
|
||
#define ALIAS_VAD_INCREMENT 4
|
||
|
||
NTSTATUS
|
||
MiSetCopyPagesFor4kPage (
|
||
IN PEPROCESS Process,
|
||
IN PMMVAD Vad,
|
||
IN OUT PVOID StartingAddress,
|
||
IN OUT PVOID EndingAddress,
|
||
IN ULONG NewProtection,
|
||
OUT PMMVAD *CallerNewVad
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function creates another map for the existing mapped view space
|
||
and gives it copy-on-write protection. This is called when
|
||
SetProtectionOnSection() tries to change the protection from
|
||
non-copy-on-write to copy-on-write. Since a large native page cannot be
|
||
split to shared and copy-on-write 4kb pages, references to the
|
||
copy-on-write page(s) need to be fixed to reference the
|
||
new mapped view space and this is done through the smart TB handler
|
||
and the alternate page table entries.
|
||
|
||
Arguments:
|
||
|
||
Process - Supplies the EPROCESS pointer to the current process.
|
||
|
||
Vad - Supplies a pointer to the VAD describing the range to protect.
|
||
|
||
StartingAddress - Supplies a pointer to the starting address to protect.
|
||
|
||
EndingAddress - Supplies a pointer to the ending address to the protect.
|
||
|
||
NewProtect - Supplies the new protection to set.
|
||
|
||
CallerNewVad - Returns the new VAD the caller should use for this range.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, address creation mutex held, APCs disabled.
|
||
|
||
--*/
|
||
|
||
{
|
||
PALIAS_VAD_INFO2 AliasBase;
|
||
HANDLE Handle;
|
||
PMMVAD_LONG NewVad;
|
||
SIZE_T AliasInfoSize;
|
||
PALIAS_VAD_INFO AliasInfo;
|
||
PALIAS_VAD_INFO NewAliasInfo;
|
||
LARGE_INTEGER SectionOffset;
|
||
SIZE_T CapturedViewSize;
|
||
PVOID CapturedBase;
|
||
PVOID Va;
|
||
PVOID VaEnd;
|
||
PVOID Alias;
|
||
PMMPTE PointerPte;
|
||
PMMPTE AltPte;
|
||
MMPTE AltPteContents;
|
||
LOGICAL AliasReferenced;
|
||
SECTION Section;
|
||
PCONTROL_AREA ControlArea;
|
||
NTSTATUS status;
|
||
PWOW64_PROCESS Wow64Process;
|
||
ULONGLONG ProtectionMask;
|
||
ULONGLONG ProtectionMaskNotCopy;
|
||
ULONG NewProtectNotCopy;
|
||
|
||
AliasReferenced = FALSE;
|
||
StartingAddress = PAGE_ALIGN(StartingAddress);
|
||
EndingAddress = (PVOID)((ULONG_PTR)PAGE_ALIGN(EndingAddress) + PAGE_SIZE - 1);
|
||
|
||
SectionOffset.QuadPart = (ULONG_PTR)MI_64K_ALIGN((ULONG_PTR)StartingAddress -
|
||
(ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT));
|
||
|
||
CapturedBase = NULL;
|
||
|
||
Va = MI_VPN_TO_VA (Vad->StartingVpn);
|
||
VaEnd = MI_VPN_TO_VA_ENDING (Vad->EndingVpn);
|
||
|
||
CapturedViewSize = (ULONG_PTR)VaEnd - (ULONG_PTR)Va + 1L;
|
||
|
||
ControlArea = Vad->ControlArea;
|
||
|
||
RtlZeroMemory ((PVOID)&Section, sizeof(Section));
|
||
|
||
status = MiMapViewOfDataSection (ControlArea,
|
||
Process,
|
||
&CapturedBase,
|
||
&SectionOffset,
|
||
&CapturedViewSize,
|
||
&Section,
|
||
ViewShare,
|
||
(ULONG)Vad->u.VadFlags.Protection,
|
||
0,
|
||
0,
|
||
0);
|
||
|
||
if (!NT_SUCCESS (status)) {
|
||
return status;
|
||
}
|
||
|
||
Handle = MiSecureVirtualMemory (CapturedBase,
|
||
CapturedViewSize,
|
||
PAGE_READONLY,
|
||
TRUE);
|
||
|
||
if (Handle == NULL) {
|
||
MiUnmapViewOfSection (Process, CapturedBase, FALSE);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// If the original VAD is a short or regular VAD, it needs to be
|
||
// reallocated as a large VAD. Note that a short VAD that was
|
||
// previously converted to a long VAD here will still be marked
|
||
// as private memory, thus to handle this case the NoChange bit
|
||
// must also be tested.
|
||
//
|
||
|
||
if (((Vad->u.VadFlags.PrivateMemory) && (Vad->u.VadFlags.NoChange == 0))
|
||
||
|
||
(Vad->u2.VadFlags2.LongVad == 0)) {
|
||
|
||
if (Vad->u.VadFlags.PrivateMemory == 0) {
|
||
ASSERT (Vad->u2.VadFlags2.OneSecured == 0);
|
||
ASSERT (Vad->u2.VadFlags2.MultipleSecured == 0);
|
||
}
|
||
|
||
AliasInfoSize = sizeof (ALIAS_VAD_INFO) + ALIAS_VAD_INCREMENT * sizeof (ALIAS_VAD_INFO2);
|
||
|
||
AliasInfo = ExAllocatePoolWithTag (NonPagedPool,
|
||
AliasInfoSize,
|
||
'AdaV');
|
||
|
||
if (AliasInfo == NULL) {
|
||
MiUnsecureVirtualMemory (Handle, TRUE);
|
||
MiUnmapViewOfSection (Process, CapturedBase, TRUE);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
AliasInfo->NumberOfEntries = 0;
|
||
AliasInfo->MaximumEntries = ALIAS_VAD_INCREMENT;
|
||
|
||
NewVad = ExAllocatePoolWithTag (NonPagedPool,
|
||
sizeof(MMVAD_LONG),
|
||
'ldaV');
|
||
|
||
if (NewVad == NULL) {
|
||
ExFreePool (AliasInfo);
|
||
MiUnsecureVirtualMemory (Handle, TRUE);
|
||
MiUnmapViewOfSection (Process, CapturedBase, TRUE);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlZeroMemory (NewVad, sizeof(MMVAD_LONG));
|
||
|
||
if (Vad->u.VadFlags.PrivateMemory) {
|
||
RtlCopyMemory (NewVad, Vad, sizeof(MMVAD_SHORT));
|
||
}
|
||
else {
|
||
RtlCopyMemory (NewVad, Vad, sizeof(MMVAD));
|
||
}
|
||
|
||
NewVad->u2.VadFlags2.LongVad = 1;
|
||
NewVad->AliasInformation = AliasInfo;
|
||
|
||
//
|
||
// Replace the current VAD with this expanded VAD.
|
||
//
|
||
|
||
LOCK_WS_UNSAFE (Process);
|
||
|
||
if (Vad->Parent) {
|
||
if (Vad->Parent->RightChild == Vad) {
|
||
Vad->Parent->RightChild = (PMMVAD) NewVad;
|
||
}
|
||
else {
|
||
ASSERT (Vad->Parent->LeftChild == Vad);
|
||
Vad->Parent->LeftChild = (PMMVAD) NewVad;
|
||
}
|
||
}
|
||
else {
|
||
Process->VadRoot = NewVad;
|
||
}
|
||
if (Vad->LeftChild) {
|
||
Vad->LeftChild->Parent = (PMMVAD) NewVad;
|
||
}
|
||
if (Vad->RightChild) {
|
||
Vad->RightChild->Parent = (PMMVAD) NewVad;
|
||
}
|
||
if (Process->VadHint == Vad) {
|
||
Process->VadHint = (PMMVAD) NewVad;
|
||
}
|
||
if (Process->VadFreeHint == Vad) {
|
||
Process->VadFreeHint = (PMMVAD) NewVad;
|
||
}
|
||
|
||
if ((Vad->u.VadFlags.PhysicalMapping == 1) ||
|
||
(Vad->u.VadFlags.WriteWatch == 1)) {
|
||
|
||
MiPhysicalViewAdjuster (Process, Vad, (PMMVAD) NewVad);
|
||
}
|
||
|
||
UNLOCK_WS_UNSAFE (Process);
|
||
|
||
ExFreePool (Vad);
|
||
|
||
Vad = (PMMVAD) NewVad;
|
||
}
|
||
else {
|
||
AliasInfo = (PALIAS_VAD_INFO) ((PMMVAD_LONG)Vad)->AliasInformation;
|
||
|
||
if (AliasInfo == NULL) {
|
||
AliasInfoSize = sizeof (ALIAS_VAD_INFO) + ALIAS_VAD_INCREMENT * sizeof (ALIAS_VAD_INFO2);
|
||
}
|
||
else if (AliasInfo->NumberOfEntries >= AliasInfo->MaximumEntries) {
|
||
|
||
AliasInfoSize = sizeof (ALIAS_VAD_INFO) + (AliasInfo->MaximumEntries + ALIAS_VAD_INCREMENT) * sizeof (ALIAS_VAD_INFO2);
|
||
}
|
||
else {
|
||
AliasInfoSize = 0;
|
||
}
|
||
|
||
if (AliasInfoSize != 0) {
|
||
NewAliasInfo = ExAllocatePoolWithTag (NonPagedPool,
|
||
AliasInfoSize,
|
||
'AdaV');
|
||
|
||
if (NewAliasInfo == NULL) {
|
||
MiUnsecureVirtualMemory (Handle, TRUE);
|
||
MiUnmapViewOfSection (Process, CapturedBase, TRUE);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
if (AliasInfo != NULL) {
|
||
RtlCopyMemory (NewAliasInfo, AliasInfo, AliasInfoSize - ALIAS_VAD_INCREMENT * sizeof (ALIAS_VAD_INFO2));
|
||
NewAliasInfo->MaximumEntries += ALIAS_VAD_INCREMENT;
|
||
ExFreePool (AliasInfo);
|
||
}
|
||
else {
|
||
NewAliasInfo->NumberOfEntries = 0;
|
||
NewAliasInfo->MaximumEntries = ALIAS_VAD_INCREMENT;
|
||
}
|
||
|
||
AliasInfo = NewAliasInfo;
|
||
}
|
||
}
|
||
|
||
*CallerNewVad = Vad;
|
||
|
||
Va = StartingAddress;
|
||
VaEnd = EndingAddress;
|
||
Alias = (PVOID)((ULONG_PTR)CapturedBase + ((ULONG_PTR)StartingAddress & (X64K - 1)));
|
||
|
||
ProtectionMask = MiMakeProtectionAteMask (NewProtection);
|
||
|
||
NewProtectNotCopy = NewProtection & ~MM_PROTECTION_COPY_MASK;
|
||
ProtectionMaskNotCopy = MiMakeProtectionAteMask (NewProtectNotCopy);
|
||
|
||
Wow64Process = Process->Wow64Process;
|
||
AltPte = MiGetAltPteAddress (Va);
|
||
|
||
LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
|
||
while (Va <= VaEnd) {
|
||
|
||
PointerPte = MiGetPteAddress (Alias);
|
||
|
||
AltPteContents.u.Long = AltPte->u.Long;
|
||
|
||
//
|
||
// If this address is NOT copy-on-write, AND it is not already
|
||
// redirected through an indirect entry, then redirect it now to
|
||
// the alias VAD which points at the original section.
|
||
//
|
||
|
||
if ((AltPteContents.u.Alt.CopyOnWrite == 0) &&
|
||
(AltPteContents.u.Alt.PteIndirect == 0)) {
|
||
|
||
AltPteContents.u.Alt.PteOffset = (ULONG_PTR)PointerPte - PTE_UBASE;
|
||
AltPteContents.u.Alt.PteIndirect = 1;
|
||
|
||
AltPte->u.Long = AltPteContents.u.Long;
|
||
|
||
AliasReferenced = TRUE;
|
||
}
|
||
|
||
Va = (PVOID)((ULONG_PTR)Va + PAGE_4K);
|
||
Alias = (PVOID)((ULONG_PTR)Alias + PAGE_4K);
|
||
AltPte += 1;
|
||
}
|
||
|
||
UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
|
||
ASSERT (AliasInfo->NumberOfEntries < AliasInfo->MaximumEntries);
|
||
|
||
if (AliasReferenced == TRUE) {
|
||
|
||
//
|
||
// The alias view of the shared section was referenced so chain it so
|
||
// the alias view can be :
|
||
//
|
||
// a) easily duplicated if the process subsequently forks.
|
||
//
|
||
// AND
|
||
//
|
||
// b) deleted when/if the original VAD is deleted later.
|
||
//
|
||
|
||
AliasBase = (PALIAS_VAD_INFO2)(AliasInfo + 1);
|
||
AliasBase += AliasInfo->NumberOfEntries;
|
||
ASSERT (CapturedBase < (PVOID)(ULONG_PTR)_2gb);
|
||
AliasBase->BaseAddress = (ULONG)(ULONG_PTR)CapturedBase;
|
||
AliasBase->SecureHandle = Handle;
|
||
AliasInfo->NumberOfEntries += 1;
|
||
}
|
||
else {
|
||
|
||
//
|
||
// The alias view of the shared section wasn't referenced, delete it.
|
||
//
|
||
|
||
MiUnsecureVirtualMemory (Handle, TRUE);
|
||
MiUnmapViewOfSection (Process, CapturedBase, TRUE);
|
||
}
|
||
|
||
PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_WOW64_SPLIT_PAGES);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
VOID
|
||
MiLockFor4kPage (
|
||
IN PVOID CapturedBase,
|
||
IN SIZE_T CapturedRegionSize,
|
||
IN PEPROCESS Process
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function adds the page locked attribute to the alternate table entries.
|
||
|
||
Arguments:
|
||
|
||
CapturedBase - Supplies the base address to be locked.
|
||
|
||
CapturedRegionSize - Supplies the size of the region to be locked.
|
||
|
||
Process - Supplies a pointer to the process object.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, address creation mutex held.
|
||
|
||
--*/
|
||
{
|
||
PWOW64_PROCESS Wow64Process;
|
||
PVOID EndingAddress;
|
||
PMMPTE StartAltPte;
|
||
PMMPTE EndAltPte;
|
||
|
||
Wow64Process = Process->Wow64Process;
|
||
|
||
EndingAddress = (PVOID)((ULONG_PTR)CapturedBase + CapturedRegionSize - 1);
|
||
|
||
StartAltPte = MiGetAltPteAddress(CapturedBase);
|
||
EndAltPte = MiGetAltPteAddress(EndingAddress);
|
||
|
||
LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
|
||
while (StartAltPte <= EndAltPte) {
|
||
StartAltPte->u.Alt.Lock = 1;
|
||
StartAltPte += 1;
|
||
}
|
||
|
||
UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
}
|
||
|
||
NTSTATUS
|
||
MiUnlockFor4kPage (
|
||
IN PVOID CapturedBase,
|
||
IN SIZE_T CapturedRegionSize,
|
||
IN PEPROCESS Process
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function removes the page locked attribute from the
|
||
alternate table entries.
|
||
|
||
Arguments:
|
||
|
||
CapturedBase - Supplies the base address to be unlocked.
|
||
|
||
CapturedRegionSize - Supplies the size of the region to be unlocked.
|
||
|
||
Process - Supplies a pointer to the process object.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, address creation and working set mutexes held.
|
||
|
||
Note this routine releases and reacquires the working set mutex !!!
|
||
|
||
--*/
|
||
|
||
{
|
||
PMMPTE StartAltPte;
|
||
PMMPTE EndAltPte;
|
||
PWOW64_PROCESS Wow64Process;
|
||
PVOID EndingAddress;
|
||
NTSTATUS Status;
|
||
|
||
UNLOCK_WS_UNSAFE (Process);
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
Wow64Process = Process->Wow64Process;
|
||
|
||
EndingAddress = (PVOID)((ULONG_PTR)CapturedBase + CapturedRegionSize - 1);
|
||
|
||
StartAltPte = MiGetAltPteAddress (CapturedBase);
|
||
EndAltPte = MiGetAltPteAddress (EndingAddress);
|
||
|
||
LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
|
||
while (StartAltPte <= EndAltPte) {
|
||
|
||
if (StartAltPte->u.Alt.Lock == 0) {
|
||
Status = STATUS_NOT_LOCKED;
|
||
goto StatusReturn;
|
||
|
||
}
|
||
|
||
StartAltPte += 1;
|
||
}
|
||
|
||
StartAltPte = MiGetAltPteAddress (CapturedBase);
|
||
|
||
while (StartAltPte <= EndAltPte) {
|
||
StartAltPte->u.Alt.Lock = 0;
|
||
StartAltPte += 1;
|
||
}
|
||
|
||
StatusReturn:
|
||
|
||
UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
|
||
LOCK_WS_UNSAFE (Process);
|
||
|
||
return Status;
|
||
}
|
||
|
||
LOGICAL
|
||
MiShouldBeUnlockedFor4kPage (
|
||
IN PVOID VirtualAddress,
|
||
IN PEPROCESS Process
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function examines whether the pape should be unlocked.
|
||
|
||
Arguments:
|
||
|
||
VirtualAddress - Supplies the virtual address to be examined.
|
||
|
||
Process - Supplies a pointer to the process object.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, address creation and working set mutexes held.
|
||
|
||
Note this routine releases and reacquires the working set mutex !!!
|
||
|
||
--*/
|
||
|
||
{
|
||
PMMPTE StartAltPte;
|
||
PMMPTE EndAltPte;
|
||
PWOW64_PROCESS Wow64Process;
|
||
PVOID VirtualAligned;
|
||
PVOID EndingAddress;
|
||
LOGICAL PageUnlocked;
|
||
|
||
UNLOCK_WS_UNSAFE (Process);
|
||
|
||
PageUnlocked = TRUE;
|
||
Wow64Process = Process->Wow64Process;
|
||
|
||
VirtualAligned = PAGE_ALIGN(VirtualAddress);
|
||
EndingAddress = (PVOID)((ULONG_PTR)VirtualAligned + PAGE_SIZE - 1);
|
||
|
||
StartAltPte = MiGetAltPteAddress (VirtualAligned);
|
||
EndAltPte = MiGetAltPteAddress (EndingAddress);
|
||
|
||
LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
|
||
while (StartAltPte <= EndAltPte) {
|
||
|
||
if (StartAltPte->u.Alt.Lock != 0) {
|
||
PageUnlocked = FALSE;
|
||
}
|
||
|
||
StartAltPte += 1;
|
||
}
|
||
|
||
UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
||
|
||
LOCK_WS_UNSAFE (Process);
|
||
|
||
return PageUnlocked;
|
||
}
|
||
|
||
VOID
|
||
MiCopyOnWriteFor4kPage (
|
||
IN PVOID VirtualAddress
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function changes the protection of the alternate pages for a copy on
|
||
write native page.
|
||
|
||
Arguments:
|
||
|
||
VirtualAddress - Supplies the virtual address which caused the
|
||
copy-on-write fault.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, alternate table mutex held.
|
||
|
||
--*/
|
||
{
|
||
PMMPTE PointerAltPte;
|
||
MMPTE TempAltPte;
|
||
ULONG i;
|
||
|
||
PointerAltPte = MiGetAltPteAddress (PAGE_ALIGN(VirtualAddress));
|
||
|
||
for (i = 0; i < SPLITS_PER_PAGE; i += 1) {
|
||
|
||
TempAltPte.u.Long = PointerAltPte->u.Long;
|
||
|
||
if ((TempAltPte.u.Alt.Commit != 0) &&
|
||
(TempAltPte.u.Alt.CopyOnWrite != 0)) {
|
||
|
||
TempAltPte.u.Alt.CopyOnWrite = 0;
|
||
TempAltPte.u.Alt.Private = 1;
|
||
TempAltPte.u.Hard.Write = 1;
|
||
|
||
TempAltPte.u.Alt.Protection =
|
||
MI_MAKE_PROTECT_NOT_WRITE_COPY(PointerAltPte->u.Alt.Protection);
|
||
|
||
PointerAltPte->u.Long = TempAltPte.u.Long;
|
||
}
|
||
|
||
//
|
||
// Atomically update the PTE.
|
||
//
|
||
|
||
PointerAltPte += 1;
|
||
}
|
||
}
|
||
|
||
ULONG
|
||
MiMakeProtectForNativePage (
|
||
IN PVOID VirtualAddress,
|
||
IN ULONG NewProtect,
|
||
IN PEPROCESS Process
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function makes a page protection mask for native pages.
|
||
|
||
Arguments:
|
||
|
||
VirtualAddress - Supplies the virtual address for the protection mask.
|
||
|
||
NewProtect - Supplies the original protection.
|
||
|
||
Process - Supplies a pointer to the process object.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
Kernel mode.
|
||
|
||
--*/
|
||
|
||
{
|
||
PWOW64_PROCESS Wow64Process;
|
||
|
||
Wow64Process = Process->Wow64Process;
|
||
|
||
if (MI_CHECK_BIT(Wow64Process->AltPermBitmap,
|
||
MI_VA_TO_VPN(VirtualAddress)) != 0) {
|
||
|
||
if (NewProtect & PAGE_NOACCESS) {
|
||
NewProtect &= ~PAGE_NOACCESS;
|
||
NewProtect |= PAGE_EXECUTE_READWRITE;
|
||
}
|
||
|
||
if (NewProtect & PAGE_READONLY) {
|
||
NewProtect &= ~PAGE_READONLY;
|
||
NewProtect |= PAGE_EXECUTE_READWRITE;
|
||
}
|
||
|
||
if (NewProtect & PAGE_EXECUTE) {
|
||
NewProtect &= ~PAGE_EXECUTE;
|
||
NewProtect |= PAGE_EXECUTE_READWRITE;
|
||
}
|
||
|
||
if (NewProtect & PAGE_EXECUTE_READ) {
|
||
NewProtect &= ~PAGE_EXECUTE_READ;
|
||
NewProtect |= PAGE_EXECUTE_READWRITE;
|
||
}
|
||
|
||
//
|
||
// Remove PAGE_GUARD as it is emulated by the Alternate Table.
|
||
//
|
||
|
||
if (NewProtect & PAGE_GUARD) {
|
||
NewProtect &= ~PAGE_GUARD;
|
||
}
|
||
}
|
||
|
||
return NewProtect;
|
||
}
|
||
|
||
VOID
|
||
MiCheckVirtualAddressFor4kPage (
|
||
IN PVOID VirtualAddress,
|
||
IN PEPROCESS Process
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function checks the virtual address to see if it is a 4K page.
|
||
|
||
Arguments:
|
||
|
||
VirtualAddress - Supplies the virtual address to check.
|
||
|
||
Process - Supplies a pointer to the process object.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, alternate table mutex held.
|
||
|
||
--*/
|
||
|
||
{
|
||
PMMPTE ProtoPte;
|
||
PMMPTE PointerAltPte;
|
||
PMMPTE PointerPde;
|
||
MMPTE AltPteContents;
|
||
ULONG OriginalProtection;
|
||
ULONGLONG ProtectionMaskOriginal;
|
||
PWOW64_PROCESS Wow64Process;
|
||
KIRQL OldIrql;
|
||
PMMPFN Pfn1;
|
||
ULONG i;
|
||
ULONG_PTR StartVpn;
|
||
|
||
Wow64Process = Process->Wow64Process;
|
||
|
||
LOCK_WS_UNSAFE (Process);
|
||
|
||
ProtoPte = MiCheckVirtualAddress (VirtualAddress, &OriginalProtection);
|
||
|
||
if (OriginalProtection == MM_UNKNOWN_PROTECTION) {
|
||
|
||
if (!MI_IS_PHYSICAL_ADDRESS(ProtoPte)) {
|
||
PointerPde = MiGetPteAddress (ProtoPte);
|
||
LOCK_PFN (OldIrql);
|
||
if (PointerPde->u.Hard.Valid == 0) {
|
||
MiMakeSystemAddressValidPfn (ProtoPte);
|
||
}
|
||
Pfn1 = MI_PFN_ELEMENT (PointerPde->u.Hard.PageFrameNumber);
|
||
MI_ADD_LOCKED_PAGE_CHARGE(Pfn1, 32);
|
||
Pfn1->u3.e2.ReferenceCount += 1;
|
||
ASSERT (Pfn1->u3.e2.ReferenceCount > 1);
|
||
UNLOCK_PFN (OldIrql);
|
||
}
|
||
else {
|
||
Pfn1 = NULL;
|
||
}
|
||
|
||
OriginalProtection =
|
||
MiMakeProtectionMask(MiGetPageProtection(ProtoPte, Process, FALSE));
|
||
|
||
//
|
||
// Unlock the page containing the prototype PTEs.
|
||
//
|
||
|
||
if (Pfn1 != NULL) {
|
||
ASSERT (!MI_IS_PHYSICAL_ADDRESS(ProtoPte));
|
||
LOCK_PFN (OldIrql);
|
||
ASSERT (Pfn1->u3.e2.ReferenceCount > 1);
|
||
MI_REMOVE_LOCKED_PAGE_CHARGE(Pfn1, 33);
|
||
Pfn1->u3.e2.ReferenceCount -= 1;
|
||
UNLOCK_PFN (OldIrql);
|
||
}
|
||
|
||
if (OriginalProtection == MM_INVALID_PROTECTION) {
|
||
UNLOCK_WS_UNSAFE (Process);
|
||
return;
|
||
}
|
||
}
|
||
|
||
UNLOCK_WS_UNSAFE (Process);
|
||
|
||
ProtectionMaskOriginal = MiMakeProtectionAteMask (OriginalProtection);
|
||
ProtectionMaskOriginal |= MM_ATE_COMMIT;
|
||
|
||
PointerAltPte = MiGetAltPteAddress (PAGE_ALIGN(VirtualAddress));
|
||
|
||
AltPteContents.u.Long = ProtectionMaskOriginal;
|
||
AltPteContents.u.Alt.Protection = OriginalProtection;
|
||
|
||
for (i = 0; i < SPLITS_PER_PAGE; i += 1) {
|
||
|
||
//
|
||
// Update the alternate PTEs.
|
||
//
|
||
|
||
PointerAltPte->u.Long = AltPteContents.u.Long;
|
||
PointerAltPte += 1;
|
||
}
|
||
|
||
//
|
||
// Update the bitmap.
|
||
//
|
||
|
||
StartVpn = MI_VA_TO_VPN (VirtualAddress);
|
||
|
||
MI_SET_BIT (Wow64Process->AltPermBitmap, StartVpn);
|
||
}
|
||
|
||
LOGICAL
|
||
MiIsNativeGuardPage (
|
||
IN PVOID VirtualAddress
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function checks to see if any of the AltPtes has the Guard bit set.
|
||
|
||
Arguments:
|
||
|
||
VirtualAddress - Supplies the virtual address to check.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, alternate table mutex held.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i;
|
||
PMMPTE PointerAltPte;
|
||
|
||
PointerAltPte = MiGetAltPteAddress (PAGE_ALIGN(VirtualAddress));
|
||
|
||
for (i = 0; i < SPLITS_PER_PAGE; i += 1) {
|
||
|
||
if ((PointerAltPte->u.Alt.Protection & MM_GUARD_PAGE) != 0) {
|
||
return TRUE;
|
||
}
|
||
|
||
PointerAltPte += 1;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
VOID
|
||
MiSetNativePteProtection (
|
||
PVOID VirtualAddress,
|
||
ULONGLONG NewPteProtection,
|
||
LOGICAL PageIsSplit,
|
||
PEPROCESS CurrentProcess
|
||
)
|
||
{
|
||
MMPTE PteContents;
|
||
MMPTE TempPte;
|
||
PMMPTE PointerPte;
|
||
PMMPTE PointerPde;
|
||
PMMPTE PointerPpe;
|
||
ULONG Waited;
|
||
|
||
PointerPte = MiGetPteAddress (VirtualAddress);
|
||
PointerPde = MiGetPdeAddress (VirtualAddress);
|
||
PointerPpe = MiGetPpeAddress (VirtualAddress);
|
||
|
||
//
|
||
// Block APCs and acquire the working set lock.
|
||
//
|
||
|
||
LOCK_WS (CurrentProcess);
|
||
|
||
//
|
||
// Make the PPE and PDE exist and valid.
|
||
//
|
||
|
||
if (MiDoesPpeExistAndMakeValid (PointerPpe,
|
||
CurrentProcess,
|
||
FALSE,
|
||
&Waited) == FALSE) {
|
||
|
||
UNLOCK_WS (CurrentProcess);
|
||
return;
|
||
}
|
||
|
||
if (MiDoesPdeExistAndMakeValid (PointerPde,
|
||
CurrentProcess,
|
||
FALSE,
|
||
&Waited) == FALSE) {
|
||
|
||
UNLOCK_WS (CurrentProcess);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Now it is safe to read PointerPte.
|
||
//
|
||
|
||
PteContents = *PointerPte;
|
||
|
||
//
|
||
// Check to see if the protection for the native page should be set
|
||
// and if the access bit of the PTE should be set.
|
||
//
|
||
|
||
if (PteContents.u.Hard.Valid != 0) {
|
||
|
||
TempPte = PteContents;
|
||
|
||
//
|
||
// Perform PTE protection mask corrections.
|
||
//
|
||
|
||
TempPte.u.Long |= NewPteProtection;
|
||
|
||
if (PteContents.u.Hard.Accessed == 0) {
|
||
|
||
TempPte.u.Hard.Accessed = 1;
|
||
|
||
if (PageIsSplit == TRUE) {
|
||
TempPte.u.Hard.Cache = MM_PTE_CACHE_RESERVED;
|
||
}
|
||
}
|
||
|
||
MI_WRITE_VALID_PTE_NEW_PROTECTION(PointerPte, TempPte);
|
||
}
|
||
|
||
UNLOCK_WS (CurrentProcess);
|
||
}
|
||
|
||
#endif
|