3333 lines
116 KiB
C
3333 lines
116 KiB
C
/*++
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
mapview.c
|
|
|
|
Abstract:
|
|
This module contains the routines which implement the NtMapViewOfSection service.
|
|
|
|
Author:
|
|
Lou Perazzoli (loup) 22-May-1989
|
|
Landy Wang (landyw) 02-June-1997
|
|
--*/
|
|
|
|
#include "mi.h"
|
|
|
|
ULONG MMPPTE_NAME = 'tPmM'; //MmPt
|
|
ULONG MMDB = 'bDmM';
|
|
extern ULONG MMVADKEY;
|
|
|
|
VOID MiSetPageModified(IN PVOID Address);
|
|
|
|
extern LIST_ENTRY MmLoadedUserImageList;
|
|
|
|
#define X256MEG (256*1024*1024)
|
|
|
|
#if DBG
|
|
extern PEPROCESS MmWatchProcess;
|
|
#endif // DBG
|
|
|
|
#define ROUND_TO_PAGES64(Size) (((UINT64)(Size) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))
|
|
|
|
MMSESSION MmSession;
|
|
|
|
NTSTATUS
|
|
MiMapViewOfImageSection(
|
|
IN PCONTROL_AREA ControlArea,
|
|
IN PEPROCESS Process,
|
|
IN PVOID *CapturedBase,
|
|
IN PLARGE_INTEGER SectionOffset,
|
|
IN PSIZE_T CapturedViewSize,
|
|
IN PSECTION Section,
|
|
IN SECTION_INHERIT InheritDisposition,
|
|
IN ULONG_PTR ZeroBits,
|
|
IN SIZE_T ImageCommitment,
|
|
OUT PBOOLEAN ReleasedWsMutex
|
|
);
|
|
|
|
NTSTATUS
|
|
MiMapViewOfDataSection(
|
|
IN PCONTROL_AREA ControlArea,
|
|
IN PEPROCESS Process,
|
|
IN PVOID *CapturedBase,
|
|
IN PLARGE_INTEGER SectionOffset,
|
|
IN PSIZE_T CapturedViewSize,
|
|
IN PSECTION Section,
|
|
IN SECTION_INHERIT InheritDisposition,
|
|
IN ULONG ProtectionMask,
|
|
IN SIZE_T CommitSize,
|
|
IN ULONG_PTR ZeroBits,
|
|
IN ULONG AllocationType,
|
|
OUT PBOOLEAN ReleasedWsMutex
|
|
);
|
|
|
|
VOID MiRemoveMappedPtes(IN PVOID BaseAddress, IN ULONG NumberOfPtes, IN PCONTROL_AREA ControlArea, IN PMMSUPPORT WorkingSetInfo);
|
|
NTSTATUS MiMapViewInSystemSpace(IN PVOID Section, IN PMMSESSION Session, OUT PVOID *MappedBase, IN OUT PSIZE_T ViewSize);
|
|
NTSTATUS MiUnmapViewInSystemSpace(IN PMMSESSION Session, IN PVOID MappedBase);
|
|
BOOLEAN MiFillSystemPageDirectory(PVOID Base, SIZE_T NumberOfBytes);
|
|
VOID VadTreeWalk(PMMVAD Start);
|
|
|
|
|
|
#if DBG
|
|
VOID MiDumpConflictingVad(IN PVOID StartingAddress, IN PVOID EndingAddress, IN PMMVAD Vad);
|
|
|
|
|
|
VOID MiDumpConflictingVad(IN PVOID StartingAddress, IN PVOID EndingAddress, IN PMMVAD Vad)
|
|
{
|
|
if (NtGlobalFlag & FLG_SHOW_LDR_SNAPS) {
|
|
DbgPrint("MM: [%p ... %p) conflicted with Vad %p\n", StartingAddress, EndingAddress, Vad);
|
|
if ((Vad->u.VadFlags.PrivateMemory == 1) || (Vad->ControlArea == NULL)) {
|
|
return;
|
|
}
|
|
if (Vad->ControlArea->u.Flags.Image)
|
|
DbgPrint(" conflict with %Z image at [%p .. %p)\n", &Vad->ControlArea->FilePointer->FileName, MI_VPN_TO_VA(Vad->StartingVpn), MI_VPN_TO_VA_ENDING(Vad->EndingVpn));
|
|
else
|
|
if (Vad->ControlArea->u.Flags.File)
|
|
DbgPrint(" conflict with %Z file at [%p .. %p)\n", &Vad->ControlArea->FilePointer->FileName, MI_VPN_TO_VA(Vad->StartingVpn), MI_VPN_TO_VA_ENDING(Vad->EndingVpn));
|
|
else
|
|
DbgPrint(" conflict with section at [%p .. %p)\n", MI_VPN_TO_VA(Vad->StartingVpn), MI_VPN_TO_VA_ENDING(Vad->EndingVpn));
|
|
}
|
|
}
|
|
#endif //DBG
|
|
|
|
|
|
ULONG CacheImageSymbols(IN PVOID ImageBase);
|
|
PVOID MiInsertInSystemSpace(IN PMMSESSION Session, IN ULONG SizeIn64k, IN PCONTROL_AREA ControlArea);
|
|
ULONG MiRemoveFromSystemSpace(IN PMMSESSION Session, IN PVOID Base, OUT PCONTROL_AREA *ControlArea);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE,NtMapViewOfSection)
|
|
#pragma alloc_text(PAGE,MmMapViewOfSection)
|
|
#pragma alloc_text(PAGE,MmSecureVirtualMemory)
|
|
#pragma alloc_text(PAGE,MmUnsecureVirtualMemory)
|
|
#pragma alloc_text(PAGE,CacheImageSymbols)
|
|
#pragma alloc_text(PAGE,NtAreMappedFilesTheSame)
|
|
|
|
#pragma alloc_text(PAGELK,MiMapViewOfPhysicalSection)
|
|
#pragma alloc_text(PAGELK,MiMapViewInSystemSpace)
|
|
#pragma alloc_text(PAGELK,MmMapViewInSystemSpace)
|
|
#pragma alloc_text(PAGELK,MmMapViewInSessionSpace)
|
|
#pragma alloc_text(PAGELK,MiUnmapViewInSystemSpace)
|
|
#pragma alloc_text(PAGELK,MmUnmapViewInSystemSpace)
|
|
#pragma alloc_text(PAGELK,MmUnmapViewInSessionSpace)
|
|
#pragma alloc_text(PAGELK,MiFillSystemPageDirectory)
|
|
#pragma alloc_text(PAGELK,MiInsertInSystemSpace)
|
|
#pragma alloc_text(PAGELK,MiRemoveFromSystemSpace)
|
|
|
|
#pragma alloc_text(PAGEHYDRA,MiInitializeSystemSpaceMap)
|
|
#pragma alloc_text(PAGEHYDRA, MiFreeSessionSpaceMap)
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
NtMapViewOfSection(
|
|
IN HANDLE SectionHandle,
|
|
IN HANDLE ProcessHandle,
|
|
IN OUT PVOID *BaseAddress,
|
|
IN ULONG_PTR ZeroBits,
|
|
IN SIZE_T CommitSize,
|
|
IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
|
|
IN OUT PSIZE_T ViewSize,
|
|
IN SECTION_INHERIT InheritDisposition,
|
|
IN ULONG AllocationType,
|
|
IN ULONG Protect
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This function maps a view in the specified subject process to the section object.
|
|
Arguments:
|
|
SectionHandle - Supplies an open handle to a section object.
|
|
ProcessHandle - Supplies an open handle to a process object.
|
|
BaseAddress - Supplies a pointer to a variable that will receive
|
|
the base address of the view. If the initial value
|
|
of this argument is not null, then the view will
|
|
be allocated starting at the specified virtual
|
|
address rounded down to the next 64kb address
|
|
boundary. If the initial value of this argument is
|
|
null, then the operating system will determine
|
|
where to allocate the view using the information
|
|
specified by the ZeroBits argument value and the
|
|
section allocation attributes (i.e. based and
|
|
tiled).
|
|
ZeroBits - Supplies the number of high order address bits that
|
|
must be zero in the base address of the section
|
|
view. The value of this argument must be less than
|
|
or equal to the maximum number of zero bits and is only
|
|
used when memory management determines where to allocate
|
|
the view (i.e. when BaseAddress is null).
|
|
|
|
If ZeroBits is zero, then no zero bit constraints are applied.
|
|
|
|
If ZeroBits is greater than 0 and less than 32, then it is
|
|
the number of leading zero bits from bit 31. Bits 63:32 are
|
|
also required to be zero. This retains compatibility with 32-bit systems.
|
|
|
|
If ZeroBits is greater than 32, then it is considered as
|
|
a mask and then number of leading zero are counted out
|
|
in the mask. This then becomes the zero bits argument.
|
|
|
|
CommitSize - Supplies the size of the initially committed region
|
|
of the view in bytes. This value is rounded up to
|
|
the next host page size boundary.
|
|
|
|
SectionOffset - Supplies the offset from the beginning of the
|
|
section to the view in bytes. This value is
|
|
rounded down to the next host page size boundary.
|
|
|
|
ViewSize - Supplies a pointer to a variable that will receive
|
|
the actual size in bytes of the view. If the value
|
|
of this argument is zero, then a view of the
|
|
section will be mapped starting at the specified
|
|
section offset and continuing to the end of the
|
|
section. Otherwise the initial value of this
|
|
argument specifies the size of the view in bytes
|
|
and is rounded up to the next host page size
|
|
boundary.
|
|
|
|
InheritDisposition - Supplies a value that specifies how the
|
|
view is to be shared by a child process created
|
|
with a create process operation.
|
|
|
|
InheritDisposition Values
|
|
|
|
ViewShare - Inherit view and share a single copy
|
|
of the committed pages with a child process
|
|
using the current protection value.
|
|
|
|
ViewUnmap - Do not map the view into a child process.
|
|
|
|
AllocationType - Supplies the type of allocation.
|
|
|
|
MEM_TOP_DOWN
|
|
MEM_DOS_LIM
|
|
MEM_LARGE_PAGES
|
|
MEM_RESERVE - for file mapped sections only.
|
|
|
|
Protect - Supplies the protection desired for the region of
|
|
initially committed pages.
|
|
|
|
Protect Values
|
|
|
|
|
|
PAGE_NOACCESS - No access to the committed region
|
|
of pages is allowed. An attempt to read,
|
|
write, or execute the committed region
|
|
results in an access violation (i.e. a GP
|
|
fault).
|
|
|
|
PAGE_EXECUTE - Execute access to the committed
|
|
region of pages is allowed. An attempt to
|
|
read or write the committed region results in
|
|
an access violation.
|
|
|
|
PAGE_READONLY - Read only and execute access to the
|
|
committed region of pages is allowed. An
|
|
attempt to write the committed region results
|
|
in an access violation.
|
|
|
|
PAGE_READWRITE - Read, write, and execute access to
|
|
the region of committed pages is allowed. If
|
|
write access to the underlying section is
|
|
allowed, then a single copy of the pages are
|
|
shared. Otherwise the pages are shared read
|
|
only/copy on write.
|
|
|
|
Return Value:
|
|
Various NTSTATUS codes.
|
|
--*/
|
|
{
|
|
PSECTION Section;
|
|
PEPROCESS Process;
|
|
KPROCESSOR_MODE PreviousMode;
|
|
NTSTATUS Status;
|
|
PVOID CapturedBase;
|
|
SIZE_T CapturedViewSize;
|
|
LARGE_INTEGER TempViewSize;
|
|
LARGE_INTEGER CapturedOffset;
|
|
ULONGLONG HighestPhysicalAddressInPfnDatabase;
|
|
ACCESS_MASK DesiredSectionAccess;
|
|
ULONG ProtectMaskForAccess;
|
|
BOOLEAN WriteCombined;
|
|
|
|
PAGED_CODE();
|
|
|
|
// Check the zero bits argument for correctness.
|
|
|
|
#if defined (_WIN64)
|
|
if (ZeroBits >= 32) {
|
|
// ZeroBits is a mask instead of a count. Translate it to a count now.
|
|
ZeroBits = 64 - RtlFindMostSignificantBit(ZeroBits);
|
|
} else if (ZeroBits) {
|
|
ZeroBits += 32;
|
|
}
|
|
#endif
|
|
|
|
if (ZeroBits > MM_MAXIMUM_ZERO_BITS) {
|
|
return STATUS_INVALID_PARAMETER_4;
|
|
}
|
|
|
|
// Check the inherit disposition flags.
|
|
if ((InheritDisposition > ViewUnmap) || (InheritDisposition < ViewShare)) {
|
|
return STATUS_INVALID_PARAMETER_8;
|
|
}
|
|
|
|
// Check the allocation type field.
|
|
|
|
#ifdef i386
|
|
// Only allow DOS_LIM support for i386. The MEM_DOS_LIM flag allows
|
|
// map views of data sections to be done on 4k boundaries rather than 64k boundaries.
|
|
if ((AllocationType & ~(MEM_TOP_DOWN | MEM_LARGE_PAGES | MEM_DOS_LIM | SEC_NO_CHANGE | MEM_RESERVE)) != 0) {
|
|
return STATUS_INVALID_PARAMETER_9;
|
|
}
|
|
#else
|
|
if ((AllocationType & ~(MEM_TOP_DOWN | MEM_LARGE_PAGES | SEC_NO_CHANGE | MEM_RESERVE)) != 0) {
|
|
return STATUS_INVALID_PARAMETER_9;
|
|
}
|
|
#endif //i386
|
|
|
|
// Check the protection field. This could raise an exception.
|
|
if (Protect & PAGE_WRITECOMBINE) {
|
|
Protect &= ~PAGE_WRITECOMBINE;
|
|
WriteCombined = TRUE;
|
|
} else {
|
|
WriteCombined = FALSE;
|
|
}
|
|
|
|
try {
|
|
ProtectMaskForAccess = MiMakeProtectionMask(Protect) & 0x7;
|
|
} except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
DesiredSectionAccess = MmMakeSectionAccess[ProtectMaskForAccess];
|
|
|
|
PreviousMode = KeGetPreviousMode();
|
|
|
|
// Establish an exception handler, probe the specified addresses for write access and capture the initial values.
|
|
try {
|
|
if (PreviousMode != KernelMode) {
|
|
ProbeForWritePointer((PULONG)BaseAddress);
|
|
ProbeForWriteUlong_ptr(ViewSize);
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(SectionOffset)) {
|
|
if (PreviousMode != KernelMode) {
|
|
ProbeForWrite(SectionOffset, sizeof(LARGE_INTEGER), sizeof(ULONG_PTR));
|
|
}
|
|
CapturedOffset = *SectionOffset;
|
|
} else {
|
|
ZERO_LARGE(CapturedOffset);
|
|
}
|
|
|
|
CapturedBase = *BaseAddress;// Capture the base address.
|
|
CapturedViewSize = *ViewSize;// Capture the region size.
|
|
} except(ExSystemExceptionFilter())
|
|
{
|
|
// If an exception occurs during the probe or capture of the initial values,
|
|
// then handle the exception and return the exception code as the status value.
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
#if DBG
|
|
if (MmDebug & MM_DBG_SHOW_NT_CALLS) {
|
|
if (!MmWatchProcess) {
|
|
DbgPrint("mapview process handle %lx section %lx base address %p zero bits %lx\n", ProcessHandle, SectionHandle, CapturedBase, ZeroBits);
|
|
DbgPrint(" view size %p offset %p commitsize %p protect %lx\n", CapturedViewSize, CapturedOffset, CommitSize, Protect);
|
|
DbgPrint(" Inheritdisp %lx Allocation type %lx\n", InheritDisposition, AllocationType);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
// Make sure the specified starting and ending addresses are
|
|
// within the user part of the virtual address space.
|
|
if (CapturedBase > MM_HIGHEST_VAD_ADDRESS) {
|
|
// Invalid base address.
|
|
return STATUS_INVALID_PARAMETER_3;
|
|
}
|
|
|
|
if (((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - (ULONG_PTR)CapturedBase) < CapturedViewSize) {
|
|
return STATUS_INVALID_PARAMETER_3;// Invalid region size;
|
|
}
|
|
|
|
if (((ULONG_PTR)CapturedBase + CapturedViewSize) > ((ULONG_PTR)MM_USER_ADDRESS_RANGE_LIMIT >> ZeroBits)) {
|
|
return STATUS_INVALID_PARAMETER_4;// Desired Base and zero_bits conflict.
|
|
}
|
|
|
|
Status = ObReferenceObjectByHandle(ProcessHandle, PROCESS_VM_OPERATION, PsProcessType, PreviousMode, (PVOID *)&Process, NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
// Reference the section object, if a view is mapped to the section
|
|
// object, the object is not dereferenced as the virtual address
|
|
// descriptor contains a pointer to the section object.
|
|
Status = ObReferenceObjectByHandle(SectionHandle, DesiredSectionAccess, MmSectionObjectType, PreviousMode, (PVOID *)&Section, NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto ErrorReturn1;
|
|
}
|
|
|
|
if (Section->u.Flags.Image == 0) {
|
|
// This is not an image section, make sure the section page protection is compatible with the specified page protection.
|
|
if (!MiIsProtectionCompatible(Section->InitialPageProtection, Protect)) {
|
|
Status = STATUS_SECTION_PROTECTION;
|
|
goto ErrorReturn;
|
|
}
|
|
}
|
|
|
|
// Check to see if this the section backs physical memory, if
|
|
// so DON'T align the offset on a 64K boundary, just a 4k boundary.
|
|
if (Section->Segment->ControlArea->u.Flags.PhysicalMemory) {
|
|
HighestPhysicalAddressInPfnDatabase = (ULONGLONG)MmHighestPhysicalPage << PAGE_SHIFT;
|
|
CapturedOffset.LowPart = CapturedOffset.LowPart & ~(PAGE_SIZE - 1);
|
|
|
|
// No usermode mappings past the end of the PFN database are allowed.
|
|
// Address wrap is checked in the common path.
|
|
if (PreviousMode != KernelMode) {
|
|
if ((ULONGLONG)(CapturedOffset.QuadPart + CapturedViewSize) > HighestPhysicalAddressInPfnDatabase) {
|
|
Status = STATUS_INVALID_PARAMETER_6;
|
|
goto ErrorReturn;
|
|
}
|
|
}
|
|
} else {
|
|
// Make sure alignments are correct for specified address
|
|
// and offset into the file.
|
|
if ((AllocationType & MEM_DOS_LIM) == 0) {
|
|
if (((ULONG_PTR)CapturedBase & (X64K - 1)) != 0) {
|
|
Status = STATUS_MAPPED_ALIGNMENT;
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
if ((ARGUMENT_PRESENT(SectionOffset)) && ((CapturedOffset.LowPart & (X64K - 1)) != 0)) {
|
|
Status = STATUS_MAPPED_ALIGNMENT;
|
|
goto ErrorReturn;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check to make sure the view size plus the offset is less
|
|
// than the size of the section.
|
|
if ((ULONGLONG)(CapturedOffset.QuadPart + CapturedViewSize) < (ULONGLONG)CapturedOffset.QuadPart) {
|
|
Status = STATUS_INVALID_VIEW_SIZE;
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
if (((ULONGLONG)(CapturedOffset.QuadPart + CapturedViewSize) > (ULONGLONG)Section->SizeOfSection.QuadPart) &&
|
|
((AllocationType & MEM_RESERVE) == 0)) {
|
|
Status = STATUS_INVALID_VIEW_SIZE;
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
if (CapturedViewSize == 0) {
|
|
// Set the view size to be size of the section less the offset.
|
|
TempViewSize.QuadPart = Section->SizeOfSection.QuadPart - CapturedOffset.QuadPart;
|
|
CapturedViewSize = (SIZE_T)TempViewSize.QuadPart;
|
|
if (
|
|
#if !defined(_WIN64)
|
|
(TempViewSize.HighPart != 0) ||
|
|
#endif
|
|
(((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - (ULONG_PTR)CapturedBase) < CapturedViewSize)) {
|
|
// Invalid region size;
|
|
Status = STATUS_INVALID_VIEW_SIZE;
|
|
goto ErrorReturn;
|
|
}
|
|
}
|
|
|
|
// Check commit size.
|
|
if ((CommitSize > CapturedViewSize) && ((AllocationType & MEM_RESERVE) == 0)) {
|
|
Status = STATUS_INVALID_PARAMETER_5;
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
if (WriteCombined == TRUE) {
|
|
Protect |= PAGE_WRITECOMBINE;
|
|
}
|
|
|
|
Status = MmMapViewOfSection((PVOID)Section,
|
|
Process,
|
|
&CapturedBase,
|
|
ZeroBits,
|
|
CommitSize,
|
|
&CapturedOffset,
|
|
&CapturedViewSize,
|
|
InheritDisposition,
|
|
AllocationType,
|
|
Protect);
|
|
if (!NT_SUCCESS(Status)) {
|
|
if ((Section->Segment->ControlArea->u.Flags.Image) && Process == PsGetCurrentProcess()) {
|
|
if (Status == STATUS_CONFLICTING_ADDRESSES) {
|
|
DbgkMapViewOfSection(SectionHandle, CapturedBase, CapturedOffset.LowPart, CapturedViewSize);
|
|
}
|
|
}
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
// Anytime the current process maps an image file,
|
|
// a potential debug event occurs. DbgkMapViewOfSection
|
|
// handles these events.
|
|
if ((Section->Segment->ControlArea->u.Flags.Image) && Process == PsGetCurrentProcess()) {
|
|
if (Status != STATUS_IMAGE_NOT_AT_BASE) {
|
|
DbgkMapViewOfSection(SectionHandle, CapturedBase, CapturedOffset.LowPart, CapturedViewSize);
|
|
}
|
|
}
|
|
|
|
// Establish an exception handler and write the size and base address.
|
|
try {
|
|
*ViewSize = CapturedViewSize;
|
|
*BaseAddress = CapturedBase;
|
|
|
|
if (ARGUMENT_PRESENT(SectionOffset)) {
|
|
*SectionOffset = CapturedOffset;
|
|
}
|
|
} except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
#if 0 // test code...
|
|
if ((Status == STATUS_SUCCESS) && (Section->u.Flags.Image == 0)) {
|
|
PVOID Base;
|
|
ULONG Size = 0;
|
|
NTSTATUS Status;
|
|
|
|
Status = MmMapViewInSystemSpace((PVOID)Section, &Base, &Size);
|
|
if (Status == STATUS_SUCCESS) {
|
|
MmUnmapViewInSystemSpace(Base);
|
|
}
|
|
}
|
|
#endif //0
|
|
|
|
{
|
|
ErrorReturn:
|
|
ObDereferenceObject(Section);
|
|
ErrorReturn1:
|
|
ObDereferenceObject(Process);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
MmMapViewOfSection(
|
|
IN PVOID SectionToMap,
|
|
IN PEPROCESS Process,
|
|
IN OUT PVOID *CapturedBase,
|
|
IN ULONG_PTR ZeroBits,
|
|
IN SIZE_T CommitSize,
|
|
IN OUT PLARGE_INTEGER SectionOffset,
|
|
IN OUT PSIZE_T CapturedViewSize,
|
|
IN SECTION_INHERIT InheritDisposition,
|
|
IN ULONG AllocationType,
|
|
IN ULONG Protect
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This function maps a view in the specified subject process to the section object.
|
|
This function is a kernel mode interface to allow LPC to map a section given the section pointer to map.
|
|
This routine assumes all arguments have been probed and captured.
|
|
NOTE: CapturedViewSize, SectionOffset, and CapturedBase must be captured in non-paged system space (i.e., kernel stack).
|
|
Arguments:
|
|
SectionToMap - Supplies a pointer to the section object.
|
|
Process - Supplies a pointer to the process object.
|
|
BaseAddress - Supplies a pointer to a variable that will receive
|
|
the base address of the view. If the initial value
|
|
of this argument is not null, then the view will
|
|
be allocated starting at the specified virtual
|
|
address rounded down to the next 64kb address
|
|
boundary. If the initial value of this argument is
|
|
null, then the operating system will determine
|
|
where to allocate the view using the information
|
|
specified by the ZeroBits argument value and the
|
|
section allocation attributes (i.e. based and
|
|
tiled).
|
|
ZeroBits - Supplies the number of high order address bits that
|
|
must be zero in the base address of the section
|
|
view. The value of this argument must be less than
|
|
21 and is only used when the operating system
|
|
determines where to allocate the view (i.e. when
|
|
BaseAddress is null).
|
|
CommitSize - Supplies the size of the initially committed region
|
|
of the view in bytes. This value is rounded up to
|
|
the next host page size boundary.
|
|
SectionOffset - Supplies the offset from the beginning of the
|
|
section to the view in bytes. This value is
|
|
rounded down to the next host page size boundary.
|
|
ViewSize - Supplies a pointer to a variable that will receive
|
|
the actual size in bytes of the view. If the value
|
|
of this argument is zero, then a view of the
|
|
section will be mapped starting at the specified
|
|
section offset and continuing to the end of the
|
|
section. Otherwise the initial value of this
|
|
argument specifies the size of the view in bytes
|
|
and is rounded up to the next host page size
|
|
boundary.
|
|
InheritDisposition - Supplies a value that specifies how the
|
|
view is to be shared by a child process created
|
|
with a create process operation.
|
|
AllocationType - Supplies the type of allocation.
|
|
Protect - Supplies the protection desired for the region of initially committed pages.
|
|
Return Value:
|
|
Returns the status
|
|
TBS
|
|
--*/
|
|
{
|
|
BOOLEAN Attached;
|
|
PSECTION Section;
|
|
PCONTROL_AREA ControlArea;
|
|
ULONG ProtectionMask;
|
|
NTSTATUS status;
|
|
BOOLEAN ReleasedWsMutex;
|
|
BOOLEAN WriteCombined;
|
|
SIZE_T ImageCommitment;
|
|
|
|
PAGED_CODE();
|
|
|
|
Attached = FALSE;
|
|
ReleasedWsMutex = TRUE;
|
|
Section = (PSECTION)SectionToMap;
|
|
|
|
// Check to make sure the section is not smaller than the view size.
|
|
if ((LONGLONG)*CapturedViewSize > Section->SizeOfSection.QuadPart) {
|
|
if ((AllocationType & MEM_RESERVE) == 0) {
|
|
return STATUS_INVALID_VIEW_SIZE;
|
|
}
|
|
}
|
|
|
|
if (AllocationType & MEM_RESERVE) {
|
|
if (((Section->InitialPageProtection & PAGE_READWRITE) |
|
|
(Section->InitialPageProtection & PAGE_EXECUTE_READWRITE)) == 0) {
|
|
return STATUS_SECTION_PROTECTION;
|
|
}
|
|
}
|
|
|
|
if (Section->u.Flags.NoCache) {
|
|
Protect |= PAGE_NOCACHE;
|
|
}
|
|
|
|
// Note that write combining is only relevant to physical memory sections
|
|
// because they are never trimmed - the write combining bits in a PTE entry
|
|
// are not preserved across trims.
|
|
if (Protect & PAGE_WRITECOMBINE) {
|
|
Protect &= ~PAGE_WRITECOMBINE;
|
|
WriteCombined = TRUE;
|
|
} else {
|
|
WriteCombined = FALSE;
|
|
}
|
|
|
|
// Check the protection field. This could raise an exception.
|
|
try {
|
|
ProtectionMask = MiMakeProtectionMask(Protect);
|
|
} except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
ControlArea = Section->Segment->ControlArea;
|
|
ImageCommitment = Section->Segment->ImageCommitment;
|
|
|
|
// If the specified process is not the current process, attach to the specified process.
|
|
if (PsGetCurrentProcess() != Process) {
|
|
KeAttachProcess(&Process->Pcb);
|
|
Attached = TRUE;
|
|
}
|
|
|
|
// Get the address creation mutex to block multiple threads
|
|
// creating or deleting address space at the same time.
|
|
LOCK_ADDRESS_SPACE(Process);
|
|
|
|
// Make sure the address space was not deleted, if so, return an error.
|
|
if (Process->AddressSpaceDeleted != 0) {
|
|
status = STATUS_PROCESS_IS_TERMINATING;
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
// Map the view base on the type.
|
|
ReleasedWsMutex = FALSE;
|
|
|
|
if (ControlArea->u.Flags.PhysicalMemory) {
|
|
MmLockPagableSectionByHandle(ExPageLockHandle);
|
|
status = MiMapViewOfPhysicalSection(ControlArea,
|
|
Process,
|
|
CapturedBase,
|
|
SectionOffset,
|
|
CapturedViewSize,
|
|
ProtectionMask,
|
|
ZeroBits,
|
|
AllocationType,
|
|
WriteCombined,
|
|
&ReleasedWsMutex);
|
|
MmUnlockPagableImageSection(ExPageLockHandle);
|
|
} else if (ControlArea->u.Flags.Image) {
|
|
if (AllocationType & MEM_RESERVE) {
|
|
status = STATUS_INVALID_PARAMETER_9;
|
|
} else if (WriteCombined == TRUE) {
|
|
status = STATUS_INVALID_PARAMETER_10;
|
|
} else {
|
|
status = MiMapViewOfImageSection(ControlArea,
|
|
Process,
|
|
CapturedBase,
|
|
SectionOffset,
|
|
CapturedViewSize,
|
|
Section,
|
|
InheritDisposition,
|
|
ZeroBits,
|
|
ImageCommitment,
|
|
&ReleasedWsMutex);
|
|
}
|
|
} else {
|
|
// Not an image section, therefore it is a data section.
|
|
if (WriteCombined == TRUE) {
|
|
status = STATUS_INVALID_PARAMETER_10;
|
|
} else {
|
|
status = MiMapViewOfDataSection(ControlArea,
|
|
Process,
|
|
CapturedBase,
|
|
SectionOffset,
|
|
CapturedViewSize,
|
|
Section,
|
|
InheritDisposition,
|
|
ProtectionMask,
|
|
CommitSize,
|
|
ZeroBits,
|
|
AllocationType,
|
|
&ReleasedWsMutex);
|
|
}
|
|
}
|
|
|
|
ErrorReturn:
|
|
if (!ReleasedWsMutex) {
|
|
UNLOCK_WS_AND_ADDRESS_SPACE(Process);
|
|
} else {
|
|
UNLOCK_ADDRESS_SPACE(Process);
|
|
}
|
|
|
|
if (Attached) {
|
|
KeDetachProcess();
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
#ifndef _ALPHA_
|
|
|
|
|
|
NTSTATUS
|
|
MiMapViewOfPhysicalSection(
|
|
IN PCONTROL_AREA ControlArea,
|
|
IN PEPROCESS Process,
|
|
IN PVOID *CapturedBase,
|
|
IN PLARGE_INTEGER SectionOffset,
|
|
IN PSIZE_T CapturedViewSize,
|
|
IN ULONG ProtectionMask,
|
|
IN ULONG_PTR ZeroBits,
|
|
IN ULONG AllocationType,
|
|
IN BOOLEAN WriteCombined,
|
|
OUT PBOOLEAN ReleasedWsMutex
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine maps the specified physical section into the specified process's address space.
|
|
Arguments:
|
|
see MmMapViewOfSection above...
|
|
ControlArea - Supplies the control area for the section.
|
|
Process - Supplies the process pointer which is receiving the section.
|
|
ProtectionMask - Supplies the initial page protection-mask.
|
|
ReleasedWsMutex - Supplies FALSE. If the working set mutex is
|
|
not held when returning this must be set to TRUE
|
|
so the caller will not release the unheld mutex.
|
|
|
|
Note if this routine acquires the working set mutex
|
|
it is always done unsafely as the address space mutex
|
|
must be held on entry regardless.
|
|
Return Value:
|
|
Status of the map view operation.
|
|
Environment:
|
|
Kernel Mode, address creation mutex held.
|
|
--*/
|
|
{
|
|
PMMVAD Vad;
|
|
PVOID StartingAddress;
|
|
PVOID EndingAddress;
|
|
KIRQL OldIrql;
|
|
KIRQL OldIrql2;
|
|
PMMPTE PointerPpe;
|
|
PMMPTE PointerPde;
|
|
PMMPTE PointerPte;
|
|
PMMPTE LastPte;
|
|
MMPTE TempPte;
|
|
PMMPFN Pfn2;
|
|
SIZE_T PhysicalViewSize;
|
|
ULONG_PTR Alignment;
|
|
PVOID UsedPageTableHandle;
|
|
PVOID UsedPageDirectoryHandle;
|
|
PMI_PHYSICAL_VIEW PhysicalView;
|
|
#ifdef LARGE_PAGES
|
|
ULONG size;
|
|
PMMPTE protoPte;
|
|
ULONG pageSize;
|
|
PSUBSECTION Subsection;
|
|
ULONG EmPageSize;
|
|
#endif //LARGE_PAGES
|
|
|
|
// Physical memory section.
|
|
|
|
// If running on an R4000 and MEM_LARGE_PAGES is specified,
|
|
// set up the PTEs as a series of pointers to the same
|
|
// prototype PTE. This prototype PTE will reference a subsection
|
|
// that indicates large pages should be used.
|
|
|
|
// The R4000 supports pages of 4k, 16k, 64k, etc (powers of 4).
|
|
// Since the TB supports 2 entries, sizes of 8k, 32k etc can
|
|
// be mapped by 2 LargePages in a single TB entry. These 2 entries
|
|
// are maintained in the subsection structure pointed to by the
|
|
// prototype PTE.
|
|
|
|
if (AllocationType & MEM_RESERVE) {
|
|
*ReleasedWsMutex = TRUE;
|
|
return STATUS_INVALID_PARAMETER_9;
|
|
}
|
|
Alignment = X64K;
|
|
LOCK_WS_UNSAFE(Process);
|
|
|
|
#ifdef LARGE_PAGES
|
|
if (AllocationType & MEM_LARGE_PAGES) {
|
|
// Determine the page size and the required alignment.
|
|
if ((SectionOffset->LowPart & (X64K - 1)) != 0) {
|
|
return STATUS_INVALID_PARAMETER_9;
|
|
}
|
|
|
|
size = (*CapturedViewSize - 1) >> (PAGE_SHIFT + 1);
|
|
pageSize = PAGE_SIZE;
|
|
|
|
while (size != 0) {
|
|
size = size >> 2;
|
|
pageSize = pageSize << 2;
|
|
}
|
|
|
|
Alignment = pageSize << 1;
|
|
if (Alignment < MM_VA_MAPPED_BY_PDE) {
|
|
Alignment = MM_VA_MAPPED_BY_PDE;
|
|
}
|
|
|
|
#if defined(_IA64_)
|
|
// Convert pageSize to the EM page-size field format.
|
|
EmPageSize = 0;
|
|
size = pageSize - 1;
|
|
|
|
while (size) {
|
|
size = size >> 1;
|
|
EmPageSize += 1;
|
|
}
|
|
|
|
if (*CapturedViewSize > pageSize) {
|
|
if (MmPageSizeInfo & (pageSize << 1)) {
|
|
// if larger page size is supported in the implementation
|
|
pageSize = pageSize << 1;
|
|
EmPageSize += 1;
|
|
} else {
|
|
EmPageSize = EmPageSize | pageSize;
|
|
}
|
|
}
|
|
|
|
pageSize = EmPageSize;
|
|
#endif
|
|
|
|
}
|
|
#endif //LARGE_PAGES
|
|
|
|
if (*CapturedBase == NULL) {
|
|
// Attempt to locate address space. This could raise an exception.
|
|
try {
|
|
// Find a starting address on a 64k boundary.
|
|
|
|
#ifdef i386
|
|
ASSERT(SectionOffset->HighPart == 0);
|
|
#endif
|
|
|
|
#ifdef LARGE_PAGES
|
|
if (AllocationType & MEM_LARGE_PAGES) {
|
|
PhysicalViewSize = Alignment;
|
|
} else {
|
|
#endif //LARGE_PAGES
|
|
|
|
PhysicalViewSize = *CapturedViewSize + (SectionOffset->LowPart & (X64K - 1));
|
|
#ifdef LARGE_PAGES
|
|
}
|
|
#endif //LARGE_PAGES
|
|
|
|
StartingAddress = MiFindEmptyAddressRange(PhysicalViewSize, Alignment, (ULONG)ZeroBits);
|
|
} except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
EndingAddress = (PVOID)(((ULONG_PTR)StartingAddress + PhysicalViewSize - 1L) | (PAGE_SIZE - 1L));
|
|
StartingAddress = (PVOID)((ULONG_PTR)StartingAddress + (SectionOffset->LowPart & (X64K - 1)));
|
|
|
|
if (ZeroBits > 0) {
|
|
if (EndingAddress > (PVOID)((ULONG_PTR)MM_USER_ADDRESS_RANGE_LIMIT >> ZeroBits)) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
} else {
|
|
// Check to make sure the specified base address to ending address is currently unused.
|
|
StartingAddress = (PVOID)((ULONG_PTR)MI_64K_ALIGN(*CapturedBase) + (SectionOffset->LowPart & (X64K - 1)));
|
|
EndingAddress = (PVOID)(((ULONG_PTR)StartingAddress + *CapturedViewSize - 1L) | (PAGE_SIZE - 1L));
|
|
|
|
#ifdef LARGE_PAGES
|
|
if (AllocationType & MEM_LARGE_PAGES) {
|
|
if (((ULONG_PTR)StartingAddress & (Alignment - 1)) != 0) {
|
|
return STATUS_CONFLICTING_ADDRESSES;
|
|
}
|
|
EndingAddress = (PVOID)((PCHAR)StartingAddress + Alignment);
|
|
}
|
|
#endif //LARGE_PAGES
|
|
|
|
Vad = MiCheckForConflictingVad(StartingAddress, EndingAddress);
|
|
if (Vad != (PMMVAD)NULL) {
|
|
#if DBG
|
|
MiDumpConflictingVad(StartingAddress, EndingAddress, Vad);
|
|
#endif
|
|
return STATUS_CONFLICTING_ADDRESSES;
|
|
}
|
|
}
|
|
|
|
// An unoccupied address range has been found, build the virtual address descriptor to describe this range.
|
|
#ifdef LARGE_PAGES
|
|
if (AllocationType & MEM_LARGE_PAGES) {
|
|
// Allocate a subsection and 4 prototype PTEs to hold the information for the large pages.
|
|
Subsection = ExAllocatePoolWithTag(NonPagedPool, sizeof(SUBSECTION) + (4 * sizeof(MMPTE)), MMPPTE_NAME);
|
|
if (Subsection == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
#endif //LARGE_PAGES
|
|
|
|
// Establish an exception handler and attempt to allocate
|
|
// the pool and charge quota. Note that the InsertVad routine
|
|
// will also charge quota which could raise an exception.
|
|
try {
|
|
PhysicalView = (PMI_PHYSICAL_VIEW)ExAllocatePoolWithTag(NonPagedPool, sizeof(MI_PHYSICAL_VIEW), MI_PHYSICAL_VIEW_KEY);
|
|
if (PhysicalView == NULL) {
|
|
ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
Vad = (PMMVAD)ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD), MMVADKEY);
|
|
if (Vad == NULL) {
|
|
ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
PhysicalView->Vad = Vad;
|
|
PhysicalView->StartVa = StartingAddress;
|
|
PhysicalView->EndVa = EndingAddress;
|
|
|
|
RtlZeroMemory(Vad, sizeof(MMVAD));
|
|
Vad->StartingVpn = MI_VA_TO_VPN(StartingAddress);
|
|
Vad->EndingVpn = MI_VA_TO_VPN(EndingAddress);
|
|
Vad->ControlArea = ControlArea;
|
|
Vad->u2.VadFlags2.Inherit = MM_VIEW_UNMAP;
|
|
Vad->u.VadFlags.PhysicalMapping = 1;
|
|
Vad->u.VadFlags.Protection = ProtectionMask;
|
|
|
|
// Set the last contiguous PTE field in the Vad to the page frame number of the starting physical page.
|
|
Vad->LastContiguousPte = (PMMPTE)(ULONG)(SectionOffset->QuadPart >> PAGE_SHIFT);
|
|
#ifdef LARGE_PAGES
|
|
if (AllocationType & MEM_LARGE_PAGES) {
|
|
Vad->u.VadFlags.LargePages = 1;
|
|
Vad->FirstPrototypePte = (PMMPTE)Subsection;
|
|
} else {
|
|
#endif //LARGE_PAGES
|
|
// Vad->u.VadFlags.LargePages = 0;
|
|
Vad->FirstPrototypePte = Vad->LastContiguousPte;
|
|
#ifdef LARGE_PAGES
|
|
}
|
|
#endif //LARGE_PAGES
|
|
|
|
// Insert the VAD. This could get an exception.
|
|
ASSERT(Vad->FirstPrototypePte <= Vad->LastContiguousPte);
|
|
MiInsertVad(Vad);
|
|
} except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
if (PhysicalView != NULL) {
|
|
ExFreePool(PhysicalView);
|
|
}
|
|
|
|
if (Vad != (PMMVAD)NULL) {
|
|
// The pool allocation succeeded, but the quota charge
|
|
// in InsertVad failed, deallocate the pool and return an error.
|
|
|
|
ExFreePool(Vad);
|
|
#ifdef LARGE_PAGES
|
|
if (AllocationType & MEM_LARGE_PAGES) {
|
|
ExFreePool(Subsection);
|
|
}
|
|
#endif //LARGE_PAGES
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
// Increment the count of the number of views for the
|
|
// section object. This requires the PFN lock to be held.
|
|
|
|
LOCK_AWE(Process, OldIrql);
|
|
LOCK_PFN2(OldIrql2);
|
|
|
|
InsertHeadList(&Process->PhysicalVadList, &PhysicalView->ListEntry);
|
|
|
|
ControlArea->NumberOfMappedViews += 1;
|
|
ControlArea->NumberOfUserReferences += 1;
|
|
|
|
ASSERT(ControlArea->NumberOfSectionReferences != 0);
|
|
|
|
UNLOCK_PFN2(OldIrql2);
|
|
UNLOCK_AWE(Process, OldIrql);
|
|
|
|
// Build the PTEs in the address space.
|
|
|
|
PointerPpe = MiGetPpeAddress(StartingAddress);
|
|
PointerPde = MiGetPdeAddress(StartingAddress);
|
|
PointerPte = MiGetPteAddress(StartingAddress);
|
|
LastPte = MiGetPteAddress(EndingAddress);
|
|
|
|
MI_MAKE_VALID_PTE(TempPte, (ULONG_PTR)Vad->LastContiguousPte, ProtectionMask, PointerPte);
|
|
|
|
if (WriteCombined == TRUE) {
|
|
MI_SET_PTE_WRITE_COMBINE(TempPte);
|
|
}
|
|
|
|
if (TempPte.u.Hard.Write) {
|
|
MI_SET_PTE_DIRTY(TempPte);
|
|
}
|
|
|
|
#if defined(_IA64_)
|
|
if (MI_IS_CACHING_DISABLED(&TempPte) || (WriteCombined == TRUE)) {
|
|
KeFlushEntireTb(FALSE, TRUE);
|
|
}
|
|
#endif
|
|
|
|
#ifdef LARGE_PAGES
|
|
if (AllocationType & MEM_LARGE_PAGES) {
|
|
Subsection->StartingSector = pageSize;
|
|
Subsection->EndingSector = (ULONG_PTR)StartingAddress;
|
|
Subsection->u.LongFlags = 0;
|
|
Subsection->u.SubsectionFlags.LargePages = 1;
|
|
protoPte = (PMMPTE)(Subsection + 1);
|
|
|
|
// Build the first 2 PTEs as entries for the TLB to
|
|
// map the specified physical address.
|
|
|
|
*protoPte = TempPte;
|
|
protoPte += 1;
|
|
|
|
if (*CapturedViewSize > pageSize) {
|
|
*protoPte = TempPte;
|
|
protoPte->u.Hard.PageFrameNumber += (pageSize >> PAGE_SHIFT);
|
|
} else {
|
|
*protoPte = ZeroPte;
|
|
}
|
|
protoPte += 1;
|
|
|
|
// Build the first prototype PTE as a paging file format PTE referring to the subsection.
|
|
protoPte->u.Long = MiGetSubsectionAddressForPte(Subsection);
|
|
protoPte->u.Soft.Prototype = 1;
|
|
protoPte->u.Soft.Protection = ProtectionMask;
|
|
|
|
// Set the PTE up for all the user's PTE entries, proto pte format pointing to the 3rd prototype PTE.
|
|
TempPte.u.Long = MiProtoAddressForPte(protoPte);
|
|
}
|
|
|
|
if (!(AllocationType & MEM_LARGE_PAGES)) {
|
|
#endif //LARGE_PAGES
|
|
|
|
#if defined (_WIN64)
|
|
MiMakePpeExistAndMakeValid(PointerPpe, Process, FALSE);
|
|
if (PointerPde->u.Long == 0) {
|
|
UsedPageDirectoryHandle = MI_GET_USED_PTES_HANDLE(PointerPte);
|
|
MI_INCREMENT_USED_PTES_BY_HANDLE(UsedPageDirectoryHandle);
|
|
}
|
|
#endif
|
|
|
|
MiMakePdeExistAndMakeValid(PointerPde, Process, FALSE);
|
|
Pfn2 = MI_PFN_ELEMENT(PointerPde->u.Hard.PageFrameNumber);
|
|
UsedPageTableHandle = MI_GET_USED_PTES_HANDLE(StartingAddress);
|
|
while (PointerPte <= LastPte) {
|
|
if (MiIsPteOnPdeBoundary(PointerPte)) {
|
|
PointerPde = MiGetPteAddress(PointerPte);
|
|
if (MiIsPteOnPpeBoundary(PointerPte)) {
|
|
PointerPpe = MiGetPteAddress(PointerPde);
|
|
MiMakePpeExistAndMakeValid(PointerPpe, Process, FALSE);
|
|
}
|
|
|
|
#if defined (_WIN64)
|
|
if (PointerPde->u.Long == 0) {
|
|
UsedPageDirectoryHandle = MI_GET_USED_PTES_HANDLE(PointerPte);
|
|
MI_INCREMENT_USED_PTES_BY_HANDLE(UsedPageDirectoryHandle);
|
|
}
|
|
#endif
|
|
|
|
MiMakePdeExistAndMakeValid(PointerPde, Process, FALSE);
|
|
Pfn2 = MI_PFN_ELEMENT(PointerPde->u.Hard.PageFrameNumber);
|
|
UsedPageTableHandle = MI_GET_USED_PTES_HANDLE(MiGetVirtualAddressMappedByPte(PointerPte));
|
|
}
|
|
|
|
ASSERT(PointerPte->u.Long == 0);
|
|
MI_WRITE_VALID_PTE(PointerPte, TempPte);
|
|
CONSISTENCY_LOCK_PFN(OldIrql);
|
|
Pfn2->u2.ShareCount += 1;
|
|
CONSISTENCY_UNLOCK_PFN(OldIrql);
|
|
|
|
// Increment the count of non-zero page table entries for this
|
|
// page table and the number of private pages for the process.
|
|
|
|
MI_INCREMENT_USED_PTES_BY_HANDLE(UsedPageTableHandle);
|
|
|
|
PointerPte += 1;
|
|
TempPte.u.Hard.PageFrameNumber += 1;
|
|
}
|
|
#ifdef LARGE_PAGES
|
|
}
|
|
#endif //LARGE_PAGES
|
|
|
|
#if defined(i386)
|
|
|
|
// If write combined was specified then flush all caches and TBs.
|
|
if (WriteCombined == TRUE && MiWriteCombiningPtes == TRUE) {
|
|
KeFlushEntireTb(FALSE, TRUE);
|
|
KeInvalidateAllCaches(TRUE);
|
|
}
|
|
#endif
|
|
|
|
#if defined(_IA64_)
|
|
if (MI_IS_CACHING_DISABLED(&TempPte) || (WriteCombined == TRUE)) {
|
|
MiSweepCacheMachineDependent(StartingAddress,
|
|
(PCHAR)EndingAddress - (PCHAR)StartingAddress + 1L,
|
|
MmWriteCombined);
|
|
}
|
|
#endif
|
|
|
|
UNLOCK_WS_UNSAFE(Process);
|
|
*ReleasedWsMutex = TRUE;
|
|
|
|
// Update the current virtual size in the process header.
|
|
|
|
*CapturedViewSize = (PCHAR)EndingAddress - (PCHAR)StartingAddress + 1L;
|
|
Process->VirtualSize += *CapturedViewSize;
|
|
|
|
if (Process->VirtualSize > Process->PeakVirtualSize) {
|
|
Process->PeakVirtualSize = Process->VirtualSize;
|
|
}
|
|
|
|
*CapturedBase = StartingAddress;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
#endif //!_ALPHA_
|
|
|
|
|
|
NTSTATUS
|
|
MiMapViewOfImageSection(
|
|
IN PCONTROL_AREA ControlArea,
|
|
IN PEPROCESS Process,
|
|
IN PVOID *CapturedBase,
|
|
IN PLARGE_INTEGER SectionOffset,
|
|
IN PSIZE_T CapturedViewSize,
|
|
IN PSECTION Section,
|
|
IN SECTION_INHERIT InheritDisposition,
|
|
IN ULONG_PTR ZeroBits,
|
|
IN SIZE_T ImageCommitment,
|
|
IN OUT PBOOLEAN ReleasedWsMutex
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine maps the specified Image section into the specified process's address space.
|
|
Arguments:
|
|
see MmMapViewOfSection above...
|
|
ControlArea - Supplies the control area for the section.
|
|
Process - Supplies the process pointer which is receiving the section.
|
|
ReleasedWsMutex - Supplies FALSE. If the working set mutex is
|
|
not held when returning this must be set to TRUE
|
|
so the caller will not release the unheld mutex.
|
|
|
|
Note if this routine acquires the working set mutex
|
|
it is always done unsafely as the address space mutex
|
|
must be held on entry regardless.
|
|
Return Value:
|
|
Status of the map view operation.
|
|
Environment:
|
|
Kernel Mode, address creation mutex held.
|
|
--*/
|
|
{
|
|
PMMVAD Vad;
|
|
PVOID StartingAddress;
|
|
PVOID EndingAddress;
|
|
BOOLEAN Attached;
|
|
KIRQL OldIrql;
|
|
PSUBSECTION Subsection;
|
|
ULONG PteOffset;
|
|
NTSTATUS ReturnedStatus;
|
|
PMMPTE ProtoPte;
|
|
PVOID BasedAddress;
|
|
SIZE_T NeededViewSize;
|
|
#if defined(_ALPHA_)
|
|
ULONG_PTR Starting2gb;
|
|
ULONG_PTR Ending2gb;
|
|
ULONG_PTR BoundaryMask;
|
|
LOGICAL AlignmentOk;
|
|
#endif
|
|
#if defined(_MIALT4K_)
|
|
PMMPTE PointerAltPte;
|
|
PMMPTE ProtoAltPte;
|
|
MMPTE TempAltPte;
|
|
PIMAGE_NT_HEADERS NtHeaders;
|
|
ULONG ImageAlignment;
|
|
#endif
|
|
|
|
Attached = FALSE;
|
|
|
|
// Image file.
|
|
|
|
// Locate the first subsection (text) and create a virtual address descriptor to map the entire image here.
|
|
if (ControlArea->u.Flags.GlobalOnlyPerSession == 0) {
|
|
Subsection = (PSUBSECTION)(ControlArea + 1);
|
|
} else {
|
|
Subsection = (PSUBSECTION)((PLARGE_CONTROL_AREA)ControlArea + 1);
|
|
}
|
|
|
|
if (ControlArea->u.Flags.ImageMappedInSystemSpace) {
|
|
if (KeGetPreviousMode() != KernelMode) {
|
|
// Mapping in system space as a driver, hence copy on write does
|
|
// not work. Don't allow user processes to map the image.
|
|
*ReleasedWsMutex = TRUE;
|
|
return STATUS_CONFLICTING_ADDRESSES;
|
|
}
|
|
}
|
|
|
|
// Check to see if a purge operation is in progress and if so, wait
|
|
// for the purge to complete. In addition, up the count of mapped
|
|
// views for this control area.
|
|
if (MiCheckPurgeAndUpMapCount(ControlArea) == FALSE) {
|
|
*ReleasedWsMutex = TRUE;
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
// Capture the based address to the stack, to prevent page faults.
|
|
BasedAddress = ControlArea->Segment->BasedAddress;
|
|
if (*CapturedViewSize == 0) {
|
|
*CapturedViewSize = (ULONG_PTR)(Section->SizeOfSection.QuadPart - SectionOffset->QuadPart);
|
|
}
|
|
|
|
LOCK_WS_UNSAFE(Process);
|
|
|
|
ReturnedStatus = STATUS_SUCCESS;
|
|
|
|
// Determine if a specific base was specified.
|
|
if (*CapturedBase != NULL) {
|
|
// Captured base is not NULL.
|
|
|
|
// Check to make sure the specified address range is currently unused and within the user address space.
|
|
Vad = (PMMVAD)1;
|
|
StartingAddress = MI_64K_ALIGN(*CapturedBase);
|
|
EndingAddress = (PVOID)(((ULONG_PTR)StartingAddress + *CapturedViewSize - 1) | (PAGE_SIZE - 1));
|
|
|
|
if ((StartingAddress <= MM_HIGHEST_VAD_ADDRESS) &&
|
|
(((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) - (ULONG_PTR)StartingAddress >= *CapturedViewSize) &&
|
|
(EndingAddress <= MM_HIGHEST_VAD_ADDRESS)) {
|
|
Vad = MiCheckForConflictingVad(StartingAddress, EndingAddress);
|
|
}
|
|
|
|
if (Vad != NULL) {
|
|
#if DBG
|
|
if (Vad != (PMMVAD)1) {
|
|
MiDumpConflictingVad(StartingAddress, EndingAddress, Vad);
|
|
}
|
|
#endif
|
|
|
|
LOCK_PFN(OldIrql);
|
|
ControlArea->NumberOfMappedViews -= 1;
|
|
ControlArea->NumberOfUserReferences -= 1;
|
|
|
|
// Check to see if the control area (segment) should be deleted.
|
|
// This routine releases the PFN lock.
|
|
MiCheckControlArea(ControlArea, Process, OldIrql);
|
|
return STATUS_CONFLICTING_ADDRESSES;
|
|
}
|
|
|
|
// A conflicting VAD was not found and the specified address range is
|
|
// within the user address space. If the image will not reside at its
|
|
// base address, then set a special return status.
|
|
if (((ULONG_PTR)StartingAddress + (ULONG_PTR)MI_64K_ALIGN(SectionOffset->LowPart)) != (ULONG_PTR)BasedAddress) {
|
|
ReturnedStatus = STATUS_IMAGE_NOT_AT_BASE;
|
|
}
|
|
} else {
|
|
// Captured base is NULL.
|
|
|
|
// If the captured view size is greater than the largest size that
|
|
// can fit in the user address space, then it is not possible to map the image.
|
|
if ((PVOID)*CapturedViewSize > MM_HIGHEST_VAD_ADDRESS) {
|
|
LOCK_PFN(OldIrql);
|
|
ControlArea->NumberOfMappedViews -= 1;
|
|
ControlArea->NumberOfUserReferences -= 1;
|
|
|
|
// Check to see if the control area (segment) should be deleted.
|
|
// This routine releases the PFN lock.
|
|
MiCheckControlArea(ControlArea, Process, OldIrql);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
// Check to make sure the specified address range is currently unused and within the user address space.
|
|
StartingAddress = (PVOID)((ULONG_PTR)BasedAddress + (ULONG_PTR)MI_64K_ALIGN(SectionOffset->LowPart));
|
|
EndingAddress = (PVOID)(((ULONG_PTR)StartingAddress + *CapturedViewSize - 1) | (PAGE_SIZE - 1));
|
|
|
|
Vad = (PMMVAD)1;
|
|
NeededViewSize = *CapturedViewSize;
|
|
|
|
#if defined(_ALPHA_)
|
|
|
|
#if defined(_AXP64_)
|
|
#define BOUNDARY ((LONG_PTR)_2gb)
|
|
#else
|
|
#define BOUNDARY ((ULONG_PTR)_2gb)
|
|
#endif
|
|
|
|
// Alpha images cannot span 2GB boundaries with the current compiler
|
|
// generated branch instructions. If a spanning image is detected, it must be relocated.
|
|
|
|
BoundaryMask = (ULONG_PTR)~(BOUNDARY - 1);
|
|
|
|
Starting2gb = (ULONG_PTR)StartingAddress & BoundaryMask;
|
|
Ending2gb = (ULONG_PTR)EndingAddress & BoundaryMask;
|
|
|
|
AlignmentOk = TRUE;
|
|
|
|
if (Starting2gb != Ending2gb) {
|
|
AlignmentOk = FALSE;
|
|
if (Starting2gb + 1 == Ending2gb) {
|
|
if (((ULONG_PTR)EndingAddress & (BOUNDARY - 1)) == 0) {
|
|
AlignmentOk = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (AlignmentOk == FALSE) {
|
|
// Find twice the required size so that it can be placed correctly.
|
|
NeededViewSize = *CapturedViewSize * 2 + X64K;
|
|
} else
|
|
#endif
|
|
|
|
if ((StartingAddress >= MM_LOWEST_USER_ADDRESS) &&
|
|
(StartingAddress <= MM_HIGHEST_VAD_ADDRESS) &&
|
|
(((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) - (ULONG_PTR)StartingAddress >= *CapturedViewSize) &&
|
|
(EndingAddress <= MM_HIGHEST_VAD_ADDRESS)) {
|
|
Vad = MiCheckForConflictingVad(StartingAddress, EndingAddress);
|
|
}
|
|
|
|
// If the VAD address is not NULL, then a conflict was discovered.
|
|
// Attempt to select another address range in which to map the image.
|
|
if (Vad != (PMMVAD)NULL) {
|
|
// The image could not be mapped at its natural base address
|
|
// try to find another place to map it.
|
|
|
|
#if DBG
|
|
if (Vad != (PMMVAD)1) {
|
|
MiDumpConflictingVad(StartingAddress, EndingAddress, Vad);
|
|
}
|
|
#endif
|
|
|
|
// If the system has been biased to an alternate base address to
|
|
// allow 3gb of user address space, then make sure the high order address bit is zero.
|
|
#if defined(_X86_)
|
|
if ((MmVirtualBias != 0) && (ZeroBits == 0)) {
|
|
ZeroBits = 1;
|
|
}
|
|
#endif
|
|
|
|
ReturnedStatus = STATUS_IMAGE_NOT_AT_BASE;
|
|
try {
|
|
// Find a starting address on a 64k boundary.
|
|
StartingAddress = MiFindEmptyAddressRange(NeededViewSize, X64K, (ULONG)ZeroBits);
|
|
} except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
LOCK_PFN(OldIrql);
|
|
ControlArea->NumberOfMappedViews -= 1;
|
|
ControlArea->NumberOfUserReferences -= 1;
|
|
|
|
// Check to see if the control area (segment) should be deleted.
|
|
// This routine releases the PFN lock.
|
|
MiCheckControlArea(ControlArea, Process, OldIrql);
|
|
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
EndingAddress = (PVOID)(((ULONG_PTR)StartingAddress + *CapturedViewSize - 1) | (PAGE_SIZE - 1));
|
|
|
|
#if defined(_ALPHA_)
|
|
|
|
if (AlignmentOk == FALSE) {
|
|
Starting2gb = (ULONG_PTR)StartingAddress & ~(BOUNDARY - 1);
|
|
Ending2gb = (ULONG_PTR)EndingAddress & ~(BOUNDARY - 1);
|
|
|
|
if (Starting2gb != Ending2gb) {
|
|
// Not in the same 2gb. Up the start to a 2gb boundary.
|
|
StartingAddress = (PVOID)Ending2gb;
|
|
EndingAddress = (PVOID)(((ULONG_PTR)StartingAddress + *CapturedViewSize - 1) | (PAGE_SIZE - 1));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Allocate and initialize a VAD for the specified address range.
|
|
Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD), MMVADKEY);
|
|
try {
|
|
if (Vad == NULL) {
|
|
ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
RtlZeroMemory(Vad, sizeof(MMVAD));
|
|
Vad->StartingVpn = MI_VA_TO_VPN(StartingAddress);
|
|
Vad->EndingVpn = MI_VA_TO_VPN(EndingAddress);
|
|
Vad->u2.VadFlags2.Inherit = (InheritDisposition == ViewShare);
|
|
Vad->u.VadFlags.ImageMap = 1;
|
|
|
|
// Set the protection in the VAD as EXECUTE_WRITE_COPY.
|
|
Vad->u.VadFlags.Protection = MM_EXECUTE_WRITECOPY;
|
|
Vad->ControlArea = ControlArea;
|
|
|
|
// Set the first prototype PTE field in the Vad.
|
|
SectionOffset->LowPart = SectionOffset->LowPart & ~(X64K - 1);
|
|
PteOffset = (ULONG)(SectionOffset->QuadPart >> PAGE_SHIFT);
|
|
|
|
Vad->FirstPrototypePte = &Subsection->SubsectionBase[PteOffset];
|
|
Vad->LastContiguousPte = MM_ALLOCATION_FILLS_VAD;
|
|
|
|
// NOTE: the full commitment is charged even if a partial map of an
|
|
// image is being done. This saves from having to run through the
|
|
// entire image (via prototype PTEs) and calculate the charge on a per page basis for the partial map.
|
|
|
|
Vad->u.VadFlags.CommitCharge = (SIZE_T)ImageCommitment; // ****** temp
|
|
ASSERT(Vad->FirstPrototypePte <= Vad->LastContiguousPte);
|
|
MiInsertVad(Vad);
|
|
} except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
LOCK_PFN(OldIrql);
|
|
ControlArea->NumberOfMappedViews -= 1;
|
|
ControlArea->NumberOfUserReferences -= 1;
|
|
|
|
// Check to see if the control area (segment) should be deleted.
|
|
// This routine releases the PFN lock.
|
|
MiCheckControlArea(ControlArea, Process, OldIrql);
|
|
if (Vad != (PMMVAD)NULL) {
|
|
// The pool allocation succeeded, but the quota charge
|
|
// in InsertVad failed, deallocate the pool and return an error.
|
|
ExFreePool(Vad);
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
*CapturedViewSize = (PCHAR)EndingAddress - (PCHAR)StartingAddress + 1L;
|
|
*CapturedBase = StartingAddress;
|
|
|
|
#if DBG
|
|
if (MmDebug & MM_DBG_WALK_VAD_TREE) {
|
|
DbgPrint("mapped image section vads\n");
|
|
VadTreeWalk(Process->VadRoot);
|
|
}
|
|
#endif
|
|
|
|
// Update the current virtual size in the process header.
|
|
Process->VirtualSize += *CapturedViewSize;
|
|
|
|
if (Process->VirtualSize > Process->PeakVirtualSize) {
|
|
Process->PeakVirtualSize = Process->VirtualSize;
|
|
}
|
|
|
|
if (ControlArea->u.Flags.FloppyMedia) {
|
|
*ReleasedWsMutex = TRUE;
|
|
UNLOCK_WS_UNSAFE(Process);
|
|
|
|
// The image resides on a floppy disk, in-page all
|
|
// pages from the floppy and mark them as modified so
|
|
// they migrate to the paging file rather than reread them from the floppy disk which may have been removed.
|
|
ProtoPte = Vad->FirstPrototypePte;
|
|
|
|
// This could get an in-page error from the floppy.
|
|
while (StartingAddress < EndingAddress) {
|
|
// If the prototype PTE is valid, transition or
|
|
// in prototype PTE format, bring the page into memory and set the modified bit.
|
|
if ((ProtoPte->u.Hard.Valid == 1) ||
|
|
(ProtoPte->u.Soft.Prototype == 1) ||
|
|
(ProtoPte->u.Soft.Transition == 1)) {
|
|
try {
|
|
MiSetPageModified(StartingAddress);
|
|
} except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
// An in page error must have occurred touching the image,
|
|
// ignore the error and continue to the next page.
|
|
NOTHING;
|
|
}
|
|
}
|
|
ProtoPte += 1;
|
|
StartingAddress = (PVOID)((PCHAR)StartingAddress + PAGE_SIZE);
|
|
}
|
|
}
|
|
|
|
if (!*ReleasedWsMutex) {
|
|
*ReleasedWsMutex = TRUE;
|
|
UNLOCK_WS_UNSAFE(Process);
|
|
}
|
|
|
|
if (NT_SUCCESS(ReturnedStatus)) {
|
|
// Check to see if this image is for the architecture of the current machine.
|
|
if (ControlArea->Segment->ImageInformation->ImageContainsCode &&
|
|
((ControlArea->Segment->ImageInformation->Machine < USER_SHARED_DATA->ImageNumberLow) ||
|
|
(ControlArea->Segment->ImageInformation->Machine > USER_SHARED_DATA->ImageNumberHigh))) {
|
|
#if defined (_WIN64)
|
|
// If this is a wow64 process, allow i386 images
|
|
if (!Process->Wow64Process || ControlArea->Segment->ImageInformation->Machine != IMAGE_FILE_MACHINE_I386) {
|
|
return STATUS_IMAGE_MACHINE_TYPE_MISMATCH;
|
|
}
|
|
#else //!_WIN64
|
|
return STATUS_IMAGE_MACHINE_TYPE_MISMATCH;
|
|
#endif
|
|
}
|
|
|
|
StartingAddress = MI_VPN_TO_VA(Vad->StartingVpn);
|
|
if (PsImageNotifyEnabled) {
|
|
IMAGE_INFO ImageInfo;
|
|
|
|
if ((StartingAddress < MmHighestUserAddress) &&
|
|
Process->UniqueProcessId &&
|
|
Process != PsInitialSystemProcess) {
|
|
ImageInfo.Properties = 0;
|
|
ImageInfo.ImageAddressingMode = IMAGE_ADDRESSING_MODE_32BIT;
|
|
ImageInfo.ImageBase = StartingAddress;
|
|
ImageInfo.ImageSize = *CapturedViewSize;
|
|
ImageInfo.ImageSelector = 0;
|
|
ImageInfo.ImageSectionNumber = 0;
|
|
PsCallImageNotifyRoutines((PUNICODE_STRING)&ControlArea->FilePointer->FileName, Process->UniqueProcessId, &ImageInfo);
|
|
}
|
|
}
|
|
|
|
if ((NtGlobalFlag & FLG_ENABLE_KDEBUG_SYMBOL_LOAD) &&
|
|
(ControlArea->u.Flags.Image) &&
|
|
(ReturnedStatus != STATUS_IMAGE_NOT_AT_BASE)) {
|
|
if (ControlArea->u.Flags.DebugSymbolsLoaded == 0) {
|
|
if (CacheImageSymbols(StartingAddress)) {
|
|
// TEMP TEMP TEMP rip out when debugger converted
|
|
PUNICODE_STRING FileName;
|
|
ANSI_STRING AnsiName;
|
|
NTSTATUS Status;
|
|
|
|
LOCK_PFN(OldIrql);
|
|
ControlArea->u.Flags.DebugSymbolsLoaded = 1;
|
|
UNLOCK_PFN(OldIrql);
|
|
|
|
FileName = (PUNICODE_STRING)&ControlArea->FilePointer->FileName;
|
|
if (FileName->Length != 0 && (NtGlobalFlag & FLG_ENABLE_KDEBUG_SYMBOL_LOAD)) {
|
|
PLIST_ENTRY Head, Next;
|
|
PLDR_DATA_TABLE_ENTRY Entry;
|
|
|
|
KeEnterCriticalRegion();
|
|
ExAcquireResourceExclusive(&PsLoadedModuleResource, TRUE);
|
|
Head = &MmLoadedUserImageList;
|
|
Next = Head->Flink;
|
|
while (Next != Head) {
|
|
Entry = CONTAINING_RECORD(Next, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
|
|
if (Entry->DllBase == StartingAddress) {
|
|
Entry->LoadCount += 1;
|
|
break;
|
|
}
|
|
Next = Next->Flink;
|
|
}
|
|
|
|
if (Next == Head) {
|
|
Entry = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(*Entry) + FileName->Length + sizeof(UNICODE_NULL),
|
|
MMDB);
|
|
if (Entry != NULL) {
|
|
PIMAGE_NT_HEADERS NtHeaders;
|
|
|
|
RtlZeroMemory(Entry, sizeof(*Entry));
|
|
|
|
try {
|
|
NtHeaders = RtlImageNtHeader(StartingAddress);
|
|
if (NtHeaders != NULL) {
|
|
#if defined(_WIN64)
|
|
if (NtHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
|
|
Entry->SizeOfImage = NtHeaders->OptionalHeader.SizeOfImage;
|
|
Entry->CheckSum = NtHeaders->OptionalHeader.CheckSum;
|
|
} else {
|
|
PIMAGE_NT_HEADERS32 NtHeaders32 = (PIMAGE_NT_HEADERS32)NtHeaders;
|
|
|
|
Entry->SizeOfImage = NtHeaders32->OptionalHeader.SizeOfImage;
|
|
Entry->CheckSum = NtHeaders32->OptionalHeader.CheckSum;
|
|
}
|
|
#else
|
|
Entry->SizeOfImage = NtHeaders->OptionalHeader.SizeOfImage;
|
|
Entry->CheckSum = NtHeaders->OptionalHeader.CheckSum;
|
|
#endif
|
|
}
|
|
} except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
NOTHING;
|
|
}
|
|
|
|
Entry->DllBase = StartingAddress;
|
|
Entry->FullDllName.Buffer = (PWSTR)(Entry + 1);
|
|
Entry->FullDllName.Length = FileName->Length;
|
|
Entry->FullDllName.MaximumLength = (USHORT)(Entry->FullDllName.Length + sizeof(UNICODE_NULL));
|
|
RtlMoveMemory(Entry->FullDllName.Buffer, FileName->Buffer, FileName->Length);
|
|
Entry->FullDllName.Buffer[Entry->FullDllName.Length / sizeof(WCHAR)] = UNICODE_NULL;
|
|
Entry->LoadCount = 1;
|
|
InsertTailList(&MmLoadedUserImageList, &Entry->InLoadOrderLinks);
|
|
InitializeListHead(&Entry->InInitializationOrderLinks);
|
|
InitializeListHead(&Entry->InMemoryOrderLinks);
|
|
}
|
|
}
|
|
|
|
ExReleaseResource(&PsLoadedModuleResource);
|
|
KeLeaveCriticalRegion();
|
|
}
|
|
|
|
Status = RtlUnicodeStringToAnsiString(&AnsiName, FileName, TRUE);
|
|
if (NT_SUCCESS(Status)) {
|
|
DbgLoadImageSymbols(&AnsiName, StartingAddress, (ULONG_PTR)Process);
|
|
RtlFreeAnsiString(&AnsiName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(_MIALT4K_)
|
|
if (Process->Wow64Process != NULL) {
|
|
MiProtectImageFileFor4kPage(StartingAddress, *CapturedViewSize, Vad->FirstPrototypePte, Process);
|
|
}
|
|
#endif
|
|
|
|
return ReturnedStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
MiMapViewOfDataSection(
|
|
IN PCONTROL_AREA ControlArea,
|
|
IN PEPROCESS Process,
|
|
IN PVOID *CapturedBase,
|
|
IN PLARGE_INTEGER SectionOffset,
|
|
IN PSIZE_T CapturedViewSize,
|
|
IN PSECTION Section,
|
|
IN SECTION_INHERIT InheritDisposition,
|
|
IN ULONG ProtectionMask,
|
|
IN SIZE_T CommitSize,
|
|
IN ULONG_PTR ZeroBits,
|
|
IN ULONG AllocationType,
|
|
IN PBOOLEAN ReleasedWsMutex
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine maps the specified physical section into the specified process's address space.
|
|
Arguments:
|
|
see MmMapViewOfSection above...
|
|
ControlArea - Supplies the control area for the section.
|
|
Process - Supplies the process pointer which is receiving the section.
|
|
ProtectionMask - Supplies the initial page protection-mask.
|
|
ReleasedWsMutex - Supplies FALSE. If the working set mutex is
|
|
not held when returning this must be set to TRUE
|
|
so the caller will not release the unheld mutex.
|
|
|
|
Note if this routine acquires the working set mutex
|
|
it is always done unsafely as the address space mutex must be held on entry regardless.
|
|
Return Value:
|
|
Status of the map view operation.
|
|
Environment:
|
|
Kernel Mode, address creation mutex held.
|
|
--*/
|
|
{
|
|
PMMVAD Vad;
|
|
PVOID StartingAddress;
|
|
PVOID EndingAddress;
|
|
BOOLEAN Attached;
|
|
KIRQL OldIrql;
|
|
PSUBSECTION Subsection;
|
|
ULONG PteOffset;
|
|
PMMPTE PointerPte;
|
|
PMMPTE LastPte;
|
|
MMPTE TempPte;
|
|
ULONG_PTR Alignment;
|
|
SIZE_T QuotaCharge;
|
|
BOOLEAN ChargedQuota;
|
|
PMMPTE TheFirstPrototypePte;
|
|
PVOID CapturedStartingVa;
|
|
ULONG CapturedCopyOnWrite;
|
|
#if defined(_MIALT4K_)
|
|
SIZE_T ViewSizeFor4k;
|
|
#endif
|
|
|
|
Attached = FALSE;
|
|
QuotaCharge = 0;
|
|
ChargedQuota = FALSE;
|
|
|
|
if ((AllocationType & MEM_RESERVE) && (ControlArea->FilePointer == NULL)) {
|
|
*ReleasedWsMutex = TRUE;
|
|
return STATUS_INVALID_PARAMETER_9;
|
|
}
|
|
|
|
// Check to see if there is a purge operation ongoing for this segment.
|
|
if ((AllocationType & MEM_DOS_LIM) != 0) {
|
|
if ((*CapturedBase == NULL) || (AllocationType & MEM_RESERVE)) {
|
|
// If MEM_DOS_LIM is specified, the address to map the view MUST be specified as well.
|
|
*ReleasedWsMutex = TRUE;
|
|
return STATUS_INVALID_PARAMETER_3;
|
|
}
|
|
Alignment = PAGE_SIZE;
|
|
} else {
|
|
Alignment = X64K;
|
|
}
|
|
|
|
// Check to see if a purge operation is in progress and if so, wait
|
|
// for the purge to complete. In addition, up the count of mapped views for this control area.
|
|
|
|
if (MiCheckPurgeAndUpMapCount(ControlArea) == FALSE) {
|
|
*ReleasedWsMutex = TRUE;
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if (*CapturedViewSize == 0) {
|
|
SectionOffset->LowPart = SectionOffset->LowPart & ~((ULONG)Alignment - 1);
|
|
*CapturedViewSize = (ULONG_PTR)(Section->SizeOfSection.QuadPart - SectionOffset->QuadPart);
|
|
} else {
|
|
*CapturedViewSize += SectionOffset->LowPart & (Alignment - 1);
|
|
SectionOffset->LowPart = SectionOffset->LowPart & ~((ULONG)Alignment - 1);
|
|
}
|
|
|
|
if ((LONG_PTR)*CapturedViewSize <= 0) {
|
|
// Section offset or view size past size of section.
|
|
LOCK_PFN(OldIrql);
|
|
ControlArea->NumberOfMappedViews -= 1;
|
|
ControlArea->NumberOfUserReferences -= 1;
|
|
|
|
// Check to see if the control area (segment) should be deleted.
|
|
// This routine releases the PFN lock.
|
|
MiCheckControlArea(ControlArea, NULL, OldIrql);
|
|
|
|
*ReleasedWsMutex = TRUE;
|
|
return STATUS_INVALID_VIEW_SIZE;
|
|
}
|
|
|
|
// Calculate the first prototype PTE field in the Vad.
|
|
ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
|
|
|
|
Subsection = (PSUBSECTION)(ControlArea + 1);
|
|
|
|
SectionOffset->LowPart = SectionOffset->LowPart & ~((ULONG)Alignment - 1);
|
|
PteOffset = (ULONG)(SectionOffset->QuadPart >> PAGE_SHIFT);
|
|
|
|
// Make sure the PTEs are not in the extended part of the segment.
|
|
if (PteOffset >= ControlArea->Segment->TotalNumberOfPtes) {
|
|
LOCK_PFN(OldIrql);
|
|
ControlArea->NumberOfMappedViews -= 1;
|
|
ControlArea->NumberOfUserReferences -= 1;
|
|
|
|
// Check to see if the control area (segment) should be deleted.
|
|
// This routine releases the PFN lock.
|
|
MiCheckControlArea(ControlArea, NULL, OldIrql);
|
|
|
|
*ReleasedWsMutex = TRUE;
|
|
return STATUS_INVALID_VIEW_SIZE;
|
|
}
|
|
|
|
while (PteOffset >= Subsection->PtesInSubsection) {
|
|
PteOffset -= Subsection->PtesInSubsection;
|
|
Subsection = Subsection->NextSubsection;
|
|
ASSERT(Subsection != NULL);
|
|
}
|
|
|
|
TheFirstPrototypePte = &Subsection->SubsectionBase[PteOffset];
|
|
|
|
// Calculate the quota for the specified pages.
|
|
if ((ControlArea->FilePointer == NULL) &&
|
|
(CommitSize != 0) &&
|
|
(ControlArea->Segment->NumberOfCommittedPages <
|
|
ControlArea->Segment->TotalNumberOfPtes)) {
|
|
ExAcquireFastMutexUnsafe(&MmSectionCommitMutex);
|
|
|
|
PointerPte = TheFirstPrototypePte;
|
|
LastPte = PointerPte + BYTES_TO_PAGES(CommitSize);
|
|
|
|
while (PointerPte < LastPte) {
|
|
if (PointerPte->u.Long == 0) {
|
|
QuotaCharge += 1;
|
|
}
|
|
PointerPte += 1;
|
|
}
|
|
ExReleaseFastMutexUnsafe(&MmSectionCommitMutex);
|
|
}
|
|
|
|
CapturedStartingVa = (PVOID)Section->Address.StartingVpn;
|
|
CapturedCopyOnWrite = Section->u.Flags.CopyOnWrite;
|
|
LOCK_WS_UNSAFE(Process);
|
|
|
|
if ((*CapturedBase == NULL) && (CapturedStartingVa == NULL)) {
|
|
// The section is not based, find an empty range.
|
|
// This could raise an exception.
|
|
try {
|
|
// Find a starting address on a 64k boundary.
|
|
if (AllocationType & MEM_TOP_DOWN) {
|
|
StartingAddress = MiFindEmptyAddressRangeDown(
|
|
*CapturedViewSize,
|
|
(PVOID)((PCHAR)MM_HIGHEST_VAD_ADDRESS + 1),
|
|
Alignment);
|
|
} else {
|
|
StartingAddress = MiFindEmptyAddressRange(*CapturedViewSize, Alignment, (ULONG)ZeroBits);
|
|
}
|
|
} except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
LOCK_PFN(OldIrql);
|
|
ControlArea->NumberOfMappedViews -= 1;
|
|
ControlArea->NumberOfUserReferences -= 1;
|
|
|
|
// Check to see if the control area (segment) should be deleted.
|
|
// This routine releases the PFN lock.
|
|
MiCheckControlArea(ControlArea, Process, OldIrql);
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
EndingAddress = (PVOID)(((ULONG_PTR)StartingAddress + *CapturedViewSize - 1L) | (PAGE_SIZE - 1L));
|
|
if (ZeroBits > 0) {
|
|
if (EndingAddress > (PVOID)((ULONG_PTR)MM_USER_ADDRESS_RANGE_LIMIT >> ZeroBits)) {
|
|
LOCK_PFN(OldIrql);
|
|
ControlArea->NumberOfMappedViews -= 1;
|
|
ControlArea->NumberOfUserReferences -= 1;
|
|
|
|
// Check to see if the control area (segment) should be deleted.
|
|
// This routine releases the PFN lock.
|
|
MiCheckControlArea(ControlArea, Process, OldIrql);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
} else {
|
|
if (*CapturedBase == NULL) {
|
|
// The section is based.
|
|
StartingAddress = (PVOID)((PCHAR)CapturedStartingVa + SectionOffset->LowPart);
|
|
} else {
|
|
StartingAddress = MI_ALIGN_TO_SIZE(*CapturedBase, Alignment);
|
|
}
|
|
|
|
// Check to make sure the specified base address to ending address is currently unused.
|
|
EndingAddress = (PVOID)(((ULONG_PTR)StartingAddress + *CapturedViewSize - 1L) | (PAGE_SIZE - 1L));
|
|
|
|
Vad = MiCheckForConflictingVad(StartingAddress, EndingAddress);
|
|
if (Vad != (PMMVAD)NULL) {
|
|
#if DBG
|
|
MiDumpConflictingVad(StartingAddress, EndingAddress, Vad);
|
|
#endif
|
|
|
|
LOCK_PFN(OldIrql);
|
|
ControlArea->NumberOfMappedViews -= 1;
|
|
ControlArea->NumberOfUserReferences -= 1;
|
|
|
|
// Check to see if the control area (segment) should be deleted.
|
|
// This routine releases the PFN lock.
|
|
MiCheckControlArea(ControlArea, Process, OldIrql);
|
|
return STATUS_CONFLICTING_ADDRESSES;
|
|
}
|
|
}
|
|
|
|
// An unoccupied address range has been found, build the virtual
|
|
// address descriptor to describe this range.
|
|
try {
|
|
Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD), MMVADKEY);
|
|
if (Vad == NULL) {
|
|
ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
RtlZeroMemory(Vad, sizeof(MMVAD));
|
|
|
|
Vad->StartingVpn = MI_VA_TO_VPN(StartingAddress);
|
|
Vad->EndingVpn = MI_VA_TO_VPN(EndingAddress);
|
|
Vad->FirstPrototypePte = TheFirstPrototypePte;
|
|
|
|
// Set the protection in the PTE template field of the VAD.
|
|
Vad->ControlArea = ControlArea;
|
|
Vad->u2.VadFlags2.Inherit = (InheritDisposition == ViewShare);
|
|
Vad->u.VadFlags.Protection = ProtectionMask;
|
|
Vad->u2.VadFlags2.CopyOnWrite = CapturedCopyOnWrite;
|
|
|
|
// Note that for MEM_DOS_LIM significance is lost here, but those
|
|
// files are not mapped MEM_RESERVE.
|
|
Vad->u2.VadFlags2.FileOffset = (ULONG)(SectionOffset->QuadPart >> 16);
|
|
if ((AllocationType & SEC_NO_CHANGE) || (Section->u.Flags.NoChange)) {
|
|
Vad->u.VadFlags.NoChange = 1;
|
|
Vad->u2.VadFlags2.SecNoChange = 1;
|
|
}
|
|
|
|
if (AllocationType & MEM_RESERVE) {
|
|
PMMEXTEND_INFO ExtendInfo;
|
|
|
|
ExAcquireFastMutexUnsafe(&MmSectionBasedMutex);
|
|
ExtendInfo = ControlArea->Segment->ExtendInfo;
|
|
if (ExtendInfo) {
|
|
ExtendInfo->ReferenceCount += 1;
|
|
if (ExtendInfo->CommittedSize < (UINT64)Section->SizeOfSection.QuadPart) {
|
|
ExtendInfo->CommittedSize = (UINT64)Section->SizeOfSection.QuadPart;
|
|
}
|
|
} else {
|
|
ExtendInfo = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMEXTEND_INFO), 'xCmM');
|
|
if (ExtendInfo == NULL) {
|
|
ExReleaseFastMutexUnsafe(&MmSectionBasedMutex);
|
|
ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
ExtendInfo->ReferenceCount = 1;
|
|
ExtendInfo->CommittedSize = ControlArea->Segment->SizeOfSegment;
|
|
if (ExtendInfo->CommittedSize < (UINT64)Section->SizeOfSection.QuadPart) {
|
|
ExtendInfo->CommittedSize = (UINT64)Section->SizeOfSection.QuadPart;
|
|
}
|
|
ControlArea->Segment->ExtendInfo = ExtendInfo;
|
|
}
|
|
ExReleaseFastMutexUnsafe(&MmSectionBasedMutex);
|
|
Vad->u2.VadFlags2.ExtendableFile = 1;
|
|
ASSERT(Vad->u4.ExtendedInfo == NULL);
|
|
Vad->u4.ExtendedInfo = ExtendInfo;
|
|
}
|
|
|
|
// If the page protection is write-copy or execute-write-copy
|
|
// charge for each page in the view as it may become private.
|
|
if (MI_IS_PTE_PROTECTION_COPY_WRITE(ProtectionMask)) {
|
|
Vad->u.VadFlags.CommitCharge = (BYTES_TO_PAGES((PCHAR)EndingAddress - (PCHAR)StartingAddress));
|
|
}
|
|
|
|
// If this is a page file backed section, charge the process's page
|
|
// file quota as if all the pages have been committed. This solves
|
|
// the problem when other processes commit all the pages and leave
|
|
// only one process around who may not have been charged the proper
|
|
// quota. This is solved by charging everyone the maximum quota.
|
|
|
|
// commented out for commitment charging.
|
|
|
|
#if 0
|
|
if (ControlArea->FilePointer == NULL) {
|
|
// This is a page file backed section. Charge for all the pages.
|
|
Vad->CommitCharge += (BYTES_TO_PAGES((PCHAR)EndingAddress - (PCHAR)StartingAddress));
|
|
}
|
|
#endif
|
|
|
|
PteOffset += (ULONG)(Vad->EndingVpn - Vad->StartingVpn);
|
|
|
|
if (PteOffset < Subsection->PtesInSubsection) {
|
|
Vad->LastContiguousPte = &Subsection->SubsectionBase[PteOffset];
|
|
} else {
|
|
Vad->LastContiguousPte = &Subsection->SubsectionBase[(Subsection->PtesInSubsection - 1) + Subsection->UnusedPtes];
|
|
}
|
|
|
|
if (QuotaCharge != 0) {
|
|
if (MiChargeCommitment(QuotaCharge, Process) == FALSE) {
|
|
ExRaiseStatus(STATUS_COMMITMENT_LIMIT);
|
|
}
|
|
ChargedQuota = TRUE;
|
|
MM_TRACK_COMMIT(MM_DBG_COMMIT_MAPVIEW_DATA, QuotaCharge);
|
|
}
|
|
|
|
ASSERT(Vad->FirstPrototypePte <= Vad->LastContiguousPte);
|
|
MiInsertVad(Vad);
|
|
} except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
LOCK_PFN(OldIrql);
|
|
ControlArea->NumberOfMappedViews -= 1;
|
|
ControlArea->NumberOfUserReferences -= 1;
|
|
|
|
// Check to see if the control area (segment) should be deleted.
|
|
// This routine releases the PFN lock.
|
|
MiCheckControlArea(ControlArea, Process, OldIrql);
|
|
if (Vad != (PMMVAD)NULL) {
|
|
// The pool allocation succeeded, but the quota charge
|
|
// in InsertVad failed, deallocate the pool and return an error.
|
|
ExFreePool(Vad);
|
|
if (ChargedQuota) {
|
|
MiReturnCommitment(QuotaCharge);
|
|
MM_TRACK_COMMIT(MM_DBG_COMMIT_RETURN_MAPVIEW_DATA, QuotaCharge);
|
|
}
|
|
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
*ReleasedWsMutex = TRUE;
|
|
|
|
#if DBG
|
|
if (!(AllocationType & MEM_RESERVE)) {
|
|
ASSERT(((ULONG_PTR)EndingAddress - (ULONG_PTR)StartingAddress) <= ROUND_TO_PAGES64(Section->Segment->SizeOfSegment));
|
|
}
|
|
#endif //DBG
|
|
|
|
UNLOCK_WS_UNSAFE(Process);
|
|
|
|
// If a commit size was specified, make sure those pages are committed.
|
|
if (QuotaCharge != 0) {
|
|
ExAcquireFastMutexUnsafe(&MmSectionCommitMutex);
|
|
|
|
PointerPte = Vad->FirstPrototypePte;
|
|
LastPte = PointerPte + BYTES_TO_PAGES(CommitSize);
|
|
TempPte = ControlArea->Segment->SegmentPteTemplate;
|
|
|
|
while (PointerPte < LastPte) {
|
|
if (PointerPte->u.Long == 0) {
|
|
MI_WRITE_INVALID_PTE(PointerPte, TempPte);
|
|
}
|
|
PointerPte += 1;
|
|
}
|
|
|
|
ControlArea->Segment->NumberOfCommittedPages += QuotaCharge;
|
|
|
|
ASSERT(ControlArea->Segment->NumberOfCommittedPages <= ControlArea->Segment->TotalNumberOfPtes);
|
|
MmSharedCommit += QuotaCharge;
|
|
|
|
ExReleaseFastMutexUnsafe(&MmSectionCommitMutex);
|
|
}
|
|
|
|
#if defined(_MIALT4K_)
|
|
if (Process->Wow64Process != NULL) {
|
|
EndingAddress = (PVOID)(((ULONG_PTR)StartingAddress + *CapturedViewSize - 1L) | (PAGE_4K - 1L));
|
|
ViewSizeFor4k = (PCHAR)EndingAddress - (PCHAR)StartingAddress + 1L;
|
|
MiProtectMapFileFor4kPage(StartingAddress, ViewSizeFor4k, ProtectionMask, Vad->FirstPrototypePte, Process);
|
|
}
|
|
#endif
|
|
|
|
// Update the current virtual size in the process header.
|
|
*CapturedViewSize = (PCHAR)EndingAddress - (PCHAR)StartingAddress + 1L;
|
|
Process->VirtualSize += *CapturedViewSize;
|
|
|
|
if (Process->VirtualSize > Process->PeakVirtualSize) {
|
|
Process->PeakVirtualSize = Process->VirtualSize;
|
|
}
|
|
|
|
*CapturedBase = StartingAddress;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
LOGICAL MiCheckPurgeAndUpMapCount(IN PCONTROL_AREA ControlArea)
|
|
/*++
|
|
Routine Description:
|
|
This routine synchronizes with any on going purge operations
|
|
on the same segment (identified via the control area). If
|
|
another purge operation is occurring, the function blocks until it is completed.
|
|
|
|
When this function returns the MappedView and the NumberOfUserReferences
|
|
count for the control area will be incremented thereby referencing the control area.
|
|
Arguments:
|
|
ControlArea - Supplies the control area for the segment to be purged.
|
|
Return Value:
|
|
TRUE if the synchronization was successful.
|
|
FALSE if the synchronization did not occur due to low resources, etc.
|
|
Environment:
|
|
Kernel Mode.
|
|
--*/
|
|
{
|
|
KIRQL OldIrql;
|
|
PEVENT_COUNTER PurgedEvent;
|
|
PEVENT_COUNTER WaitEvent;
|
|
ULONG OldRef;
|
|
|
|
PurgedEvent = NULL;
|
|
OldRef = 1;
|
|
|
|
LOCK_PFN(OldIrql);
|
|
while (ControlArea->u.Flags.BeingPurged != 0) {
|
|
// A purge operation is in progress.
|
|
if (PurgedEvent == NULL) {
|
|
// Release the locks and allocate pool for the event.
|
|
PurgedEvent = MiGetEventCounter();
|
|
if (PurgedEvent == NULL) {
|
|
UNLOCK_PFN(OldIrql);
|
|
return FALSE;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (ControlArea->WaitingForDeletion == NULL) {
|
|
ControlArea->WaitingForDeletion = PurgedEvent;
|
|
WaitEvent = PurgedEvent;
|
|
PurgedEvent = NULL;
|
|
} else {
|
|
WaitEvent = ControlArea->WaitingForDeletion;
|
|
WaitEvent->RefCount += 1;
|
|
}
|
|
|
|
// Release the pfn lock and wait for the event.
|
|
KeEnterCriticalRegion();
|
|
UNLOCK_PFN_AND_THEN_WAIT(OldIrql);
|
|
|
|
KeWaitForSingleObject(&WaitEvent->Event, WrVirtualMemory, KernelMode, FALSE, (PLARGE_INTEGER)NULL);
|
|
LOCK_PFN(OldIrql);
|
|
KeLeaveCriticalRegion();
|
|
MiFreeEventCounter(WaitEvent, FALSE);
|
|
}
|
|
|
|
// Indicate another file is mapped for the segment.
|
|
ControlArea->NumberOfMappedViews += 1;
|
|
ControlArea->NumberOfUserReferences += 1;
|
|
ControlArea->u.Flags.HadUserReference = 1;
|
|
ASSERT(ControlArea->NumberOfSectionReferences != 0);
|
|
if (PurgedEvent != NULL) {
|
|
MiFreeEventCounter(PurgedEvent, TRUE);
|
|
}
|
|
UNLOCK_PFN(OldIrql);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
typedef struct _NTSYM
|
|
{
|
|
struct _NTSYM *Next;
|
|
PVOID SymbolTable;
|
|
ULONG NumberOfSymbols;
|
|
PVOID StringTable;
|
|
USHORT Flags;
|
|
USHORT EntrySize;
|
|
ULONG MinimumVa;
|
|
ULONG MaximumVa;
|
|
PCHAR MapName;
|
|
ULONG MapNameLen;
|
|
} NTSYM, *PNTSYM;
|
|
|
|
|
|
ULONG CacheImageSymbols(IN PVOID ImageBase)
|
|
{
|
|
PIMAGE_DEBUG_DIRECTORY DebugDirectory;
|
|
ULONG DebugSize;
|
|
|
|
PAGED_CODE();
|
|
|
|
try {
|
|
DebugDirectory = (PIMAGE_DEBUG_DIRECTORY)RtlImageDirectoryEntryToData(ImageBase, TRUE, IMAGE_DIRECTORY_ENTRY_DEBUG, &DebugSize);
|
|
if (!DebugDirectory) {
|
|
return FALSE;
|
|
}
|
|
|
|
// If using remote KD, ImageBase is what it wants to see.
|
|
} except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
NTSYSAPI NTSTATUS NTAPI NtAreMappedFilesTheSame(IN PVOID File1MappedAsAnImage, IN PVOID File2MappedAsFile)
|
|
/*++
|
|
Routine Description:
|
|
This routine compares the two files mapped at the specified addresses to see if they are both the same file.
|
|
Arguments:
|
|
File1MappedAsAnImage - Supplies an address within the first file which is mapped as an image file.
|
|
File2MappedAsFile - Supplies an address within the second file which is mapped as either an image file or a data file.
|
|
Return Value:
|
|
STATUS_SUCCESS is returned if the two files are the same.
|
|
STATUS_NOT_SAME_DEVICE is returned if the files are different.
|
|
Other status values can be returned if the addresses are not mapped as files, etc.
|
|
Environment:
|
|
User mode callable system service.
|
|
--*/
|
|
{
|
|
PMMVAD FoundVad1;
|
|
PMMVAD FoundVad2;
|
|
NTSTATUS Status;
|
|
|
|
LOCK_ADDRESS_SPACE(PsGetCurrentProcess());
|
|
FoundVad1 = MiLocateAddress(File1MappedAsAnImage);
|
|
FoundVad2 = MiLocateAddress(File2MappedAsFile);
|
|
|
|
if ((FoundVad1 == NULL) || (FoundVad2 == NULL)) {
|
|
// No virtual address is allocated at the specified base address, return an error.
|
|
Status = STATUS_INVALID_ADDRESS;
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
// Check file names.
|
|
if ((FoundVad1->u.VadFlags.PrivateMemory == 1) || (FoundVad2->u.VadFlags.PrivateMemory == 1)) {
|
|
Status = STATUS_CONFLICTING_ADDRESSES;
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
if ((FoundVad1->ControlArea == NULL) || (FoundVad2->ControlArea == NULL)) {
|
|
Status = STATUS_CONFLICTING_ADDRESSES;
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
if ((FoundVad1->ControlArea->FilePointer == NULL) || (FoundVad2->ControlArea->FilePointer == NULL)) {
|
|
Status = STATUS_CONFLICTING_ADDRESSES;
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
Status = STATUS_NOT_SAME_DEVICE;
|
|
|
|
if ((PVOID)FoundVad1->ControlArea == FoundVad2->ControlArea->FilePointer->SectionObjectPointer->ImageSectionObject) {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
ErrorReturn:
|
|
UNLOCK_ADDRESS_SPACE(PsGetCurrentProcess());
|
|
return Status;
|
|
}
|
|
|
|
|
|
VOID MiSetPageModified(IN PVOID Address)
|
|
/*++
|
|
Routine Description:
|
|
This routine sets the modified bit in the PFN database for the pages that correspond to the specified address range.
|
|
Note that the dirty bit in the PTE is cleared by this operation.
|
|
Arguments:
|
|
Address - Supplies the address of the start of the range. This range must reside within the system cache.
|
|
Return Value:
|
|
None.
|
|
Environment:
|
|
Kernel mode. APC_LEVEL and below for pagable addresses,
|
|
DISPATCH_LEVEL and below for non-pagable addresses.
|
|
--*/
|
|
{
|
|
volatile PMMPTE PointerPpe;
|
|
volatile PMMPTE PointerPde;
|
|
volatile PMMPTE PointerPte;
|
|
PMMPFN Pfn1;
|
|
MMPTE PteContents;
|
|
KIRQL OldIrql;
|
|
|
|
// Loop on the copy on write case until the page is only writable.
|
|
PointerPte = MiGetPteAddress(Address);
|
|
PointerPde = MiGetPdeAddress(Address);
|
|
PointerPpe = MiGetPpeAddress(Address);
|
|
|
|
*(volatile CCHAR *)Address;
|
|
|
|
LOCK_PFN(OldIrql);
|
|
|
|
#if defined (_WIN64)
|
|
while ((PointerPpe->u.Hard.Valid == 0) || (PointerPde->u.Hard.Valid == 0) || (PointerPte->u.Hard.Valid == 0))
|
|
#else
|
|
while ((PointerPde->u.Hard.Valid == 0) || (PointerPte->u.Hard.Valid == 0))
|
|
#endif
|
|
{
|
|
// Page is no longer valid.
|
|
UNLOCK_PFN(OldIrql);
|
|
*(volatile CCHAR *)Address;
|
|
LOCK_PFN(OldIrql);
|
|
}
|
|
|
|
PteContents = *PointerPte;
|
|
|
|
Pfn1 = MI_PFN_ELEMENT(PteContents.u.Hard.PageFrameNumber);
|
|
Pfn1->u3.e1.Modified = 1;
|
|
|
|
if ((Pfn1->OriginalPte.u.Soft.Prototype == 0) && (Pfn1->u3.e1.WriteInProgress == 0)) {
|
|
MiReleasePageFileSpace(Pfn1->OriginalPte);
|
|
Pfn1->OriginalPte.u.Soft.PageFileHigh = 0;
|
|
}
|
|
|
|
#ifdef NT_UP
|
|
if (MI_IS_PTE_DIRTY(PteContents)) {
|
|
#endif //NT_UP
|
|
MI_SET_PTE_CLEAN(PteContents);
|
|
|
|
// Clear the dirty bit in the PTE so new writes can be tracked.
|
|
(VOID)KeFlushSingleTb(Address, FALSE, TRUE, (PHARDWARE_PTE)PointerPte, PteContents.u.Flush);
|
|
#ifdef NT_UP
|
|
}
|
|
#endif //NT_UP
|
|
|
|
UNLOCK_PFN(OldIrql);
|
|
return;
|
|
}
|
|
|
|
|
|
NTSTATUS MmMapViewInSystemSpace(IN PVOID Section, OUT PVOID *MappedBase, IN OUT PSIZE_T ViewSize)
|
|
/*++
|
|
Routine Description:
|
|
This routine maps the specified section into the system's address space.
|
|
Arguments:
|
|
Section - Supplies a pointer to the section to map.
|
|
*MappedBase - Returns the address where the section was mapped.
|
|
ViewSize - Supplies the size of the view to map. If this
|
|
is specified as zero, the whole section is mapped.
|
|
Returns the actual size mapped.
|
|
Return Value:
|
|
Status of the map view operation.
|
|
Environment:
|
|
Kernel Mode, IRQL of dispatch level.
|
|
--*/
|
|
{
|
|
PMMSESSION Session;
|
|
|
|
PAGED_CODE();
|
|
|
|
Session = &MmSession;
|
|
|
|
return MiMapViewInSystemSpace(Section, Session, MappedBase, ViewSize);
|
|
}
|
|
|
|
|
|
NTSTATUS MmMapViewInSessionSpace(IN PVOID Section, OUT PVOID *MappedBase, IN OUT PSIZE_T ViewSize)
|
|
/*++
|
|
Routine Description:
|
|
This routine maps the specified section into the current process's session address space.
|
|
Arguments:
|
|
Section - Supplies a pointer to the section to map.
|
|
*MappedBase - Returns the address where the section was mapped.
|
|
ViewSize - Supplies the size of the view to map. If this
|
|
is specified as zero, the whole section is mapped.
|
|
Returns the actual size mapped.
|
|
Return Value:
|
|
Status of the map view operation.
|
|
Environment:
|
|
Kernel Mode, IRQL of dispatch level.
|
|
--*/
|
|
{
|
|
PMMSESSION Session;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (MiHydra == TRUE) {
|
|
if (PsGetCurrentProcess()->Vm.u.Flags.ProcessInSession == 0) {
|
|
return STATUS_NOT_MAPPED_VIEW;
|
|
}
|
|
ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
|
|
Session = &MmSessionSpace->Session;
|
|
} else {
|
|
Session = &MmSession;
|
|
}
|
|
|
|
return MiMapViewInSystemSpace(Section, Session, MappedBase, ViewSize);
|
|
}
|
|
|
|
|
|
NTSTATUS MiMapViewInSystemSpace(IN PVOID Section, IN PMMSESSION Session, OUT PVOID *MappedBase, IN OUT PSIZE_T ViewSize)
|
|
/*++
|
|
Routine Description:
|
|
This routine maps the specified section into the system's address space.
|
|
Arguments:
|
|
Section - Supplies a pointer to the section to map.
|
|
Session - Supplies the session data structure for this view.
|
|
*MappedBase - Returns the address where the section was mapped.
|
|
ViewSize - Supplies the size of the view to map. If this
|
|
is specified as zero, the whole section is mapped.
|
|
Returns the actual size mapped.
|
|
Protect - Supplies the protection for the view. Must be either PAGE_READWRITE or PAGE_READONLY.
|
|
Return Value:
|
|
Status of the map view operation.
|
|
Environment:
|
|
Kernel Mode, IRQL of APC_LEVEL or below.
|
|
--*/
|
|
{
|
|
PVOID Base;
|
|
KIRQL OldIrql;
|
|
PSUBSECTION Subsection;
|
|
PCONTROL_AREA ControlArea;
|
|
PCONTROL_AREA NewControlArea;
|
|
ULONG StartBit;
|
|
ULONG SizeIn64k;
|
|
ULONG NewSizeIn64k;
|
|
PMMPTE BasePte;
|
|
ULONG NumberOfPtes;
|
|
ULONG NumberOfBytes;
|
|
BOOLEAN status;
|
|
KIRQL WsIrql;
|
|
|
|
PAGED_CODE();
|
|
|
|
// Check to see if a purge operation is in progress and if so, wait
|
|
// for the purge to complete. In addition, up the count of mapped views for this control area.
|
|
ControlArea = ((PSECTION)Section)->Segment->ControlArea;
|
|
if (ControlArea->u.Flags.GlobalOnlyPerSession == 0) {
|
|
Subsection = (PSUBSECTION)(ControlArea + 1);
|
|
} else {
|
|
Subsection = (PSUBSECTION)((PLARGE_CONTROL_AREA)ControlArea + 1);
|
|
}
|
|
|
|
MmLockPagableSectionByHandle(ExPageLockHandle);
|
|
|
|
if (MiCheckPurgeAndUpMapCount(ControlArea) == FALSE) {
|
|
MmUnlockPagableImageSection(ExPageLockHandle);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if (*ViewSize == 0) {
|
|
*ViewSize = ((PSECTION)Section)->SizeOfSection.LowPart;
|
|
} else if (*ViewSize > ((PSECTION)Section)->SizeOfSection.LowPart) {
|
|
// Section offset or view size past size of section.
|
|
LOCK_PFN(OldIrql);
|
|
ControlArea->NumberOfMappedViews -= 1;
|
|
ControlArea->NumberOfUserReferences -= 1;
|
|
|
|
// Check to see if the control area (segment) should be deleted.
|
|
// This routine releases the PFN lock.
|
|
MiCheckControlArea(ControlArea, NULL, OldIrql);
|
|
|
|
MmUnlockPagableImageSection(ExPageLockHandle);
|
|
return STATUS_INVALID_VIEW_SIZE;
|
|
}
|
|
|
|
// Calculate the first prototype PTE field in the Vad.
|
|
SizeIn64k = (ULONG)((*ViewSize / X64K) + ((*ViewSize & (X64K - 1)) != 0));
|
|
|
|
Base = MiInsertInSystemSpace(Session, SizeIn64k, ControlArea);
|
|
if (Base == NULL) {
|
|
LOCK_PFN(OldIrql);
|
|
ControlArea->NumberOfMappedViews -= 1;
|
|
ControlArea->NumberOfUserReferences -= 1;
|
|
|
|
// Check to see if the control area (segment) should be deleted.
|
|
// This routine releases the PFN lock.
|
|
MiCheckControlArea(ControlArea, NULL, OldIrql);
|
|
|
|
MmUnlockPagableImageSection(ExPageLockHandle);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
NumberOfBytes = SizeIn64k * X64K;
|
|
|
|
if (Session == &MmSession) {
|
|
status = MiFillSystemPageDirectory(Base, NumberOfBytes);
|
|
} else {
|
|
LOCK_SESSION_SPACE_WS(WsIrql);
|
|
if (NT_SUCCESS(MiSessionCommitPageTables(Base,
|
|
(PVOID)((ULONG_PTR)Base + NumberOfBytes - 1)))) {
|
|
status = TRUE;
|
|
} else {
|
|
status = FALSE;
|
|
}
|
|
}
|
|
|
|
if (status == FALSE) {
|
|
if (Session != &MmSession) {
|
|
UNLOCK_SESSION_SPACE_WS(WsIrql);
|
|
}
|
|
|
|
LOCK_PFN(OldIrql);
|
|
ControlArea->NumberOfMappedViews -= 1;
|
|
ControlArea->NumberOfUserReferences -= 1;
|
|
|
|
// Check to see if the control area (segment) should be deleted.
|
|
// This routine releases the PFN lock.
|
|
|
|
MiCheckControlArea(ControlArea, NULL, OldIrql);
|
|
StartBit = (ULONG)(((ULONG_PTR)Base - (ULONG_PTR)Session->SystemSpaceViewStart) >> 16);
|
|
LOCK_SYSTEM_VIEW_SPACE(Session);
|
|
NewSizeIn64k = MiRemoveFromSystemSpace(Session, Base, &NewControlArea);
|
|
ASSERT(ControlArea == NewControlArea);
|
|
ASSERT(SizeIn64k == NewSizeIn64k);
|
|
RtlClearBits(Session->SystemSpaceBitMap, StartBit, SizeIn64k);
|
|
UNLOCK_SYSTEM_VIEW_SPACE(Session);
|
|
MmUnlockPagableImageSection(ExPageLockHandle);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
// Setup PTEs to point to prototype PTEs.
|
|
if (((PSECTION)Section)->u.Flags.Image) {
|
|
#if DBG
|
|
// The only reason this ASSERT isn't done for Hydra is because
|
|
// the session space working set lock is currently held so faults are not allowed.
|
|
if (Session == &MmSession) {
|
|
ASSERT(((PSECTION)Section)->Segment->ControlArea == ControlArea);
|
|
}
|
|
#endif
|
|
|
|
LOCK_PFN(OldIrql);
|
|
ControlArea->u.Flags.ImageMappedInSystemSpace = 1;
|
|
UNLOCK_PFN(OldIrql);
|
|
}
|
|
|
|
BasePte = MiGetPteAddress(Base);
|
|
NumberOfPtes = BYTES_TO_PAGES(*ViewSize);
|
|
|
|
MiAddMappedPtes(BasePte, NumberOfPtes, ControlArea);
|
|
|
|
if (Session != &MmSession) {
|
|
UNLOCK_SESSION_SPACE_WS(WsIrql);
|
|
}
|
|
|
|
*MappedBase = Base;
|
|
|
|
MmUnlockPagableImageSection(ExPageLockHandle);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
BOOLEAN MiFillSystemPageDirectory(PVOID Base, SIZE_T NumberOfBytes)
|
|
/*++
|
|
Routine Description:
|
|
This routine allocates page tables and fills the system page directory
|
|
entries for the specified virtual address range.
|
|
Arguments:
|
|
Base - Supplies the virtual address of the view.
|
|
NumberOfBytes - Supplies the number of bytes the view spans.
|
|
Return Value:
|
|
TRUE on success, FALSE on failure.
|
|
Environment:
|
|
Kernel Mode, IRQL of dispatch level.
|
|
--*/
|
|
{
|
|
PMMPTE FirstPde;
|
|
PMMPTE LastPde;
|
|
PMMPTE FirstSystemPde;
|
|
MMPTE TempPte;
|
|
PFN_NUMBER PageFrameIndex;
|
|
KIRQL OldIrql;
|
|
ULONG i;
|
|
|
|
PAGED_CODE();
|
|
|
|
// CODE IS ALREADY LOCKED BY CALLER.
|
|
FirstPde = MiGetPdeAddress(Base);
|
|
LastPde = MiGetPdeAddress((PVOID)(((PCHAR)Base) + NumberOfBytes - 1));
|
|
|
|
#if defined (_WIN64)
|
|
FirstSystemPde = FirstPde;
|
|
#else
|
|
#if !defined (_X86PAE_)
|
|
FirstSystemPde = &MmSystemPagePtes[((ULONG_PTR)FirstPde & ((PDE_PER_PAGE * sizeof(MMPTE)) - 1)) / sizeof(MMPTE)];
|
|
#else
|
|
FirstSystemPde = &MmSystemPagePtes[((ULONG_PTR)FirstPde & (PD_PER_SYSTEM * (PDE_PER_PAGE * sizeof(MMPTE)) - 1)) / sizeof(MMPTE)];
|
|
#endif
|
|
#endif
|
|
|
|
do {
|
|
if (FirstSystemPde->u.Hard.Valid == 0) {
|
|
// No page table page exists, get a page and map it in.
|
|
TempPte = ValidKernelPde;
|
|
|
|
LOCK_PFN(OldIrql);
|
|
|
|
if (((volatile MMPTE *)FirstSystemPde)->u.Hard.Valid == 0) {
|
|
if (MiEnsureAvailablePageOrWait(NULL, FirstPde)) {
|
|
// PFN_LOCK was dropped, redo this loop as another process could have made this PDE valid.
|
|
UNLOCK_PFN(OldIrql);
|
|
continue;
|
|
}
|
|
|
|
MiChargeCommitmentCantExpand(1, TRUE);
|
|
MM_TRACK_COMMIT(MM_DBG_COMMIT_FILL_SYSTEM_DIRECTORY, 1);
|
|
PageFrameIndex = MiRemoveAnyPage(MI_GET_PAGE_COLOR_FROM_PTE(FirstSystemPde));
|
|
TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
|
|
MI_WRITE_VALID_PTE(FirstSystemPde, TempPte);
|
|
MI_WRITE_VALID_PTE(FirstPde, TempPte);
|
|
|
|
#if defined (_WIN64)
|
|
MiInitializePfn(PageFrameIndex, FirstPde, 1);
|
|
#else
|
|
|
|
#if !defined (_X86PAE_)
|
|
MiInitializePfnForOtherProcess(PageFrameIndex, FirstPde, MmSystemPageDirectory);
|
|
#else
|
|
i = (FirstPde - MiGetPdeAddress(0)) / PDE_PER_PAGE;
|
|
MiInitializePfnForOtherProcess(PageFrameIndex, FirstPde, MmSystemPageDirectory[i]);
|
|
#endif
|
|
|
|
#endif
|
|
|
|
MiFillMemoryPte(MiGetVirtualAddressMappedByPte(FirstPde), PAGE_SIZE, MM_ZERO_KERNEL_PTE);
|
|
}
|
|
UNLOCK_PFN(OldIrql);
|
|
}
|
|
|
|
FirstSystemPde += 1;
|
|
FirstPde += 1;
|
|
} while (FirstPde <= LastPde);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
NTSTATUS MmUnmapViewInSystemSpace(IN PVOID MappedBase)
|
|
/*++
|
|
Routine Description:
|
|
This routine unmaps the specified section from the system's address space.
|
|
Arguments:
|
|
MappedBase - Supplies the address of the view to unmap.
|
|
Return Value:
|
|
Status of the map view operation.
|
|
Environment:
|
|
Kernel Mode, IRQL of dispatch level.
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
return MiUnmapViewInSystemSpace(&MmSession, MappedBase);
|
|
}
|
|
|
|
|
|
NTSTATUS MmUnmapViewInSessionSpace(IN PVOID MappedBase)
|
|
/*++
|
|
Routine Description:
|
|
This routine unmaps the specified section from the system's address space.
|
|
Arguments:
|
|
MappedBase - Supplies the address of the view to unmap.
|
|
Return Value:
|
|
Status of the map view operation.
|
|
Environment:
|
|
Kernel Mode, IRQL of dispatch level.
|
|
--*/
|
|
{
|
|
PMMSESSION Session;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (MiHydra == TRUE) {
|
|
if (PsGetCurrentProcess()->Vm.u.Flags.ProcessInSession == 0) {
|
|
return STATUS_NOT_MAPPED_VIEW;
|
|
}
|
|
ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
|
|
Session = &MmSessionSpace->Session;
|
|
} else {
|
|
Session = &MmSession;
|
|
}
|
|
|
|
return MiUnmapViewInSystemSpace(Session, MappedBase);
|
|
}
|
|
|
|
|
|
NTSTATUS MiUnmapViewInSystemSpace(IN PMMSESSION Session, IN PVOID MappedBase)
|
|
/*++
|
|
Routine Description:
|
|
This routine unmaps the specified section from the system's address space.
|
|
Arguments:
|
|
Session - Supplies the session data structure for this view.
|
|
MappedBase - Supplies the address of the view to unmap.
|
|
Return Value:
|
|
Status of the map view operation.
|
|
Environment:
|
|
Kernel Mode, IRQL of dispatch level.
|
|
--*/
|
|
{
|
|
ULONG StartBit;
|
|
ULONG Size;
|
|
PCONTROL_AREA ControlArea;
|
|
PMMSUPPORT Ws;
|
|
KIRQL WsIrql;
|
|
|
|
PAGED_CODE();
|
|
|
|
MmLockPagableSectionByHandle(ExPageLockHandle);
|
|
StartBit = (ULONG)(((ULONG_PTR)MappedBase - (ULONG_PTR)Session->SystemSpaceViewStart) >> 16);
|
|
|
|
LOCK_SYSTEM_VIEW_SPACE(Session);
|
|
Size = MiRemoveFromSystemSpace(Session, MappedBase, &ControlArea);
|
|
RtlClearBits(Session->SystemSpaceBitMap, StartBit, Size);
|
|
|
|
// Zero PTEs.
|
|
Size = Size * (X64K >> PAGE_SHIFT);
|
|
|
|
if (Session == &MmSession) {
|
|
Ws = &MmSystemCacheWs;
|
|
} else {
|
|
Ws = &MmSessionSpace->Vm;
|
|
LOCK_SESSION_SPACE_WS(WsIrql);
|
|
}
|
|
|
|
MiRemoveMappedPtes(MappedBase, Size, ControlArea, Ws);
|
|
|
|
if (Session != &MmSession) {
|
|
UNLOCK_SESSION_SPACE_WS(WsIrql);
|
|
}
|
|
|
|
UNLOCK_SYSTEM_VIEW_SPACE(Session);
|
|
|
|
MmUnlockPagableImageSection(ExPageLockHandle);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
PVOID MiInsertInSystemSpace(IN PMMSESSION Session, IN ULONG SizeIn64k, IN PCONTROL_AREA ControlArea)
|
|
/*++
|
|
Routine Description:
|
|
This routine creates a view in system space for the specified control area (file mapping).
|
|
Arguments:
|
|
SizeIn64k - Supplies the size of the view to be created.
|
|
ControlArea - Supplies a pointer to the control area for this view.
|
|
Return Value:
|
|
Base address where the view was mapped, NULL if the view could not be mapped.
|
|
Environment:
|
|
Kernel Mode.
|
|
--*/
|
|
{
|
|
PVOID Base;
|
|
ULONG_PTR Entry;
|
|
ULONG Hash;
|
|
ULONG i;
|
|
ULONG AllocSize;
|
|
PMMVIEW OldTable;
|
|
ULONG StartBit;
|
|
ULONG NewHashSize;
|
|
|
|
PAGED_CODE();
|
|
|
|
// CODE IS ALREADY LOCKED BY CALLER.
|
|
|
|
LOCK_SYSTEM_VIEW_SPACE(Session);
|
|
|
|
if (Session->SystemSpaceHashEntries + 8 > Session->SystemSpaceHashSize) {
|
|
// Less than 8 free slots, reallocate and rehash.
|
|
NewHashSize = Session->SystemSpaceHashSize << 1;
|
|
|
|
AllocSize = sizeof(MMVIEW) * NewHashSize;
|
|
ASSERT(AllocSize < PAGE_SIZE);
|
|
|
|
OldTable = Session->SystemSpaceViewTable;
|
|
|
|
Session->SystemSpaceViewTable = ExAllocatePoolWithTag(PagedPool, AllocSize, ' mM');
|
|
|
|
if (Session->SystemSpaceViewTable == NULL) {
|
|
Session->SystemSpaceViewTable = OldTable;
|
|
} else {
|
|
RtlZeroMemory(Session->SystemSpaceViewTable, AllocSize);
|
|
|
|
Session->SystemSpaceHashSize = NewHashSize;
|
|
Session->SystemSpaceHashKey = Session->SystemSpaceHashSize - 1;
|
|
|
|
for (i = 0; i < (Session->SystemSpaceHashSize / 2); i += 1) {
|
|
if (OldTable[i].Entry != 0) {
|
|
Hash = (ULONG)((OldTable[i].Entry >> 16) % Session->SystemSpaceHashKey);
|
|
|
|
while (Session->SystemSpaceViewTable[Hash].Entry != 0) {
|
|
Hash += 1;
|
|
if (Hash >= Session->SystemSpaceHashSize) {
|
|
Hash = 0;
|
|
}
|
|
}
|
|
Session->SystemSpaceViewTable[Hash] = OldTable[i];
|
|
}
|
|
}
|
|
ExFreePool(OldTable);
|
|
}
|
|
}
|
|
|
|
if (Session->SystemSpaceHashEntries == Session->SystemSpaceHashSize) {
|
|
// There are no free hash slots to place a new entry into even
|
|
// though there may still be unused virtual address space.
|
|
UNLOCK_SYSTEM_VIEW_SPACE(Session);
|
|
return NULL;
|
|
}
|
|
|
|
StartBit = RtlFindClearBitsAndSet(Session->SystemSpaceBitMap, SizeIn64k, 0);
|
|
if (StartBit == 0xFFFFFFFF) {
|
|
UNLOCK_SYSTEM_VIEW_SPACE(Session);
|
|
return NULL;
|
|
}
|
|
|
|
Base = (PVOID)((PCHAR)Session->SystemSpaceViewStart + (StartBit * X64K));
|
|
Entry = (ULONG_PTR)MI_64K_ALIGN(Base) + SizeIn64k;
|
|
Hash = (ULONG)((Entry >> 16) % Session->SystemSpaceHashKey);
|
|
|
|
while (Session->SystemSpaceViewTable[Hash].Entry != 0) {
|
|
Hash += 1;
|
|
if (Hash >= Session->SystemSpaceHashSize) {
|
|
Hash = 0;
|
|
}
|
|
}
|
|
|
|
Session->SystemSpaceHashEntries += 1;
|
|
Session->SystemSpaceViewTable[Hash].Entry = Entry;
|
|
Session->SystemSpaceViewTable[Hash].ControlArea = ControlArea;
|
|
|
|
UNLOCK_SYSTEM_VIEW_SPACE(Session);
|
|
return Base;
|
|
}
|
|
|
|
|
|
ULONG MiRemoveFromSystemSpace(IN PMMSESSION Session, IN PVOID Base, OUT PCONTROL_AREA *ControlArea)
|
|
/*++
|
|
Routine Description:
|
|
This routine looks up the specified view in the system space hash
|
|
table and unmaps the view from system space and the table.
|
|
Arguments:
|
|
Session - Supplies the session data structure for this view.
|
|
Base - Supplies the base address for the view. If this address is NOT found in the hash table, the system bugchecks.
|
|
ControlArea - Returns the control area corresponding to the base address.
|
|
Return Value:
|
|
Size of the view divided by 64k.
|
|
Environment:
|
|
Kernel Mode, system view hash table locked.
|
|
--*/
|
|
{
|
|
ULONG_PTR Base16;
|
|
ULONG Hash;
|
|
ULONG Size;
|
|
ULONG count;
|
|
|
|
PAGED_CODE();
|
|
|
|
count = 0;
|
|
|
|
// CODE IS ALREADY LOCKED BY CALLER.
|
|
Base16 = (ULONG_PTR)Base >> 16;
|
|
Hash = (ULONG)(Base16 % Session->SystemSpaceHashKey);
|
|
|
|
while ((Session->SystemSpaceViewTable[Hash].Entry >> 16) != Base16) {
|
|
Hash += 1;
|
|
if (Hash >= Session->SystemSpaceHashSize) {
|
|
Hash = 0;
|
|
count += 1;
|
|
if (count == 2) {
|
|
KeBugCheckEx(DRIVER_UNMAPPING_INVALID_VIEW, (ULONG_PTR)Base, MiHydra, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
Session->SystemSpaceHashEntries -= 1;
|
|
Size = (ULONG)(Session->SystemSpaceViewTable[Hash].Entry & 0xFFFF);
|
|
Session->SystemSpaceViewTable[Hash].Entry = 0;
|
|
*ControlArea = Session->SystemSpaceViewTable[Hash].ControlArea;
|
|
return Size;
|
|
}
|
|
|
|
|
|
BOOLEAN MiInitializeSystemSpaceMap(PVOID InputSession OPTIONAL)
|
|
/*++
|
|
Routine Description:
|
|
This routine initializes the tables for mapping views into system space.
|
|
Views are kept in a multiple of 64k bytes in a growable hashed table.
|
|
Arguments:
|
|
NULL if this is the initial system session (non-Hydra), a valid session
|
|
pointer (the pointer must be in global space, not session space) for
|
|
Hydra session initialization.
|
|
Return Value:
|
|
TRUE on success, FALSE on failure.
|
|
Environment:
|
|
Kernel Mode, initialization.
|
|
--*/
|
|
{
|
|
ULONG AllocSize;
|
|
ULONG Size;
|
|
PCHAR ViewStart;
|
|
PMMSESSION Session;
|
|
|
|
if (ARGUMENT_PRESENT(InputSession)) {
|
|
Session = (PMMSESSION)InputSession;
|
|
ViewStart = (PCHAR)MI_SESSION_VIEW_START;
|
|
Size = MI_SESSION_VIEW_SIZE;
|
|
} else {
|
|
Session = &MmSession;
|
|
ViewStart = (PCHAR)MiSystemViewStart;
|
|
if (MiHydra == TRUE) {
|
|
Size = MM_SYSTEM_VIEW_SIZE_IF_HYDRA;
|
|
} else {
|
|
Size = MM_SYSTEM_VIEW_SIZE;
|
|
}
|
|
}
|
|
|
|
// We are passed a system global address for the address of the session.
|
|
// Save a global pointer to the mutex below because multiple sessions will
|
|
// generally give us a session-space (not a global space) pointer to the
|
|
// MMSESSION in subsequent calls. We need the global pointer for the mutex
|
|
// field for the kernel primitives to work properly.
|
|
Session->SystemSpaceViewLockPointer = &Session->SystemSpaceViewLock;
|
|
ExInitializeFastMutex(Session->SystemSpaceViewLockPointer);
|
|
|
|
// If the kernel image has not been biased to allow for 3gb of user space,
|
|
// then the system space view starts at the defined place. Otherwise, it
|
|
// starts 16mb above the kernel image.
|
|
Session->SystemSpaceViewStart = ViewStart;
|
|
|
|
MiCreateBitMap(&Session->SystemSpaceBitMap, Size / X64K, NonPagedPool);
|
|
if (Session->SystemSpaceBitMap == NULL) {
|
|
MM_BUMP_SESSION_FAILURES(MM_SESSION_FAILURE_NO_NONPAGED_POOL);
|
|
return FALSE;
|
|
}
|
|
|
|
RtlClearAllBits(Session->SystemSpaceBitMap);
|
|
|
|
// Build the view table.
|
|
Session->SystemSpaceHashSize = 31;
|
|
Session->SystemSpaceHashKey = Session->SystemSpaceHashSize - 1;
|
|
Session->SystemSpaceHashEntries = 0;
|
|
|
|
AllocSize = sizeof(MMVIEW) * Session->SystemSpaceHashSize;
|
|
ASSERT(AllocSize < PAGE_SIZE);
|
|
|
|
Session->SystemSpaceViewTable = ExAllocatePoolWithTag(PagedPool, AllocSize, ' mM');
|
|
|
|
if (Session->SystemSpaceViewTable == NULL) {
|
|
MM_BUMP_SESSION_FAILURES(MM_SESSION_FAILURE_NO_SESSION_PAGED_POOL);
|
|
MiRemoveBitMap(&Session->SystemSpaceBitMap);
|
|
return FALSE;
|
|
}
|
|
|
|
RtlZeroMemory(Session->SystemSpaceViewTable, AllocSize);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID MiFreeSessionSpaceMap(VOID)
|
|
/*++
|
|
Routine Description:
|
|
This routine frees the tables used for mapping session views.
|
|
Arguments:
|
|
None.
|
|
Return Value:
|
|
None.
|
|
Environment:
|
|
Kernel Mode. The caller must be in the correct session context.
|
|
--*/
|
|
{
|
|
PMMSESSION Session;
|
|
|
|
PAGED_CODE();
|
|
|
|
Session = &MmSessionSpace->Session;
|
|
|
|
// Check for leaks of objects in the view table.
|
|
LOCK_SYSTEM_VIEW_SPACE(Session);
|
|
|
|
if (Session->SystemSpaceViewTable && Session->SystemSpaceHashEntries) {
|
|
KeBugCheckEx(SESSION_HAS_VALID_VIEWS_ON_EXIT,
|
|
(ULONG_PTR)MmSessionSpace->SessionId,
|
|
Session->SystemSpaceHashEntries,
|
|
(ULONG_PTR)&Session->SystemSpaceViewTable[0],
|
|
Session->SystemSpaceHashSize);
|
|
|
|
#if 0
|
|
ULONG Index;
|
|
|
|
for (Index = 0; Index < Session->SystemSpaceHashSize; Index += 1) {
|
|
PMMVIEW Table;
|
|
PVOID Base;
|
|
|
|
Table = &Session->SystemSpaceViewTable[Index];
|
|
if (Table->Entry) {
|
|
#if DBG
|
|
DbgPrint("MM: MiFreeSessionSpaceMap: view entry %d leak: ControlArea %p, Addr %p, Size %d\n",
|
|
Index,
|
|
Table->ControlArea,
|
|
Table->Entry & ~0xFFFF,
|
|
Table->Entry & 0x0000FFFF
|
|
);
|
|
#endif
|
|
|
|
Base = (PVOID)(Table->Entry & ~0xFFFF);
|
|
|
|
// MiUnmapViewInSystemSpace locks the ViewLock.
|
|
UNLOCK_SYSTEM_VIEW_SPACE(Session);
|
|
MiUnmapViewInSystemSpace(Session, Base);
|
|
LOCK_SYSTEM_VIEW_SPACE(Session);
|
|
|
|
// The view table may have been deleted while we let go of the lock.
|
|
if (Session->SystemSpaceViewTable == NULL) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
UNLOCK_SYSTEM_VIEW_SPACE(Session);
|
|
|
|
if (Session->SystemSpaceViewTable) {
|
|
ExFreePool(Session->SystemSpaceViewTable);
|
|
Session->SystemSpaceViewTable = NULL;
|
|
}
|
|
|
|
if (Session->SystemSpaceBitMap) {
|
|
MiRemoveBitMap(&Session->SystemSpaceBitMap);
|
|
}
|
|
}
|
|
|
|
|
|
HANDLE MmSecureVirtualMemory(IN PVOID Address, IN SIZE_T Size, IN ULONG ProbeMode)
|
|
/*++
|
|
Routine Description:
|
|
This routine probes the requested address range and protects
|
|
the specified address range from having its protection made more restricted and being deleted.
|
|
|
|
MmUnsecureVirtualMemory is used to allow the range to return to a normal state.
|
|
Arguments:
|
|
Address - Supplies the base address to probe and secure.
|
|
Size - Supplies the size of the range to secure.
|
|
ProbeMode - Supplies one of PAGE_READONLY or PAGE_READWRITE.
|
|
Return Value:
|
|
Returns a handle to be used to unsecure the range.
|
|
If the range could not be locked because of protection
|
|
problems or noncommitted memory, the value (HANDLE)0 is returned.
|
|
Environment:
|
|
Kernel Mode.
|
|
--*/
|
|
{
|
|
ULONG_PTR EndAddress;
|
|
PVOID StartAddress;
|
|
CHAR Temp;
|
|
ULONG Probe;
|
|
HANDLE Handle;
|
|
PMMVAD Vad;
|
|
PMMVAD NewVad;
|
|
PMMSECURE_ENTRY Secure;
|
|
PEPROCESS Process;
|
|
PMMPTE PointerPpe;
|
|
PMMPTE PointerPde;
|
|
PMMPTE PointerPte;
|
|
PMMPTE LastPte;
|
|
MMLOCK_CONFLICT Conflict;
|
|
ULONG Waited;
|
|
|
|
PAGED_CODE();
|
|
|
|
if ((ULONG_PTR)Address + Size > (ULONG_PTR)MM_HIGHEST_USER_ADDRESS || (ULONG_PTR)Address + Size <= (ULONG_PTR)Address) {
|
|
return (HANDLE)0;
|
|
}
|
|
|
|
Handle = (HANDLE)0;
|
|
|
|
Probe = (ProbeMode == PAGE_READONLY);
|
|
|
|
Process = PsGetCurrentProcess();
|
|
StartAddress = Address;
|
|
|
|
LOCK_ADDRESS_SPACE(Process);
|
|
|
|
// Check for a private committed VAD first instead of probing to avoid all
|
|
// the page faults and zeroing. If we find one, then we run the PTEs instead.
|
|
if (Size >= 64 * 1024) {
|
|
EndAddress = (ULONG_PTR)StartAddress + Size - 1;
|
|
Vad = MiLocateAddress(StartAddress);
|
|
|
|
if (Vad == NULL) {
|
|
goto Return1;
|
|
}
|
|
|
|
if (Vad->u.VadFlags.UserPhysicalPages == 1) {
|
|
goto Return1;
|
|
}
|
|
|
|
if (Vad->u.VadFlags.MemCommit == 0) {
|
|
goto LongWay;
|
|
}
|
|
|
|
if (Vad->u.VadFlags.PrivateMemory == 0) {
|
|
goto LongWay;
|
|
}
|
|
|
|
if (Vad->u.VadFlags.PhysicalMapping == 1) {
|
|
goto LongWay;
|
|
}
|
|
|
|
ASSERT(Vad->u.VadFlags.Protection);
|
|
|
|
if ((MI_VA_TO_VPN(StartAddress) < Vad->StartingVpn) || (MI_VA_TO_VPN(EndAddress) > Vad->EndingVpn)) {
|
|
goto Return1;
|
|
}
|
|
|
|
if (Vad->u.VadFlags.Protection == MM_NOACCESS) {
|
|
goto LongWay;
|
|
}
|
|
|
|
if (ProbeMode == PAGE_READONLY) {
|
|
if (Vad->u.VadFlags.Protection > MM_EXECUTE_WRITECOPY) {
|
|
goto LongWay;
|
|
}
|
|
} else {
|
|
if (Vad->u.VadFlags.Protection != MM_READWRITE && Vad->u.VadFlags.Protection != MM_EXECUTE_READWRITE) {
|
|
goto LongWay;
|
|
}
|
|
}
|
|
|
|
// Check individual page permissions.
|
|
PointerPde = MiGetPdeAddress(StartAddress);
|
|
PointerPpe = MiGetPteAddress(PointerPde);
|
|
PointerPte = MiGetPteAddress(StartAddress);
|
|
LastPte = MiGetPteAddress(EndAddress);
|
|
|
|
LOCK_WS_UNSAFE(Process);
|
|
|
|
do {
|
|
while (MiDoesPpeExistAndMakeValid(PointerPpe, Process, FALSE, &Waited) == FALSE) {
|
|
// Page directory parent entry is empty, go to the next one.
|
|
PointerPpe += 1;
|
|
PointerPde = MiGetVirtualAddressMappedByPte(PointerPpe);
|
|
PointerPte = MiGetVirtualAddressMappedByPte(PointerPde);
|
|
if (PointerPte > LastPte) {
|
|
UNLOCK_WS_UNSAFE(Process);
|
|
goto EditVad;
|
|
}
|
|
}
|
|
|
|
Waited = 0;
|
|
|
|
while (MiDoesPdeExistAndMakeValid(PointerPde, Process, FALSE, &Waited) == FALSE) {
|
|
// This page directory entry is empty, go to the next one.
|
|
PointerPde += 1;
|
|
PointerPpe = MiGetPteAddress(PointerPde);
|
|
PointerPte = MiGetVirtualAddressMappedByPte(PointerPde);
|
|
if (PointerPte > LastPte) {
|
|
UNLOCK_WS_UNSAFE(Process);
|
|
goto EditVad;
|
|
}
|
|
#if defined (_WIN64)
|
|
if (MiIsPteOnPdeBoundary(PointerPde)) {
|
|
Waited = 1;
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
} while (Waited != 0);
|
|
|
|
while (PointerPte <= LastPte) {
|
|
if (MiIsPteOnPdeBoundary(PointerPte)) {
|
|
PointerPde = MiGetPteAddress(PointerPte);
|
|
PointerPpe = MiGetPteAddress(PointerPde);
|
|
|
|
do {
|
|
while (MiDoesPpeExistAndMakeValid(PointerPpe, Process, FALSE, &Waited) == FALSE) {
|
|
// Page directory parent entry is empty, go to the next one.
|
|
PointerPpe += 1;
|
|
PointerPde = MiGetVirtualAddressMappedByPte(PointerPpe);
|
|
PointerPte = MiGetVirtualAddressMappedByPte(PointerPde);
|
|
if (PointerPte > LastPte) {
|
|
UNLOCK_WS_UNSAFE(Process);
|
|
goto EditVad;
|
|
}
|
|
}
|
|
|
|
Waited = 0;
|
|
|
|
while (MiDoesPdeExistAndMakeValid(PointerPde, Process, FALSE, &Waited) == FALSE) {
|
|
// This page directory entry is empty, go to the next one.
|
|
PointerPde += 1;
|
|
PointerPpe = MiGetPteAddress(PointerPde);
|
|
PointerPte = MiGetVirtualAddressMappedByPte(PointerPde);
|
|
if (PointerPte > LastPte) {
|
|
UNLOCK_WS_UNSAFE(Process);
|
|
goto EditVad;
|
|
}
|
|
#if defined (_WIN64)
|
|
if (MiIsPteOnPdeBoundary(PointerPde)) {
|
|
Waited = 1;
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
} while (Waited != 0);
|
|
}
|
|
|
|
if (PointerPte->u.Long) {
|
|
UNLOCK_WS_UNSAFE(Process);
|
|
goto LongWay;
|
|
}
|
|
PointerPte += 1;
|
|
}
|
|
|
|
UNLOCK_WS_UNSAFE(Process);
|
|
} else {
|
|
LongWay:
|
|
MiInsertConflictInList(&Conflict);
|
|
try {
|
|
if (ProbeMode == PAGE_READONLY) {
|
|
EndAddress = (ULONG_PTR)Address + Size - 1;
|
|
EndAddress = (EndAddress & ~(PAGE_SIZE - 1)) + PAGE_SIZE;
|
|
|
|
do {
|
|
Temp = *(volatile CHAR *)Address;
|
|
Address = (PVOID)(((ULONG_PTR)Address & ~(PAGE_SIZE - 1)) + PAGE_SIZE);
|
|
} while ((ULONG_PTR)Address != EndAddress);
|
|
} else {
|
|
ProbeForWrite(Address, (ULONG)Size, 1); // ****** temp ******
|
|
}
|
|
} except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
MiRemoveConflictFromList(&Conflict);
|
|
goto Return1;
|
|
}
|
|
MiRemoveConflictFromList(&Conflict);
|
|
|
|
// Locate VAD and add in secure descriptor.
|
|
EndAddress = (ULONG_PTR)StartAddress + Size - 1;
|
|
Vad = MiLocateAddress(StartAddress);
|
|
if (Vad == NULL) {
|
|
goto Return1;
|
|
}
|
|
|
|
if (Vad->u.VadFlags.UserPhysicalPages == 1) {
|
|
goto Return1;
|
|
}
|
|
|
|
if ((MI_VA_TO_VPN(StartAddress) < Vad->StartingVpn) || (MI_VA_TO_VPN(EndAddress) > Vad->EndingVpn)) {
|
|
// Not within the section virtual address descriptor, return an error.
|
|
goto Return1;
|
|
}
|
|
}
|
|
|
|
EditVad:
|
|
// If this is a short VAD, it needs to be reallocated as a large VAD.
|
|
if ((Vad->u.VadFlags.PrivateMemory) && (!Vad->u.VadFlags.NoChange)) {
|
|
NewVad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD), MMVADKEY);
|
|
if (NewVad == NULL) {
|
|
goto Return1;
|
|
}
|
|
|
|
RtlZeroMemory(NewVad, sizeof(MMVAD));
|
|
RtlCopyMemory(NewVad, Vad, sizeof(MMVAD_SHORT));
|
|
NewVad->u.VadFlags.NoChange = 1;
|
|
NewVad->u2.VadFlags2.OneSecured = 1;
|
|
NewVad->u2.VadFlags2.StoredInVad = 1;
|
|
NewVad->u2.VadFlags2.ReadOnly = Probe;
|
|
NewVad->u3.Secured.StartVpn = (ULONG_PTR)StartAddress;
|
|
NewVad->u3.Secured.EndVpn = EndAddress;
|
|
|
|
// Replace the current VAD with this expanded VAD.
|
|
LOCK_WS_UNSAFE(Process);
|
|
if (Vad->Parent) {
|
|
if (Vad->Parent->RightChild == Vad) {
|
|
Vad->Parent->RightChild = NewVad;
|
|
} else {
|
|
ASSERT(Vad->Parent->LeftChild == Vad);
|
|
Vad->Parent->LeftChild = NewVad;
|
|
}
|
|
} else {
|
|
Process->VadRoot = NewVad;
|
|
}
|
|
if (Vad->LeftChild) {
|
|
Vad->LeftChild->Parent = NewVad;
|
|
}
|
|
if (Vad->RightChild) {
|
|
Vad->RightChild->Parent = NewVad;
|
|
}
|
|
if (Process->VadHint == Vad) {
|
|
Process->VadHint = NewVad;
|
|
}
|
|
if (Process->VadFreeHint == Vad) {
|
|
Process->VadFreeHint = NewVad;
|
|
}
|
|
|
|
if ((Vad->u.VadFlags.PhysicalMapping == 1) || (Vad->u.VadFlags.WriteWatch == 1)) {
|
|
MiPhysicalViewAdjuster(Process, Vad, NewVad);
|
|
}
|
|
|
|
UNLOCK_WS_UNSAFE(Process);
|
|
ExFreePool(Vad);
|
|
Handle = (HANDLE)&NewVad->u2.LongFlags2;
|
|
goto Return1;
|
|
}
|
|
|
|
// This is already a large VAD, add the secure entry.
|
|
if (Vad->u2.VadFlags2.OneSecured) {
|
|
// This VAD already is secured. Move the info out of the block into pool.
|
|
Secure = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMSECURE_ENTRY), 'eSmM');
|
|
if (Secure == NULL) {
|
|
goto Return1;
|
|
}
|
|
|
|
ASSERT(Vad->u.VadFlags.NoChange == 1);
|
|
Vad->u2.VadFlags2.OneSecured = 0;
|
|
Vad->u2.VadFlags2.MultipleSecured = 1;
|
|
Secure->u2.LongFlags2 = (ULONG)Vad->u.LongFlags;
|
|
Secure->u2.VadFlags2.StoredInVad = 0;
|
|
Secure->StartVpn = Vad->u3.Secured.StartVpn;
|
|
Secure->EndVpn = Vad->u3.Secured.EndVpn;
|
|
|
|
InitializeListHead(&Vad->u3.List);
|
|
InsertTailList(&Vad->u3.List, &Secure->List);
|
|
}
|
|
|
|
if (Vad->u2.VadFlags2.MultipleSecured) {
|
|
// This VAD already has a secured element in its list, allocate and add in the new secured element.
|
|
Secure = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMSECURE_ENTRY), 'eSmM');
|
|
if (Secure == NULL) {
|
|
goto Return1;
|
|
}
|
|
|
|
Secure->u2.LongFlags2 = 0;
|
|
Secure->u2.VadFlags2.ReadOnly = Probe;
|
|
Secure->StartVpn = (ULONG_PTR)StartAddress;
|
|
Secure->EndVpn = EndAddress;
|
|
|
|
InsertTailList(&Vad->u3.List, &Secure->List);
|
|
Handle = (HANDLE)Secure;
|
|
} else {
|
|
// This list does not have a secure element. Put it in the VAD.
|
|
Vad->u.VadFlags.NoChange = 1;
|
|
Vad->u2.VadFlags2.OneSecured = 1;
|
|
Vad->u2.VadFlags2.StoredInVad = 1;
|
|
Vad->u2.VadFlags2.ReadOnly = Probe;
|
|
Vad->u3.Secured.StartVpn = (ULONG_PTR)StartAddress;
|
|
Vad->u3.Secured.EndVpn = EndAddress;
|
|
Handle = (HANDLE)&Vad->u2.LongFlags2;
|
|
}
|
|
|
|
Return1:
|
|
UNLOCK_ADDRESS_SPACE(Process);
|
|
return Handle;
|
|
}
|
|
|
|
|
|
VOID MmUnsecureVirtualMemory(IN HANDLE SecureHandle)
|
|
/*++
|
|
Routine Description:
|
|
This routine unsecures memory previous secured via a call to MmSecureVirtualMemory.
|
|
Arguments:
|
|
SecureHandle - Supplies the handle returned in MmSecureVirtualMemory.
|
|
Return Value:
|
|
None.
|
|
Environment:
|
|
Kernel Mode.
|
|
--*/
|
|
{
|
|
PMMSECURE_ENTRY Secure;
|
|
PEPROCESS Process;
|
|
PMMVAD Vad;
|
|
|
|
PAGED_CODE();
|
|
|
|
Secure = (PMMSECURE_ENTRY)SecureHandle;
|
|
Process = PsGetCurrentProcess();
|
|
LOCK_ADDRESS_SPACE(Process);
|
|
|
|
if (Secure->u2.VadFlags2.StoredInVad) {
|
|
Vad = CONTAINING_RECORD(Secure, MMVAD, u2.LongFlags2);
|
|
} else {
|
|
Vad = MiLocateAddress((PVOID)Secure->StartVpn);
|
|
}
|
|
|
|
ASSERT(Vad);
|
|
ASSERT(Vad->u.VadFlags.NoChange == 1);
|
|
|
|
if (Vad->u2.VadFlags2.OneSecured) {
|
|
ASSERT(Secure == (PMMSECURE_ENTRY)&Vad->u2.LongFlags2);
|
|
Vad->u2.VadFlags2.OneSecured = 0;
|
|
ASSERT(Vad->u2.VadFlags2.MultipleSecured == 0);
|
|
if (Vad->u2.VadFlags2.SecNoChange == 0) {
|
|
Vad->u.VadFlags.NoChange = 0;// No more secure entries in this list, remove the state.
|
|
}
|
|
} else {
|
|
ASSERT(Vad->u2.VadFlags2.MultipleSecured == 1);
|
|
|
|
if (Secure == (PMMSECURE_ENTRY)&Vad->u2.LongFlags2) {
|
|
// This was a single block that got converted into a list.
|
|
// Reset the entry.
|
|
Secure = CONTAINING_RECORD(Vad->u3.List.Flink, MMSECURE_ENTRY, List);
|
|
}
|
|
RemoveEntryList(&Secure->List);
|
|
ExFreePool(Secure);
|
|
if (IsListEmpty(&Vad->u3.List)) {
|
|
Vad->u2.VadFlags2.MultipleSecured = 0;// No more secure entries, reset the state.
|
|
|
|
if ((Vad->u2.VadFlags2.SecNoChange == 0) && (Vad->u.VadFlags.PrivateMemory == 0)) {
|
|
// No more secure entries in this list, remove the state if and only if this VAD is not private.
|
|
// If this VAD
|
|
// is private, removing the state NoChange flag indicates that this is a short VAD which it no longer is.
|
|
Vad->u.VadFlags.NoChange = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
UNLOCK_ADDRESS_SPACE(Process);
|
|
}
|