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.
3753 lines
101 KiB
3753 lines
101 KiB
/*++
|
|
|
|
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
|
|
MiResetAccessBitForNativePtes (
|
|
IN PVOID StartVirtual,
|
|
IN PVOID EndVirtual,
|
|
IN PEPROCESS Process
|
|
);
|
|
|
|
LOGICAL
|
|
MiIsSplitPage (
|
|
IN PVOID Virtual
|
|
);
|
|
|
|
VOID
|
|
MiCheckDemandZeroCopyOnWriteFor4kPage (
|
|
PVOID VirtualAddress,
|
|
PEPROCESS Process
|
|
);
|
|
|
|
LOGICAL
|
|
MiIsNativeGuardPage (
|
|
IN PVOID VirtualAddress
|
|
);
|
|
|
|
VOID
|
|
MiSetNativePteProtection (
|
|
IN PVOID VirtualAddress,
|
|
IN ULONGLONG NewPteProtection,
|
|
IN LOGICAL PageIsSplit,
|
|
IN PEPROCESS CurrentProcess
|
|
);
|
|
|
|
VOID
|
|
MiSyncAltPte (
|
|
IN PVOID VirtualAddress
|
|
);
|
|
|
|
extern PMMPTE MmPteHit;
|
|
|
|
#if defined (_MI_DEBUG_ALTPTE)
|
|
|
|
typedef struct _MI_ALTPTE_TRACES {
|
|
|
|
PETHREAD Thread;
|
|
PMMPTE PointerPte;
|
|
MMPTE PteContents;
|
|
MMPTE NewPteContents;
|
|
PVOID Caller;
|
|
PVOID CallersCaller;
|
|
PVOID Temp[2];
|
|
|
|
} MI_ALTPTE_TRACES, *PMI_ALTPTE_TRACES;
|
|
|
|
#define MI_ALTPTE_TRACE_SIZE 0x1000
|
|
|
|
VOID
|
|
FORCEINLINE
|
|
MiSnapAltPte (
|
|
IN PMMPTE PointerPte,
|
|
IN MMPTE NewValue,
|
|
IN ULONG Id
|
|
)
|
|
{
|
|
ULONG Index;
|
|
SIZE_T NumberOfBytes;
|
|
PMI_ALTPTE_TRACES Information;
|
|
PVOID HighestUserAddress;
|
|
PLONG IndexPointer;
|
|
PMI_ALTPTE_TRACES TablePointer;
|
|
PWOW64_PROCESS Wow64Process;
|
|
|
|
HighestUserAddress = MiGetVirtualAddressMappedByPte (MmWorkingSetList->HighestUserPte);
|
|
ASSERT (HighestUserAddress <= (PVOID) _4gb);
|
|
|
|
NumberOfBytes = ((ULONG_PTR)HighestUserAddress >> PTI_SHIFT) / 8;
|
|
|
|
Wow64Process = PsGetCurrentProcess()->Wow64Process;
|
|
ASSERT (Wow64Process != NULL);
|
|
|
|
IndexPointer = (PLONG) ((PCHAR) Wow64Process->AltPermBitmap + NumberOfBytes);
|
|
TablePointer = (PMI_ALTPTE_TRACES)IndexPointer + 1;
|
|
|
|
Index = InterlockedIncrement (IndexPointer);
|
|
|
|
Index &= (MI_ALTPTE_TRACE_SIZE - 1);
|
|
|
|
Information = &TablePointer[Index];
|
|
|
|
Information->Thread = PsGetCurrentThread ();
|
|
Information->PteContents = *PointerPte;
|
|
Information->NewPteContents = NewValue;
|
|
Information->PointerPte = PointerPte;
|
|
|
|
#if 1
|
|
Information->Caller = MiGetInstructionPointer ();
|
|
#else
|
|
// Ip generates link (not compile) errors
|
|
Information->Caller = (PVOID) __getReg (CV_IA64_Ip);
|
|
|
|
// StIIp generates no compiler or link errors, but bugcheck 3Bs on the
|
|
// execution of the actual generated mov r19=cr.iip instruction.
|
|
Information->Caller = (PVOID) __getReg (CV_IA64_StIIP);
|
|
#endif
|
|
|
|
Information->Temp[0] = (PVOID) (ULONG_PTR) Id;
|
|
|
|
Information->CallersCaller = (PVOID) _ReturnAddress ();
|
|
}
|
|
|
|
#define MI_ALTPTE_TRACKING_BYTES ((MI_ALTPTE_TRACE_SIZE + 1) * sizeof (MI_ALTPTE_TRACES))
|
|
|
|
#define MI_LOG_ALTPTE_CHANGE(_PointerPte, _PteContents, Id) MiSnapAltPte(_PointerPte, _PteContents, Id)
|
|
|
|
#else
|
|
|
|
#define MI_ALTPTE_TRACKING_BYTES 0
|
|
|
|
#define MI_LOG_ALTPTE_CHANGE(_PointerPte, _PteContents, Id)
|
|
|
|
#endif
|
|
|
|
|
|
#define MI_WRITE_ALTPTE(PointerAltPte, AltPteContents, Id) { \
|
|
MI_LOG_ALTPTE_CHANGE (PointerAltPte, AltPteContents, Id); \
|
|
(PointerAltPte)->u.Long = AltPteContents.u.Long; \
|
|
}
|
|
|
|
#if defined (_MI_DEBUG_PTE) && defined (_MI_DEBUG_ALTPTE)
|
|
VOID
|
|
MiLogPteInAltTrace (
|
|
IN PVOID InputNativeInformation
|
|
)
|
|
{
|
|
ULONG Index;
|
|
SIZE_T NumberOfBytes;
|
|
PMI_ALTPTE_TRACES Information;
|
|
PVOID HighestUserAddress;
|
|
PLONG IndexPointer;
|
|
PMI_ALTPTE_TRACES TablePointer;
|
|
PWOW64_PROCESS Wow64Process;
|
|
PMI_PTE_TRACES NativeInformation;
|
|
|
|
NativeInformation = (PMI_PTE_TRACES) InputNativeInformation;
|
|
|
|
if (PsGetCurrentProcess()->Peb == NULL) {
|
|
|
|
//
|
|
// Don't log PTE traces during process creation if the altperm
|
|
// bitmap pool allocation hasn't been done yet (the EPROCESS
|
|
// Wow64Process pointer is already initialized) !
|
|
//
|
|
|
|
return;
|
|
}
|
|
|
|
if (PsGetCurrentProcess()->VmDeleted == 1) {
|
|
|
|
//
|
|
// Don't log PTE traces during process deletion as the altperm
|
|
// bitmap pool allocation may have already been freed !
|
|
//
|
|
|
|
return;
|
|
}
|
|
|
|
HighestUserAddress = MiGetVirtualAddressMappedByPte (MmWorkingSetList->HighestUserPte);
|
|
ASSERT (HighestUserAddress <= (PVOID) _4gb);
|
|
|
|
NumberOfBytes = ((ULONG_PTR)HighestUserAddress >> PTI_SHIFT) / 8;
|
|
|
|
Wow64Process = PsGetCurrentProcess()->Wow64Process;
|
|
ASSERT (Wow64Process != NULL);
|
|
|
|
IndexPointer = (PLONG) ((PCHAR) Wow64Process->AltPermBitmap + NumberOfBytes);
|
|
TablePointer = (PMI_ALTPTE_TRACES)IndexPointer + 1;
|
|
|
|
Index = InterlockedIncrement (IndexPointer);
|
|
|
|
Index &= (MI_ALTPTE_TRACE_SIZE - 1);
|
|
|
|
Information = &TablePointer[Index];
|
|
|
|
Information->Thread = NativeInformation->Thread;
|
|
Information->PteContents = NativeInformation->PteContents;
|
|
Information->NewPteContents = NativeInformation->NewPteContents;
|
|
Information->PointerPte = NativeInformation->PointerPte;
|
|
|
|
Information->Caller = NativeInformation->StackTrace[0];
|
|
Information->CallersCaller = NativeInformation->StackTrace[1];
|
|
|
|
Information->Temp[0] = (PVOID) (ULONG_PTR) -1;
|
|
}
|
|
#endif
|
|
|
|
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 32-bit user address space.
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
ULONG Waited;
|
|
PMMVAD TempVad;
|
|
MMPTE PteContents;
|
|
PMMPTE PointerAltPte;
|
|
PMMPTE PointerAltPte2;
|
|
PMMPTE PointerAltPteForNativePage;
|
|
MMPTE AltPteContents;
|
|
PMMPTE PointerPte;
|
|
PMMPTE PointerPde;
|
|
ULONGLONG NewPteProtection;
|
|
LOGICAL FillZero;
|
|
LOGICAL PageIsSplit;
|
|
LOGICAL SharedPageFault;
|
|
LOGICAL NativeGuardPage;
|
|
PEPROCESS CurrentProcess;
|
|
PWOW64_PROCESS Wow64Process;
|
|
KIRQL OldIrql;
|
|
NTSTATUS status;
|
|
ULONGLONG ProtectionMaskOriginal;
|
|
PMMPTE ProtoPte;
|
|
PMMPFN Pfn1;
|
|
PVOID OriginalVirtualAddress;
|
|
ULONG_PTR Vpn;
|
|
PVOID ZeroAddress;
|
|
PMMPTE PointerPpe;
|
|
ULONG FirstProtect;
|
|
|
|
ASSERT (VirtualAddress < MmWorkingSetList->HighestUserAddress);
|
|
|
|
if (KeGetCurrentIrql () > APC_LEVEL) {
|
|
return MmAccessFault (FaultStatus,
|
|
VirtualAddress,
|
|
PreviousMode,
|
|
TrapInformation);
|
|
}
|
|
|
|
NewPteProtection = 0;
|
|
FillZero = FALSE;
|
|
PageIsSplit = FALSE;
|
|
SharedPageFault = FALSE;
|
|
NativeGuardPage = FALSE;
|
|
OriginalVirtualAddress = VirtualAddress;
|
|
|
|
CurrentProcess = PsGetCurrentProcess ();
|
|
|
|
Wow64Process = CurrentProcess->Wow64Process;
|
|
|
|
PointerPte = MiGetPteAddress (VirtualAddress);
|
|
PointerAltPte = MiGetAltPteAddress (VirtualAddress);
|
|
|
|
Vpn = MI_VA_TO_VPN (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 (Wow64Process);
|
|
|
|
LOCK_WS (CurrentProcess);
|
|
|
|
if (MiWaitForForkToComplete (CurrentProcess) == 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, Vpn) == 0) {
|
|
MiSyncAltPte (VirtualAddress);
|
|
}
|
|
|
|
//
|
|
// Read the alternate PTE contents.
|
|
//
|
|
|
|
AltPteContents = *PointerAltPte;
|
|
|
|
//
|
|
// If the alternate PTE indicates no access for this 4K page
|
|
// then deliver an access violation.
|
|
//
|
|
|
|
if (AltPteContents.u.Alt.NoAccess != 0) {
|
|
status = STATUS_ACCESS_VIOLATION;
|
|
MI_BREAK_ON_AV (VirtualAddress, 0x20);
|
|
goto return_status;
|
|
}
|
|
|
|
//
|
|
// 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 (AltPteContents.u.Alt.InPageInProgress == 1) {
|
|
|
|
//
|
|
// Release the Alt PTE lock
|
|
//
|
|
|
|
UNLOCK_ALTERNATE_TABLE (Wow64Process);
|
|
|
|
//
|
|
// Flush the TB as MiSetNativePteProtection may have edited the PTE.
|
|
//
|
|
|
|
KiFlushSingleTb (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;
|
|
}
|
|
|
|
//
|
|
// 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,
|
|
&FirstProtect,
|
|
&TempVad);
|
|
|
|
if (ProtoPte != NULL) {
|
|
|
|
if (FirstProtect == MM_UNKNOWN_PROTECTION) {
|
|
|
|
//
|
|
// Ultimately this must be an address backed by a prototype
|
|
// PTE in an image section (and the real PTE is currently
|
|
// zero). Therefore we are guaranteed that the protection
|
|
// in the prototype PTE is the correct one to use (ie: there
|
|
// is no WSLE overriding it).
|
|
//
|
|
|
|
ASSERT (!MI_IS_PHYSICAL_ADDRESS(ProtoPte));
|
|
|
|
PointerPde = MiGetPteAddress (ProtoPte);
|
|
LOCK_PFN (OldIrql);
|
|
if (PointerPde->u.Hard.Valid == 0) {
|
|
MiMakeSystemAddressValidPfn (ProtoPte, OldIrql);
|
|
}
|
|
|
|
PteContents = *ProtoPte;
|
|
|
|
if (PteContents.u.Long == 0) {
|
|
FirstProtect = MM_NOACCESS;
|
|
}
|
|
else if (PteContents.u.Hard.Valid == 1) {
|
|
|
|
//
|
|
// The prototype PTE is valid, get the protection from
|
|
// the PFN database.
|
|
//
|
|
|
|
Pfn1 = MI_PFN_ELEMENT (PteContents.u.Hard.PageFrameNumber);
|
|
|
|
FirstProtect = (ULONG) Pfn1->OriginalPte.u.Soft.Protection;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// The prototype PTE is not valid, ie: subsection format,
|
|
// demand zero, pagefile or in transition - in all cases,
|
|
// the protection is in the PTE.
|
|
//
|
|
|
|
FirstProtect = (ULONG) PteContents.u.Soft.Protection;
|
|
}
|
|
|
|
UNLOCK_PFN (OldIrql);
|
|
|
|
ASSERT (FirstProtect != MM_INVALID_PROTECTION);
|
|
}
|
|
|
|
UNLOCK_WS (CurrentProcess);
|
|
|
|
if (FirstProtect == MM_INVALID_PROTECTION) {
|
|
status = STATUS_ACCESS_VIOLATION;
|
|
MI_BREAK_ON_AV (VirtualAddress, 0x21);
|
|
goto return_status;
|
|
}
|
|
|
|
if (FirstProtect != MM_NOACCESS) {
|
|
|
|
ProtectionMaskOriginal = MiMakeProtectionAteMask (FirstProtect);
|
|
|
|
SharedPageFault = TRUE;
|
|
ProtectionMaskOriginal |= MM_ATE_COMMIT;
|
|
|
|
AltPteContents.u.Long = ProtectionMaskOriginal;
|
|
AltPteContents.u.Alt.Protection = FirstProtect;
|
|
|
|
//
|
|
// Atomically update the PTE.
|
|
//
|
|
|
|
MI_WRITE_ALTPTE (PointerAltPte, AltPteContents, 1);
|
|
}
|
|
}
|
|
else {
|
|
UNLOCK_WS (CurrentProcess);
|
|
}
|
|
}
|
|
|
|
if (AltPteContents.u.Alt.Commit == 0) {
|
|
|
|
//
|
|
// If the page is not committed, return an access violation.
|
|
//
|
|
|
|
status = STATUS_ACCESS_VIOLATION;
|
|
MI_BREAK_ON_AV (VirtualAddress, 0x22);
|
|
goto return_status;
|
|
}
|
|
|
|
//
|
|
// Check whether the faulting page is split into 4k pages.
|
|
//
|
|
|
|
PointerAltPte2 = MiGetAltPteAddress (PAGE_ALIGN (VirtualAddress));
|
|
PteContents = *PointerAltPte2;
|
|
|
|
PageIsSplit = FALSE;
|
|
|
|
for (i = 0; i < SPLITS_PER_PAGE; i += 1) {
|
|
|
|
if ((PointerAltPte2->u.Long != 0) &&
|
|
((PointerAltPte2->u.Alt.Commit == 0) ||
|
|
(PointerAltPte2->u.Alt.Accessed == 0) ||
|
|
(PointerAltPte2->u.Alt.CopyOnWrite != 0) ||
|
|
(PointerAltPte2->u.Alt.PteIndirect != 0) ||
|
|
(PointerAltPte2->u.Alt.FillZero != 0))) {
|
|
|
|
//
|
|
// If it is a NoAccess, FillZero or Guard page, CopyOnWrite,
|
|
// mark it as a split page.
|
|
//
|
|
|
|
PageIsSplit = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (PteContents.u.Long != PointerAltPte2->u.Long) {
|
|
|
|
//
|
|
// If the next 4kb page is different from the 1st 4k page
|
|
// the page is split.
|
|
//
|
|
|
|
PageIsSplit = TRUE;
|
|
break;
|
|
}
|
|
|
|
PointerAltPte2 += 1;
|
|
}
|
|
|
|
//
|
|
// Get the real protection for the native PTE.
|
|
//
|
|
|
|
NewPteProtection = 0;
|
|
|
|
PointerAltPte2 -= i;
|
|
|
|
for (i = 0; i < SPLITS_PER_PAGE; i += 1) {
|
|
|
|
PteContents.u.Long = PointerAltPte2->u.Long;
|
|
|
|
if (PteContents.u.Alt.PteIndirect == 0) {
|
|
NewPteProtection |= (PointerAltPte2->u.Long & ALT_PROTECTION_MASK);
|
|
}
|
|
|
|
PointerAltPte2 += 1;
|
|
}
|
|
|
|
PointerAltPte2 -= SPLITS_PER_PAGE;
|
|
|
|
//
|
|
// 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);
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
MI_BREAK_ON_AV (OriginalVirtualAddress, 0x23);
|
|
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)) {
|
|
|
|
//
|
|
// Change the protection of the alternate pages for this
|
|
// copy on write native page.
|
|
//
|
|
|
|
ASSERT (PointerAltPte2 == MiGetAltPteAddress (PAGE_ALIGN(OriginalVirtualAddress)));
|
|
|
|
for (i = 0; i < SPLITS_PER_PAGE; i += 1) {
|
|
|
|
AltPteContents.u.Long = PointerAltPte2->u.Long;
|
|
|
|
if (AltPteContents.u.Alt.Commit != 0) {
|
|
|
|
//
|
|
// When a copy-on-write page is touched, the native
|
|
// page will be made private by MM, so all the sub-4k
|
|
// pages within the native page should be made
|
|
// private (if they are committed/mapped).
|
|
//
|
|
|
|
AltPteContents.u.Alt.Private = 1;
|
|
|
|
if (AltPteContents.u.Alt.CopyOnWrite != 0) {
|
|
|
|
AltPteContents.u.Alt.CopyOnWrite = 0;
|
|
AltPteContents.u.Hard.Write = 1;
|
|
|
|
AltPteContents.u.Alt.Protection =
|
|
MI_MAKE_PROTECT_NOT_WRITE_COPY(AltPteContents.u.Alt.Protection);
|
|
}
|
|
|
|
//
|
|
// Atomically update the PTE.
|
|
//
|
|
|
|
MI_WRITE_ALTPTE (PointerAltPte2, AltPteContents, 2);
|
|
}
|
|
|
|
PointerAltPte2 += 1;
|
|
}
|
|
}
|
|
|
|
goto return_status;
|
|
}
|
|
|
|
if (AltPteContents.u.Hard.Write == 0) {
|
|
status = STATUS_ACCESS_VIOLATION;
|
|
MI_BREAK_ON_AV (OriginalVirtualAddress, 0x24);
|
|
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) {
|
|
|
|
AltPteContents = *PointerAltPteForNativePage;
|
|
AltPteContents.u.Alt.InPageInProgress = TRUE;
|
|
|
|
MI_WRITE_ALTPTE (PointerAltPteForNativePage, AltPteContents, 3);
|
|
|
|
PointerAltPteForNativePage += 1;
|
|
}
|
|
|
|
//
|
|
// Let MmAccessFault() perform an inpage, dirty-bit setting, etc.
|
|
//
|
|
// Release the alternate table mutex but block APCs to prevent an
|
|
// incoming APC that references the same page from deadlocking this thread.
|
|
// It is safe to drop allow APCs only after the in progress bit in
|
|
// the alternate PTE has been cleared.
|
|
//
|
|
|
|
KeRaiseIrql (APC_LEVEL, &OldIrql);
|
|
|
|
UNLOCK_ALTERNATE_TABLE (Wow64Process);
|
|
|
|
status = MmAccessFault (FaultStatus,
|
|
VirtualAddress,
|
|
PreviousMode,
|
|
TrapInformation);
|
|
|
|
LOCK_ALTERNATE_TABLE (Wow64Process);
|
|
|
|
//
|
|
// This IRQL lower will have no effect since the alternate table guarded
|
|
// mutex is now held again.
|
|
//
|
|
|
|
KeLowerIrql (OldIrql);
|
|
|
|
for (i = 0; i < SPLITS_PER_PAGE; i += 1) {
|
|
|
|
PointerAltPteForNativePage -= 1;
|
|
|
|
AltPteContents = *PointerAltPteForNativePage;
|
|
AltPteContents.u.Alt.InPageInProgress = FALSE;
|
|
|
|
MI_WRITE_ALTPTE (PointerAltPteForNativePage, AltPteContents, 4);
|
|
}
|
|
|
|
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;
|
|
|
|
MI_WRITE_ALTPTE (PointerAltPte, AltPteContents, 5);
|
|
|
|
if ((status != STATUS_PAGE_FAULT_GUARD_PAGE) &&
|
|
(status != STATUS_STACK_OVERFLOW)) {
|
|
|
|
UNLOCK_ALTERNATE_TABLE (Wow64Process);
|
|
|
|
status = MiCheckForUserStackOverflow (VirtualAddress);
|
|
|
|
LOCK_ALTERNATE_TABLE (Wow64Process);
|
|
}
|
|
}
|
|
else if (status == STATUS_GUARD_PAGE_VIOLATION) {
|
|
|
|
//
|
|
// Native PTE has the guard bit set, but the AltPte
|
|
// doesn't have it.
|
|
//
|
|
// See if any of the AltPtes has the guard bit set.
|
|
//
|
|
|
|
ASSERT (PointerAltPteForNativePage == MiGetAltPteAddress (PAGE_ALIGN (VirtualAddress)));
|
|
|
|
for (i = 0; i < SPLITS_PER_PAGE; i += 1) {
|
|
|
|
if (PointerAltPteForNativePage->u.Alt.Protection & MM_GUARD_PAGE) {
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
PointerAltPteForNativePage += 1;
|
|
}
|
|
}
|
|
else if ((SharedPageFault == TRUE) && (status == STATUS_ACCESS_VIOLATION)) {
|
|
|
|
AltPteContents.u.Long = PointerAltPte->u.Long;
|
|
|
|
AltPteContents.u.Alt.Commit = 0;
|
|
|
|
MI_WRITE_ALTPTE (PointerAltPte, AltPteContents, 6);
|
|
}
|
|
|
|
return_status:
|
|
|
|
KiFlushSingleTb (OriginalVirtualAddress);
|
|
|
|
if (FillZero == TRUE) {
|
|
|
|
//
|
|
// Zero the specified 4k page.
|
|
//
|
|
|
|
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.
|
|
//
|
|
|
|
goto Finished;
|
|
}
|
|
|
|
//
|
|
// 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 (CurrentProcess);
|
|
|
|
if (MiDoesPpeExistAndMakeValid (PointerPpe,
|
|
CurrentProcess,
|
|
MM_NOIRQL,
|
|
&Waited) == FALSE) {
|
|
PteContents.u.Long = 0;
|
|
}
|
|
else if (MiDoesPdeExistAndMakeValid (PointerPde,
|
|
CurrentProcess,
|
|
MM_NOIRQL,
|
|
&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 (MiIsAddressValid (PointerAltPte, TRUE) == TRUE) {
|
|
break;
|
|
}
|
|
|
|
UNLOCK_WS_UNSAFE (CurrentProcess);
|
|
|
|
} while (TRUE);
|
|
|
|
AltPteContents.u.Long = PointerAltPte->u.Long;
|
|
|
|
if (PteContents.u.Hard.Valid != 0) {
|
|
|
|
ZeroAddress = KSEG_ADDRESS (PteContents.u.Hard.PageFrameNumber);
|
|
|
|
ZeroAddress =
|
|
(PVOID)((ULONG_PTR)ZeroAddress +
|
|
((ULONG_PTR)PAGE_4K_ALIGN(VirtualAddress) & (PAGE_SIZE-1)));
|
|
|
|
RtlZeroMemory (ZeroAddress, PAGE_4K);
|
|
|
|
UNLOCK_WS_UNSAFE (CurrentProcess);
|
|
|
|
AltPteContents.u.Alt.FillZero = 0;
|
|
AltPteContents.u.Alt.Accessed = 1;
|
|
}
|
|
else {
|
|
|
|
UNLOCK_WS_UNSAFE (CurrentProcess);
|
|
|
|
AltPteContents.u.Alt.Accessed = 0;
|
|
}
|
|
|
|
MI_WRITE_ALTPTE (PointerAltPte, AltPteContents, 7);
|
|
}
|
|
|
|
Finished:
|
|
|
|
UNLOCK_ALTERNATE_TABLE (Wow64Process);
|
|
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
MiSyncAltPte (
|
|
IN PVOID VirtualAddress
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called to compute the alternate PTE entries for the
|
|
given virtual address. It is called with the alternate table mutex held
|
|
and updates the alternate table bitmap before returning.
|
|
|
|
Arguments:
|
|
|
|
VirtualAddress - Supplies the virtual address to evaluate.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Environment:
|
|
|
|
Kernel mode, alternate table mutex held.
|
|
|
|
--*/
|
|
|
|
{
|
|
PMMVAD TempVad;
|
|
MMPTE PteContents;
|
|
PMMPTE PointerAltPte;
|
|
MMPTE AltPteContents;
|
|
PMMPTE PointerPde;
|
|
PEPROCESS CurrentProcess;
|
|
PWOW64_PROCESS Wow64Process;
|
|
KIRQL OldIrql;
|
|
PMMPTE ProtoPte;
|
|
PMMPFN Pfn1;
|
|
ULONG_PTR Vpn;
|
|
ULONG FirstProtect;
|
|
ULONG SecondProtect;
|
|
PSUBSECTION Subsection;
|
|
PSUBSECTION FirstSubsection;
|
|
PCONTROL_AREA ControlArea;
|
|
PMMVAD Vad;
|
|
|
|
Vpn = MI_VA_TO_VPN (VirtualAddress);
|
|
|
|
CurrentProcess = PsGetCurrentProcess ();
|
|
|
|
Wow64Process = CurrentProcess->Wow64Process;
|
|
|
|
LOCK_WS_UNSAFE (CurrentProcess);
|
|
|
|
ASSERT ((MiGetPpeAddress (VirtualAddress)->u.Hard.Valid == 0) ||
|
|
(MiGetPdeAddress (VirtualAddress)->u.Hard.Valid == 0) ||
|
|
(MiGetPteAddress (VirtualAddress)->u.Long == 0));
|
|
|
|
ProtoPte = MiCheckVirtualAddress (VirtualAddress,
|
|
&FirstProtect,
|
|
&TempVad);
|
|
|
|
if (FirstProtect == MM_UNKNOWN_PROTECTION) {
|
|
|
|
//
|
|
// Ultimately this must be an address backed by a prototype PTE in
|
|
// an image section (and the real PTE is currently zero). Therefore
|
|
// we are guaranteed that the protection in the prototype PTE
|
|
// is the correct one to use (ie: there is no WSLE overriding it).
|
|
//
|
|
|
|
Vad = MiLocateAddress (VirtualAddress);
|
|
|
|
ASSERT (Vad != NULL);
|
|
|
|
ControlArea = Vad->ControlArea;
|
|
ASSERT (ControlArea->u.Flags.Image == 1);
|
|
|
|
if ((ControlArea->u.Flags.Rom == 1) ||
|
|
(ControlArea->u.Flags.GlobalOnlyPerSession == 1)) {
|
|
|
|
FirstSubsection = (PSUBSECTION)((PLARGE_CONTROL_AREA)ControlArea + 1);
|
|
}
|
|
else {
|
|
FirstSubsection = (PSUBSECTION)(ControlArea + 1);
|
|
}
|
|
|
|
ASSERT (!MI_IS_PHYSICAL_ADDRESS (ProtoPte));
|
|
|
|
//
|
|
// Get the original protection information from the prototype PTE.
|
|
//
|
|
// A non-NULL subsection indicates split permissions need to be
|
|
// applied on the ALT PTEs for this native PTE.
|
|
//
|
|
|
|
Subsection = NULL;
|
|
|
|
//
|
|
// Read the prototype PTE contents without the PFN lock - the PFN
|
|
// lock is only needed if the prototype PTE is valid so that the
|
|
// PFN's original PTE field can be fetched.
|
|
//
|
|
|
|
PteContents = *ProtoPte;
|
|
|
|
DecodeProto:
|
|
|
|
if (PteContents.u.Hard.Valid == 0) {
|
|
|
|
if (PteContents.u.Long == 0) {
|
|
FirstProtect = MM_NOACCESS;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// The prototype PTE is not valid, ie: subsection format,
|
|
// demand zero, pagefile or in transition - in all cases,
|
|
// the protection is in the PTE.
|
|
//
|
|
|
|
FirstProtect = (ULONG) PteContents.u.Soft.Protection;
|
|
|
|
if (PteContents.u.Soft.SplitPermissions == 1) {
|
|
Subsection = (PSUBSECTION) 1;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
PointerPde = MiGetPteAddress (ProtoPte);
|
|
LOCK_PFN (OldIrql);
|
|
if (PointerPde->u.Hard.Valid == 0) {
|
|
MiMakeSystemAddressValidPfn (ProtoPte, OldIrql);
|
|
}
|
|
|
|
PteContents = *ProtoPte;
|
|
|
|
if (PteContents.u.Hard.Valid == 0) {
|
|
UNLOCK_PFN (OldIrql);
|
|
goto DecodeProto;
|
|
}
|
|
|
|
//
|
|
// The prototype PTE is valid, get the protection from
|
|
// the PFN database. Unless the protection is split, in
|
|
// which case it must be retrieved from the subsection.
|
|
// Note that if the page has been trimmed already then
|
|
// the original PTE is no longer in subsection format.
|
|
//
|
|
|
|
Pfn1 = MI_PFN_ELEMENT (PteContents.u.Hard.PageFrameNumber);
|
|
|
|
FirstProtect = (ULONG) Pfn1->OriginalPte.u.Soft.Protection;
|
|
|
|
UNLOCK_PFN (OldIrql);
|
|
|
|
if (PteContents.u.Hard.Cache == MM_PTE_CACHE_RESERVED) {
|
|
Subsection = (PSUBSECTION) 1;
|
|
}
|
|
}
|
|
|
|
ASSERT (FirstProtect != MM_INVALID_PROTECTION);
|
|
|
|
if (Subsection != NULL) {
|
|
|
|
//
|
|
// Compute the subsection address as the split permissions are
|
|
// stored there. Note to reduce lock contention this was
|
|
// deferred until after the PFN lock was released.
|
|
//
|
|
|
|
Subsection = FirstSubsection;
|
|
|
|
do {
|
|
ASSERT (Subsection->SubsectionBase != NULL);
|
|
|
|
if ((ProtoPte >= Subsection->SubsectionBase) &&
|
|
(ProtoPte < Subsection->SubsectionBase + Subsection->PtesInSubsection)) {
|
|
break;
|
|
}
|
|
|
|
Subsection = Subsection->NextSubsection;
|
|
|
|
} while (TRUE);
|
|
|
|
ASSERT (Subsection != NULL);
|
|
|
|
//
|
|
// Get the protection for each 4K page from the subsection.
|
|
//
|
|
|
|
FirstProtect = Subsection->u.SubsectionFlags.Protection;
|
|
SecondProtect = Subsection->LastSplitPageProtection;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// If demand-zero and copy-on-write, remove copy-on-write.
|
|
// Note this cannot happen for images with native (ie: multiple
|
|
// subsection) support.
|
|
//
|
|
|
|
if ((!IS_PTE_NOT_DEMAND_ZERO (PteContents)) &&
|
|
(PteContents.u.Soft.Protection & MM_COPY_ON_WRITE_MASK)) {
|
|
|
|
FirstProtect = FirstProtect & ~MM_PROTECTION_COPY_MASK;
|
|
}
|
|
|
|
SecondProtect = FirstProtect;
|
|
}
|
|
|
|
ASSERT ((FirstProtect != MM_INVALID_PROTECTION) &&
|
|
(SecondProtect != MM_INVALID_PROTECTION));
|
|
|
|
UNLOCK_WS_UNSAFE (CurrentProcess);
|
|
|
|
PointerAltPte = MiGetAltPteAddress (PAGE_ALIGN (VirtualAddress));
|
|
|
|
//
|
|
// Update the first alternate PTE.
|
|
//
|
|
|
|
AltPteContents.u.Long = MiMakeProtectionAteMask (FirstProtect) | MM_ATE_COMMIT;
|
|
AltPteContents.u.Alt.Protection = FirstProtect;
|
|
|
|
if ((FirstProtect & MM_PROTECTION_COPY_MASK) == 0) {
|
|
|
|
//
|
|
// If copy-on-write is removed, make it private.
|
|
//
|
|
|
|
AltPteContents.u.Alt.Private = 1;
|
|
}
|
|
|
|
MI_WRITE_ALTPTE (PointerAltPte, AltPteContents, 8);
|
|
|
|
//
|
|
// Update the second alternate PTE, computing it
|
|
// only if it is different from the first.
|
|
//
|
|
|
|
if (Subsection != NULL) {
|
|
|
|
AltPteContents.u.Long = MiMakeProtectionAteMask (SecondProtect) | MM_ATE_COMMIT;
|
|
AltPteContents.u.Alt.Protection = SecondProtect;
|
|
|
|
if ((SecondProtect & MM_PROTECTION_COPY_MASK) == 0) {
|
|
|
|
//
|
|
// If copy-on-write is removed, make it private.
|
|
//
|
|
|
|
AltPteContents.u.Alt.Private = 1;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
UNLOCK_WS_UNSAFE (CurrentProcess);
|
|
|
|
AltPteContents.u.Long = MiMakeProtectionAteMask (FirstProtect);
|
|
AltPteContents.u.Alt.Protection = FirstProtect;
|
|
AltPteContents.u.Alt.Commit = 1;
|
|
|
|
PointerAltPte = MiGetAltPteAddress (PAGE_ALIGN (VirtualAddress));
|
|
|
|
//
|
|
// Update the alternate PTEs.
|
|
//
|
|
|
|
MI_WRITE_ALTPTE (PointerAltPte, AltPteContents, 9);
|
|
}
|
|
|
|
MI_WRITE_ALTPTE (PointerAltPte + 1, AltPteContents, 0xA);
|
|
|
|
//
|
|
// Update the bitmap.
|
|
//
|
|
|
|
MI_SET_BIT (Wow64Process->AltPermBitmap, Vpn);
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
MiProtectImageFileFor4kPage (
|
|
IN PVOID VirtualAddress,
|
|
IN SIZE_T ViewSize
|
|
)
|
|
{
|
|
ULONG Vpn;
|
|
PVOID EndAddress;
|
|
PULONG Bitmap;
|
|
PWOW64_PROCESS Wow64Process;
|
|
|
|
ASSERT (BYTE_OFFSET (VirtualAddress) == 0);
|
|
|
|
Vpn = (ULONG) MI_VA_TO_VPN (VirtualAddress);
|
|
|
|
EndAddress = (PVOID)((PCHAR) VirtualAddress + ViewSize - 1);
|
|
|
|
Wow64Process = PsGetCurrentProcess()->Wow64Process;
|
|
|
|
Bitmap = Wow64Process->AltPermBitmap;
|
|
|
|
LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
|
|
|
do {
|
|
|
|
if (MI_CHECK_BIT (Bitmap, Vpn) == 0) {
|
|
MiSyncAltPte (VirtualAddress);
|
|
}
|
|
|
|
VirtualAddress = (PVOID)((PCHAR) VirtualAddress + PAGE_SIZE);
|
|
|
|
Vpn += 1;
|
|
|
|
} while (VirtualAddress <= EndAddress);
|
|
|
|
UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Define and initialize the protection conversion table for the
|
|
// Alternate Permission 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 Starting4KAddress,
|
|
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:
|
|
|
|
Starting4KAddress - 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.
|
|
|
|
--*/
|
|
|
|
{
|
|
RTL_BITMAP BitMap;
|
|
ULONG NumberOfPtes;
|
|
ULONG StartingNativeVpn;
|
|
PVOID Ending4KAddress;
|
|
ULONG NewProtectNotCopy;
|
|
ULONGLONG ProtectionMask;
|
|
ULONGLONG ProtectionMaskNotCopy;
|
|
PMMPTE StartAltPte;
|
|
PMMPTE EndAltPte;
|
|
PMMPTE StartAltPte0;
|
|
PMMPTE EndAltPte0;
|
|
PWOW64_PROCESS Wow64Process;
|
|
MMPTE AltPteContents;
|
|
MMPTE TempAltPte;
|
|
|
|
Ending4KAddress = (PCHAR)Starting4KAddress + Size - 1;
|
|
|
|
//
|
|
// If the addresses are not WOW64 then nothing needs to be done here.
|
|
//
|
|
|
|
if ((Starting4KAddress >= MmWorkingSetList->HighestUserAddress) ||
|
|
(Ending4KAddress >= MmWorkingSetList->HighestUserAddress)) {
|
|
|
|
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);
|
|
|
|
StartAltPte0 = MiGetAltPteAddress (PAGE_ALIGN (Starting4KAddress));
|
|
EndAltPte0 = MiGetAltPteAddress ((ULONG_PTR)PAGE_ALIGN(Ending4KAddress)+PAGE_SIZE-1);
|
|
|
|
Wow64Process = Process->Wow64Process;
|
|
|
|
StartingNativeVpn = (ULONG) MI_VA_TO_VPN (Starting4KAddress);
|
|
|
|
TempAltPte.u.Long = 0;
|
|
|
|
//
|
|
// Acquire the mutex guarding the alternate page table.
|
|
//
|
|
|
|
LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
|
|
|
if (!(Flags & ALT_ALLOCATE) &&
|
|
(MI_CHECK_BIT(Wow64Process->AltPermBitmap, StartingNativeVpn) == 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.
|
|
//
|
|
|
|
MI_WRITE_ALTPTE (StartAltPte, TempAltPte, 0xB);
|
|
|
|
StartAltPte += 1;
|
|
}
|
|
|
|
ASSERT (TempAltPte.u.Long != 0);
|
|
|
|
if (Flags & ALT_ALLOCATE) {
|
|
|
|
//
|
|
// Fill the empty Alt PTE as NoAccess ATE at the end.
|
|
//
|
|
|
|
EndAltPte += 1;
|
|
|
|
while (EndAltPte <= EndAltPte0) {
|
|
|
|
if (EndAltPte->u.Long == 0) {
|
|
|
|
TempAltPte.u.Long = EndAltPte->u.Long;
|
|
TempAltPte.u.Alt.NoAccess = 1;
|
|
|
|
//
|
|
// Atomic PTE update.
|
|
//
|
|
|
|
MI_WRITE_ALTPTE (EndAltPte, TempAltPte, 0xC);
|
|
}
|
|
|
|
EndAltPte += 1;
|
|
}
|
|
|
|
//
|
|
// Update the permission bitmap.
|
|
//
|
|
// Initialize the bitmap inline for speed.
|
|
//
|
|
|
|
BitMap.SizeOfBitMap = (ULONG)((ULONG_PTR)MmWorkingSetList->HighestUserAddress >> PTI_SHIFT);
|
|
BitMap.Buffer = Wow64Process->AltPermBitmap;
|
|
|
|
RtlSetBits (&BitMap, StartingNativeVpn, NumberOfPtes);
|
|
}
|
|
|
|
MiResetAccessBitForNativePtes (Starting4KAddress, Ending4KAddress, Process);
|
|
|
|
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;
|
|
ULONG Vpn;
|
|
ULONG VpnRange;
|
|
|
|
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 >= MmWorkingSetList->HighestUserAddress) ||
|
|
(Ending4KAddress >= MmWorkingSetList->HighestUserAddress)) {
|
|
|
|
return;
|
|
}
|
|
|
|
Vpn = (ULONG) MI_VA_TO_VPN (Base);
|
|
VpnRange = (ULONG) (MI_VA_TO_VPN (Ending4KAddress) - Vpn + 1);
|
|
|
|
//
|
|
// 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);
|
|
|
|
LastCommitPte = PointerPte + BYTES_TO_PAGES (CommitSize);
|
|
|
|
TempAltPte.u.Long = ProtectionMask;
|
|
TempAltPte.u.Alt.Protection = NewProtect;
|
|
|
|
//
|
|
// Initialize the bitmap inline for speed.
|
|
//
|
|
|
|
BitMap.SizeOfBitMap = (ULONG)((ULONG_PTR)MmWorkingSetList->HighestUserAddress >> PTI_SHIFT);
|
|
BitMap.Buffer = Wow64Process->AltPermBitmap;
|
|
|
|
|
|
LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
|
|
|
KeAcquireGuardedMutexUnsafe (&MmSectionCommitMutex);
|
|
|
|
//
|
|
// And then change all of the protections.
|
|
//
|
|
|
|
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.
|
|
//
|
|
|
|
MI_WRITE_ALTPTE (StartAltPte, TempAltPte, 0xD);
|
|
|
|
StartAltPte += 1;
|
|
|
|
if (((ULONG_PTR)StartAltPte & ((SPLITS_PER_PAGE * sizeof(MMPTE))-1)) == 0) {
|
|
PointerPte += 1;
|
|
}
|
|
}
|
|
|
|
ASSERT (TempAltPte.u.Long != 0);
|
|
|
|
KeReleaseGuardedMutexUnsafe (&MmSectionCommitMutex);
|
|
|
|
//
|
|
// Fill the empty Alt PTE as NoAccess ATE at the end.
|
|
//
|
|
|
|
EndAltPte += 1;
|
|
|
|
while (EndAltPte <= EndAltPte0) {
|
|
|
|
if (EndAltPte->u.Long == 0) {
|
|
|
|
TempAltPte.u.Long = EndAltPte->u.Long;
|
|
TempAltPte.u.Alt.NoAccess = 1;
|
|
|
|
//
|
|
// Atomic PTE size update.
|
|
//
|
|
|
|
MI_WRITE_ALTPTE (EndAltPte, TempAltPte, 0xE);
|
|
}
|
|
|
|
EndAltPte += 1;
|
|
}
|
|
|
|
RtlSetBits (&BitMap, Vpn, VpnRange);
|
|
|
|
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;
|
|
PVOID VirtualAddress;
|
|
PVOID OriginalStartVa;
|
|
PVOID OriginalEndVa;
|
|
ULONG i;
|
|
PWOW64_PROCESS Wow64Process;
|
|
PFN_NUMBER NumberOfAltPtes;
|
|
|
|
ASSERT (StartVirtual <= EndVirtual);
|
|
|
|
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 = StartVirtual;
|
|
|
|
ASSERT ((ULONG) ADDRESS_AND_SIZE_TO_SPAN_PAGES (StartVirtual,
|
|
(ULONG_PTR)EndVirtual -
|
|
(ULONG_PTR)StartVirtual) != 0);
|
|
|
|
LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
|
|
|
do {
|
|
MI_WRITE_ALTPTE (StartAltPte, TempAltPte, 0xF);
|
|
|
|
NumberOfAltPtes -= 1;
|
|
StartAltPte += 1;
|
|
|
|
} while (NumberOfAltPtes != 0);
|
|
|
|
while (VirtualAddress <= 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) {
|
|
MI_WRITE_ALTPTE (StartAltPte, ZeroPte, 0x10);
|
|
StartAltPte -= 1;
|
|
i -= 1;
|
|
}
|
|
break;
|
|
}
|
|
StartAltPte += 1;
|
|
}
|
|
|
|
VirtualAddress = (PVOID)((PCHAR) VirtualAddress + PAGE_SIZE);
|
|
}
|
|
|
|
MiResetAccessBitForNativePtes (StartVirtual, EndVirtual, Process);
|
|
|
|
//
|
|
// Mark the native released pages as non-split so they get 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 = (ULONG)((ULONG_PTR)MmWorkingSetList->HighestUserAddress >> 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));
|
|
}
|
|
|
|
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;
|
|
PWOW64_PROCESS Wow64Process;
|
|
|
|
Wow64Process = Process->Wow64Process;
|
|
|
|
ASSERT (StartVirtual <= EndVirtual);
|
|
|
|
StartAltPte = MiGetAltPteAddress (StartVirtual);
|
|
EndAltPte = MiGetAltPteAddress (EndVirtual);
|
|
|
|
ASSERT ((ULONG) ADDRESS_AND_SIZE_TO_SPAN_PAGES (StartVirtual,
|
|
(ULONG_PTR)EndVirtual -
|
|
(ULONG_PTR)StartVirtual) != 0);
|
|
|
|
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.
|
|
//
|
|
|
|
MI_WRITE_ALTPTE (StartAltPte, TempAltPte, 0x11);
|
|
|
|
StartAltPte += 1;
|
|
}
|
|
|
|
//
|
|
// Update the native PTEs and flush the relevant native TB entries.
|
|
//
|
|
|
|
MiResetAccessBitForNativePtes (StartVirtual, EndVirtual, Process);
|
|
|
|
UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
|
}
|
|
|
|
|
|
VOID
|
|
MiDeleteFor4kPage (
|
|
IN PVOID VirtualAddress,
|
|
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:
|
|
|
|
VirtualAddress - 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.
|
|
|
|
--*/
|
|
|
|
{
|
|
RTL_BITMAP BitMap;
|
|
PMMPTE EndAltPte;
|
|
PMMPTE StartAltPte;
|
|
PWOW64_PROCESS Wow64Process;
|
|
PFN_NUMBER NumberOfAltPtes;
|
|
ULONG Vpn;
|
|
ULONG VpnRange;
|
|
|
|
ASSERT (VirtualAddress <= EndVirtual);
|
|
|
|
StartAltPte = MiGetAltPteAddress (VirtualAddress);
|
|
EndAltPte = MiGetAltPteAddress (EndVirtual);
|
|
|
|
NumberOfAltPtes = EndAltPte - StartAltPte + 1;
|
|
|
|
ASSERT (ADDRESS_AND_SIZE_TO_SPAN_PAGES (VirtualAddress,
|
|
(ULONG_PTR)EndVirtual -
|
|
(ULONG_PTR)VirtualAddress) != 0);
|
|
|
|
Wow64Process = Process->Wow64Process;
|
|
|
|
Vpn = (ULONG) MI_VA_TO_VPN (VirtualAddress);
|
|
VpnRange = (ULONG) (MI_VA_TO_VPN (EndVirtual) - Vpn + 1);
|
|
|
|
//
|
|
// Initialize the bitmap inline for speed.
|
|
//
|
|
|
|
BitMap.SizeOfBitMap = (ULONG)((ULONG_PTR)MmWorkingSetList->HighestUserAddress >> PTI_SHIFT);
|
|
|
|
BitMap.Buffer = Wow64Process->AltPermBitmap;
|
|
|
|
LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
|
|
|
do {
|
|
|
|
MI_WRITE_ALTPTE (StartAltPte, ZeroPte, 0x12);
|
|
|
|
NumberOfAltPtes -= 1;
|
|
StartAltPte += 1;
|
|
|
|
} while (NumberOfAltPtes != 0);
|
|
|
|
RtlClearBits (&BitMap, Vpn, VpnRange);
|
|
|
|
//
|
|
// VirtualAddress and EndVirtual are already aligned to the native
|
|
// PAGE_SIZE so no need to readjust them before removing the split markers.
|
|
//
|
|
|
|
MiResetAccessBitForNativePtes (VirtualAddress, EndVirtual, Process);
|
|
|
|
UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
|
}
|
|
|
|
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 VirtualAddress,
|
|
IN PVOID EndVirtual,
|
|
IN PEPROCESS Process
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function resets the access bit of the native PTEs which have
|
|
corresponding initialized alternate PTEs.
|
|
|
|
This results in the next access to these VAs incurring a TB miss.
|
|
|
|
The miss will then be processed by the 4k TB miss handler (if the
|
|
cache reserved bit is set in the native PTE) and either handled
|
|
inline (if the alternate PTE allows it) or via a call to MmX86Fault.
|
|
|
|
If the cache reserved bit is *NOT* set in the native PTE (ie: the
|
|
native page does not contain split permissions), then the access
|
|
will still go to KiPageFault and then to MmX86Fault and then to
|
|
MmAccessFault for general processing to set the access bit.
|
|
|
|
Arguments:
|
|
|
|
VirtualAddress - 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.
|
|
|
|
Process - Supplies the pointer to the process.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Environment:
|
|
|
|
Alternate table mutex held at APC_LEVEL.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG NumberOfPtes;
|
|
MMPTE PteContents;
|
|
PMMPTE PointerPte;
|
|
PMMPTE PointerPde;
|
|
PMMPTE PointerPpe;
|
|
LOGICAL FirstTime;
|
|
ULONG Waited;
|
|
PULONG Bitmap;
|
|
PVOID Virtual[MM_MAXIMUM_FLUSH_COUNT];
|
|
|
|
NumberOfPtes = 0;
|
|
Bitmap = Process->Wow64Process->AltPermBitmap;
|
|
|
|
VirtualAddress = PAGE_ALIGN (VirtualAddress);
|
|
|
|
PointerPte = MiGetPteAddress (VirtualAddress);
|
|
|
|
FirstTime = TRUE;
|
|
|
|
LOCK_WS_UNSAFE (Process);
|
|
|
|
while (VirtualAddress <= EndVirtual) {
|
|
|
|
if ((FirstTime == TRUE) || MiIsPteOnPdeBoundary (PointerPte)) {
|
|
|
|
PointerPde = MiGetPteAddress (PointerPte);
|
|
PointerPpe = MiGetPdeAddress (PointerPte);
|
|
|
|
if (MiDoesPpeExistAndMakeValid (PointerPpe,
|
|
Process,
|
|
MM_NOIRQL,
|
|
&Waited) == FALSE) {
|
|
|
|
//
|
|
// This page directory parent entry is empty,
|
|
// go to the next one.
|
|
//
|
|
|
|
PointerPpe += 1;
|
|
PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
|
|
PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
|
|
VirtualAddress = MiGetVirtualAddressMappedByPte (PointerPte);
|
|
continue;
|
|
}
|
|
|
|
if (MiDoesPdeExistAndMakeValid (PointerPde,
|
|
Process,
|
|
MM_NOIRQL,
|
|
&Waited) == FALSE) {
|
|
|
|
|
|
//
|
|
// This page directory entry is empty,
|
|
// go to the next one.
|
|
//
|
|
|
|
PointerPde += 1;
|
|
PointerPte = MiGetVirtualAddressMappedByPte(PointerPde);
|
|
VirtualAddress = MiGetVirtualAddressMappedByPte(PointerPte);
|
|
continue;
|
|
}
|
|
|
|
FirstTime = FALSE;
|
|
|
|
}
|
|
|
|
PteContents = *PointerPte;
|
|
|
|
if (PteContents.u.Hard.Valid != 0) {
|
|
|
|
if ((PteContents.u.Hard.Accessed != 0) &&
|
|
(MI_CHECK_BIT (Bitmap, MI_VA_TO_VPN (VirtualAddress)))) {
|
|
|
|
PteContents.u.Hard.Accessed = 0;
|
|
|
|
MI_WRITE_VALID_PTE_NEW_PROTECTION (PointerPte, PteContents);
|
|
}
|
|
|
|
//
|
|
// Flush this valid native TB entry. Note this is done even if no
|
|
// changes were made to it above because our caller may not
|
|
// have flushed the native PTE if the starting 4k address was
|
|
// preceded by another valid 4k page within the same native page,
|
|
// or if the ending 4k address was followed by another valid 4k
|
|
// page within the same native page.
|
|
//
|
|
|
|
if (NumberOfPtes < MM_MAXIMUM_FLUSH_COUNT) {
|
|
Virtual[NumberOfPtes] = VirtualAddress;
|
|
NumberOfPtes += 1;
|
|
}
|
|
}
|
|
|
|
PointerPte += 1;
|
|
VirtualAddress = (PVOID)((ULONG_PTR)VirtualAddress + PAGE_SIZE);
|
|
}
|
|
|
|
UNLOCK_WS_UNSAFE (Process);
|
|
|
|
if (NumberOfPtes == 0) {
|
|
NOTHING;
|
|
}
|
|
else if (NumberOfPtes < MM_MAXIMUM_FLUSH_COUNT) {
|
|
KeFlushMultipleTb (NumberOfPtes, &Virtual[0], FALSE);
|
|
}
|
|
else {
|
|
KeFlushProcessTb (FALSE);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
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;
|
|
PMMPTE LastAltPte;
|
|
MMPTE AltContents;
|
|
PWOW64_PROCESS Wow64Process;
|
|
SIZE_T RegionSize4k;
|
|
|
|
//
|
|
// If above the Wow64 max address, just return.
|
|
//
|
|
|
|
if ((BaseAddress >= MmWorkingSetList->HighestUserAddress) ||
|
|
(EndAddress >= MmWorkingSetList->HighestUserAddress)) {
|
|
|
|
return;
|
|
}
|
|
|
|
AltPte = MiGetAltPteAddress (BaseAddress);
|
|
LastAltPte = MiGetAltPteAddress (EndAddress);
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
AltPte += 1;
|
|
RegionSize4k = PAGE_4K;
|
|
|
|
while (AltPte <= LastAltPte) {
|
|
|
|
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, bail.
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
RegionSize4k += PAGE_4K;
|
|
AltPte += 1;
|
|
}
|
|
|
|
UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
|
|
|
*RegionSize = RegionSize4k;
|
|
}
|
|
|
|
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(HighestUserAddress) \
|
|
((((((ULONG_PTR)HighestUserAddress) >> PAGE_4K_SHIFT) * sizeof (MMPTE)) >> PAGE_SHIFT) + 1)
|
|
|
|
|
|
NTSTATUS
|
|
MiInitializeAlternateTable (
|
|
IN PEPROCESS Process,
|
|
IN PVOID HighestUserAddress
|
|
)
|
|
|
|
/*++
|
|
|
|
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.
|
|
|
|
HighestUserAddress - Supplies the highest 32-bit user address for this
|
|
process.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
Environment:
|
|
|
|
--*/
|
|
|
|
{
|
|
PULONG AltTablePointer;
|
|
PWOW64_PROCESS Wow64Process;
|
|
SIZE_T AltPteCharge;
|
|
SIZE_T NumberOfBytes;
|
|
|
|
//
|
|
// Charge commitment now for the alternate PTE table pages as they will
|
|
// need to be dynamically created later at fault time.
|
|
//
|
|
// Add X64K to include alternate PTEs for the guard region.
|
|
//
|
|
|
|
HighestUserAddress = (PVOID)((PCHAR)HighestUserAddress + X64K);
|
|
|
|
AltPteCharge = MI_ALTERNATE_PAGE_TABLE_CHARGE (HighestUserAddress);
|
|
|
|
if (MiChargeCommitment (AltPteCharge, NULL) == FALSE) {
|
|
return STATUS_COMMITMENT_LIMIT;
|
|
}
|
|
|
|
NumberOfBytes = ((ULONG_PTR)HighestUserAddress >> PTI_SHIFT) / 8;
|
|
|
|
NumberOfBytes += MI_ALTPTE_TRACKING_BYTES;
|
|
|
|
AltTablePointer = (PULONG) ExAllocatePoolWithTag (NonPagedPool,
|
|
NumberOfBytes,
|
|
'AlmM');
|
|
|
|
if (AltTablePointer == NULL) {
|
|
MiReturnCommitment (AltPteCharge);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
RtlZeroMemory (AltTablePointer, NumberOfBytes);
|
|
|
|
Wow64Process = Process->Wow64Process;
|
|
|
|
Wow64Process->AltPermBitmap = AltTablePointer;
|
|
|
|
KeInitializeGuardedMutex (&Wow64Process->AlternateTableLock);
|
|
|
|
MmWorkingSetList->HighestUserPte = MiGetPteAddress (HighestUserAddress);
|
|
MmWorkingSetList->HighestAltPte = MiGetAltPteAddress (HighestUserAddress);
|
|
|
|
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;
|
|
PVOID Va;
|
|
ULONG i;
|
|
ULONG j;
|
|
ULONG Waited;
|
|
|
|
//
|
|
// 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,
|
|
((ULONG_PTR)MmWorkingSetList->HighestUserAddress >> 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 = ALT4KB_PERMISSION_TABLE_START;
|
|
|
|
do {
|
|
|
|
if (MiDoesPdeExistAndMakeValid (PointerPde,
|
|
CurrentProcess,
|
|
MM_NOIRQL,
|
|
&Waited) == TRUE) {
|
|
|
|
//
|
|
// 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,
|
|
MM_NOIRQL,
|
|
&Waited) == TRUE) {
|
|
|
|
ASSERT (PointerPte->u.Hard.Valid == 1);
|
|
|
|
Source = KSEG_ADDRESS (PointerPte->u.Hard.PageFrameNumber);
|
|
|
|
KeStackAttachProcess (&ProcessToInitialize->Pcb,
|
|
&ApcState);
|
|
|
|
RtlCopyMemory (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);
|
|
}
|
|
}
|
|
|
|
Va = (PVOID)((PCHAR) Va + PAGE_SIZE);
|
|
PointerPte += 1;
|
|
}
|
|
}
|
|
|
|
PointerPde += 1;
|
|
PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
|
|
Va = MiGetVirtualAddressMappedByPte (PointerPte);
|
|
|
|
} while (Va < ALT4KB_PERMISSION_TABLE_END);
|
|
|
|
//
|
|
// 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.
|
|
|
|
--*/
|
|
|
|
{
|
|
PVOID HighestUserAddress;
|
|
PMMPTE PointerPte;
|
|
PMMPTE PointerPde;
|
|
PVOID Va;
|
|
PVOID 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().
|
|
//
|
|
|
|
Va = ALT4KB_PERMISSION_TABLE_START;
|
|
PointerPte = MiGetPteAddress (ALT4KB_PERMISSION_TABLE_START);
|
|
PointerPde = MiGetPdeAddress (ALT4KB_PERMISSION_TABLE_START);
|
|
|
|
PteFlushList.Count = 0;
|
|
|
|
do {
|
|
|
|
if (MiDoesPdeExistAndMakeValid (PointerPde,
|
|
Process,
|
|
MM_NOIRQL,
|
|
&Waited) == TRUE) {
|
|
|
|
//
|
|
// Delete the PTE entries mapping the Alternate Table.
|
|
//
|
|
|
|
TempVa = Va;
|
|
|
|
LOCK_PFN (OldIrql);
|
|
|
|
for (i = 0; i < PTE_PER_PAGE; i += 1) {
|
|
|
|
if (PointerPte->u.Long != 0) {
|
|
|
|
if (IS_PTE_NOT_DEMAND_ZERO (*PointerPte)) {
|
|
|
|
MiDeletePte (PointerPte,
|
|
TempVa,
|
|
TRUE,
|
|
Process,
|
|
NULL,
|
|
&PteFlushList,
|
|
OldIrql);
|
|
}
|
|
else {
|
|
|
|
MI_WRITE_INVALID_PTE (PointerPte, ZeroPte);
|
|
}
|
|
|
|
}
|
|
|
|
TempVa = (PVOID)((PCHAR)TempVa + PAGE_SIZE);
|
|
PointerPte += 1;
|
|
}
|
|
|
|
//
|
|
// Delete the PDE entry mapping the Alternate Table.
|
|
//
|
|
|
|
TempVa = MiGetVirtualAddressMappedByPte (PointerPde);
|
|
|
|
MiDeletePte (PointerPde,
|
|
TempVa,
|
|
TRUE,
|
|
Process,
|
|
NULL,
|
|
&PteFlushList,
|
|
OldIrql);
|
|
|
|
MiFlushPteList (&PteFlushList, FALSE);
|
|
|
|
UNLOCK_PFN (OldIrql);
|
|
}
|
|
|
|
PointerPde += 1;
|
|
PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
|
|
Va = MiGetVirtualAddressMappedByPte (PointerPte);
|
|
|
|
} while (Va < ALT4KB_PERMISSION_TABLE_END);
|
|
|
|
HighestUserAddress = MmWorkingSetList->HighestUserAddress;
|
|
|
|
ASSERT (HighestUserAddress != NULL);
|
|
|
|
//
|
|
// Add X64K to include alternate PTEs for the guard region.
|
|
//
|
|
|
|
HighestUserAddress = (PVOID)((PCHAR)HighestUserAddress + X64K);
|
|
|
|
MiReturnCommitment (MI_ALTERNATE_PAGE_TABLE_CHARGE (HighestUserAddress));
|
|
|
|
ExFreePool (Wow64Process->AltPermBitmap);
|
|
|
|
Wow64Process->AltPermBitmap = NULL;
|
|
|
|
return;
|
|
}
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG_PTR Vpn;
|
|
PALIAS_VAD_INFO2 AliasBase;
|
|
HANDLE Handle;
|
|
PMMVAD VadParent;
|
|
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, TRUE);
|
|
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);
|
|
|
|
VadParent = (PMMVAD) SANITIZE_PARENT_NODE (Vad->u1.Parent);
|
|
ASSERT (VadParent != NULL);
|
|
|
|
if (VadParent != Vad) {
|
|
if (VadParent->RightChild == Vad) {
|
|
VadParent->RightChild = (PMMVAD) NewVad;
|
|
}
|
|
else {
|
|
ASSERT (VadParent->LeftChild == Vad);
|
|
VadParent->LeftChild = (PMMVAD) NewVad;
|
|
}
|
|
}
|
|
else {
|
|
Process->VadRoot.BalancedRoot.RightChild = (PMMADDRESS_NODE) NewVad;
|
|
}
|
|
if (Vad->LeftChild) {
|
|
Vad->LeftChild->u1.Parent = (PMMVAD) MI_MAKE_PARENT (NewVad, Vad->LeftChild->u1.Balance);
|
|
}
|
|
if (Vad->RightChild) {
|
|
Vad->RightChild->u1.Parent = (PMMVAD) MI_MAKE_PARENT (NewVad, Vad->RightChild->u1.Balance);
|
|
}
|
|
if (Process->VadRoot.NodeHint == Vad) {
|
|
Process->VadRoot.NodeHint = (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) {
|
|
|
|
//
|
|
// Check to see if the protection is registered in the alternate entry.
|
|
//
|
|
|
|
Vpn = (ULONG) MI_VA_TO_VPN (Va);
|
|
|
|
if (MI_CHECK_BIT (Wow64Process->AltPermBitmap, Vpn) == 0) {
|
|
MiSyncAltPte (Va);
|
|
}
|
|
|
|
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;
|
|
|
|
MI_WRITE_ALTPTE (AltPte, AltPteContents, 0x13);
|
|
|
|
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;
|
|
MMPTE AltPteContents;
|
|
|
|
Wow64Process = Process->Wow64Process;
|
|
|
|
EndingAddress = (PVOID)((ULONG_PTR)CapturedBase + CapturedRegionSize - 1);
|
|
|
|
StartAltPte = MiGetAltPteAddress(CapturedBase);
|
|
EndAltPte = MiGetAltPteAddress(EndingAddress);
|
|
|
|
LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
|
|
|
while (StartAltPte <= EndAltPte) {
|
|
|
|
AltPteContents = *StartAltPte;
|
|
AltPteContents.u.Alt.Lock = 1;
|
|
|
|
MI_WRITE_ALTPTE (StartAltPte, AltPteContents, 0x14);
|
|
|
|
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 PointerAltPte;
|
|
PMMPTE StartAltPte;
|
|
PMMPTE EndAltPte;
|
|
PWOW64_PROCESS Wow64Process;
|
|
PVOID EndingAddress;
|
|
NTSTATUS Status;
|
|
MMPTE AltPteContents;
|
|
|
|
UNLOCK_WS_UNSAFE (Process);
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
Wow64Process = Process->Wow64Process;
|
|
|
|
EndingAddress = (PVOID)((ULONG_PTR)CapturedBase + CapturedRegionSize - 1);
|
|
|
|
StartAltPte = MiGetAltPteAddress (CapturedBase);
|
|
EndAltPte = MiGetAltPteAddress (EndingAddress);
|
|
|
|
PointerAltPte = StartAltPte;
|
|
|
|
LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
|
|
|
|
do {
|
|
|
|
if (PointerAltPte->u.Alt.Lock == 0) {
|
|
Status = STATUS_NOT_LOCKED;
|
|
goto StatusReturn;
|
|
|
|
}
|
|
|
|
PointerAltPte += 1;
|
|
|
|
} while (PointerAltPte <= EndAltPte);
|
|
|
|
PointerAltPte = StartAltPte;
|
|
|
|
do {
|
|
AltPteContents = *PointerAltPte;
|
|
AltPteContents.u.Alt.Lock = 0;
|
|
|
|
MI_WRITE_ALTPTE (PointerAltPte, AltPteContents, 0x15);
|
|
|
|
PointerAltPte += 1;
|
|
|
|
} while (PointerAltPte <= EndAltPte);
|
|
|
|
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 page 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;
|
|
}
|
|
|
|
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
|
|
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,
|
|
MM_NOIRQL,
|
|
&Waited) == FALSE) {
|
|
|
|
UNLOCK_WS (CurrentProcess);
|
|
return;
|
|
}
|
|
|
|
if (MiDoesPdeExistAndMakeValid (PointerPde,
|
|
CurrentProcess,
|
|
MM_NOIRQL,
|
|
&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
|