reactos/ntoskrnl/mm/section.c
2021-02-03 09:41:24 +01:00

5073 lines
160 KiB
C

/*
* Copyright (C) 1998-2005 ReactOS Team (and the authors from the programmers section)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*
* PROJECT: ReactOS kernel
* FILE: ntoskrnl/mm/section.c
* PURPOSE: Implements section objects
*
* PROGRAMMERS: Rex Jolliff
* David Welch
* Eric Kohl
* Emanuele Aliberti
* Eugene Ingerman
* Casper Hornstrup
* KJK::Hyperion
* Guido de Jong
* Ge van Geldorp
* Royce Mitchell III
* Filip Navara
* Aleksey Bragin
* Jason Filby
* Thomas Weidenmueller
* Gunnar Andre' Dalsnes
* Mike Nordell
* Alex Ionescu
* Gregor Anich
* Steven Edwards
* Herve Poussineau
*/
/* INCLUDES *****************************************************************/
#include <ntoskrnl.h>
#include <cache/newcc.h>
#include <cache/section/newmm.h>
#define NDEBUG
#include <debug.h>
#include <reactos/exeformat.h>
#include "ARM3/miarm.h"
#undef MmSetPageEntrySectionSegment
#define MmSetPageEntrySectionSegment(S,O,E) do { \
DPRINT("SetPageEntrySectionSegment(old,%p,%x,%x)\n",(S),(O)->LowPart,E); \
_MmSetPageEntrySectionSegment((S),(O),(E),__FILE__,__LINE__); \
} while (0)
extern MMSESSION MmSession;
static LARGE_INTEGER TinyTime = {{-1L, -1L}};
#ifndef NEWCC
KEVENT MmWaitPageEvent;
VOID
NTAPI
_MmLockSectionSegment(PMM_SECTION_SEGMENT Segment, const char *file, int line)
{
//DPRINT("MmLockSectionSegment(%p,%s:%d)\n", Segment, file, line);
ExAcquireFastMutex(&Segment->Lock);
Segment->Locked = TRUE;
}
VOID
NTAPI
_MmUnlockSectionSegment(PMM_SECTION_SEGMENT Segment, const char *file, int line)
{
ASSERT(Segment->Locked);
Segment->Locked = FALSE;
ExReleaseFastMutex(&Segment->Lock);
//DPRINT("MmUnlockSectionSegment(%p,%s:%d)\n", Segment, file, line);
}
#endif
static
PMM_SECTION_SEGMENT
MiGrabDataSection(PSECTION_OBJECT_POINTERS SectionObjectPointer)
{
KIRQL OldIrql = MiAcquirePfnLock();
PMM_SECTION_SEGMENT Segment = NULL;
while (TRUE)
{
Segment = SectionObjectPointer->DataSectionObject;
if (!Segment)
break;
if (Segment->SegFlags & (MM_SEGMENT_INCREATE | MM_SEGMENT_INDELETE))
{
MiReleasePfnLock(OldIrql);
KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
OldIrql = MiAcquirePfnLock();
continue;
}
ASSERT(Segment->SegFlags & MM_DATAFILE_SEGMENT);
InterlockedIncrement64(&Segment->RefCount);
break;
}
MiReleasePfnLock(OldIrql);
return Segment;
}
/* Somewhat grotesque, but eh... */
PMM_IMAGE_SECTION_OBJECT ImageSectionObjectFromSegment(PMM_SECTION_SEGMENT Segment)
{
ASSERT((Segment->SegFlags & MM_DATAFILE_SEGMENT) == 0);
return CONTAINING_RECORD(Segment->ReferenceCount, MM_IMAGE_SECTION_OBJECT, RefCount);
}
NTSTATUS
MiMapViewInSystemSpace(IN PVOID Section,
IN PVOID Session,
OUT PVOID *MappedBase,
IN OUT PSIZE_T ViewSize,
IN PLARGE_INTEGER SectionOffset);
NTSTATUS
NTAPI
MmCreateArm3Section(OUT PVOID *SectionObject,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN PLARGE_INTEGER InputMaximumSize,
IN ULONG SectionPageProtection,
IN ULONG AllocationAttributes,
IN HANDLE FileHandle OPTIONAL,
IN PFILE_OBJECT FileObject OPTIONAL);
NTSTATUS
NTAPI
MmMapViewOfArm3Section(IN PVOID SectionObject,
IN PEPROCESS Process,
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);
//
// PeFmtCreateSection depends on the following:
//
C_ASSERT(EXEFMT_LOAD_HEADER_SIZE >= sizeof(IMAGE_DOS_HEADER));
C_ASSERT(sizeof(IMAGE_NT_HEADERS32) <= sizeof(IMAGE_NT_HEADERS64));
C_ASSERT(TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) == TYPE_ALIGNMENT(IMAGE_NT_HEADERS64));
C_ASSERT(RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader) == RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS64, FileHeader));
C_ASSERT(FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader) == FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader));
C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, Magic));
C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SectionAlignment));
C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, FileAlignment));
C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, Subsystem));
C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, MinorSubsystemVersion));
C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, MajorSubsystemVersion));
C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, AddressOfEntryPoint));
C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SizeOfCode));
C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SizeOfHeaders));
/* TYPES *********************************************************************/
typedef struct
{
PMEMORY_AREA MemoryArea;
PMM_SECTION_SEGMENT Segment;
LARGE_INTEGER Offset;
BOOLEAN WasDirty;
BOOLEAN Private;
PEPROCESS CallingProcess;
ULONG_PTR SectionEntry;
}
MM_SECTION_PAGEOUT_CONTEXT;
/* GLOBALS *******************************************************************/
POBJECT_TYPE MmSectionObjectType = NULL;
ULONG_PTR MmSubsectionBase;
static ULONG SectionCharacteristicsToProtect[16] =
{
PAGE_NOACCESS, /* 0 = NONE */
PAGE_NOACCESS, /* 1 = SHARED */
PAGE_EXECUTE, /* 2 = EXECUTABLE */
PAGE_EXECUTE, /* 3 = EXECUTABLE, SHARED */
PAGE_READONLY, /* 4 = READABLE */
PAGE_READONLY, /* 5 = READABLE, SHARED */
PAGE_EXECUTE_READ, /* 6 = READABLE, EXECUTABLE */
PAGE_EXECUTE_READ, /* 7 = READABLE, EXECUTABLE, SHARED */
/*
* FIXME? do we really need the WriteCopy field in segments? can't we use
* PAGE_WRITECOPY here?
*/
PAGE_READWRITE, /* 8 = WRITABLE */
PAGE_READWRITE, /* 9 = WRITABLE, SHARED */
PAGE_EXECUTE_READWRITE, /* 10 = WRITABLE, EXECUTABLE */
PAGE_EXECUTE_READWRITE, /* 11 = WRITABLE, EXECUTABLE, SHARED */
PAGE_READWRITE, /* 12 = WRITABLE, READABLE */
PAGE_READWRITE, /* 13 = WRITABLE, READABLE, SHARED */
PAGE_EXECUTE_READWRITE, /* 14 = WRITABLE, READABLE, EXECUTABLE */
PAGE_EXECUTE_READWRITE, /* 15 = WRITABLE, READABLE, EXECUTABLE, SHARED */
};
extern ULONG MmMakeFileAccess [];
ACCESS_MASK NTAPI MiArm3GetCorrectFileAccessMask(IN ACCESS_MASK SectionPageProtection);
static GENERIC_MAPPING MmpSectionMapping =
{
STANDARD_RIGHTS_READ | SECTION_MAP_READ | SECTION_QUERY,
STANDARD_RIGHTS_WRITE | SECTION_MAP_WRITE,
STANDARD_RIGHTS_EXECUTE | SECTION_MAP_EXECUTE,
SECTION_ALL_ACCESS
};
/* FUNCTIONS *****************************************************************/
NTSTATUS
NTAPI
MiWritePage(PMM_SECTION_SEGMENT Segment,
LONGLONG SegOffset,
PFN_NUMBER Page)
/*
* FUNCTION: write a page for a section backed memory area.
* PARAMETERS:
* MemoryArea - Memory area to write the page for.
* Offset - Offset of the page to write.
* Page - Page which contains the data to write.
*/
{
NTSTATUS Status;
IO_STATUS_BLOCK IoStatus;
KEVENT Event;
UCHAR MdlBase[sizeof(MDL) + sizeof(PFN_NUMBER)];
PMDL Mdl = (PMDL)MdlBase;
PFILE_OBJECT FileObject = Segment->FileObject;
LARGE_INTEGER FileOffset;
FileOffset.QuadPart = Segment->Image.FileOffset + SegOffset;
RtlZeroMemory(MdlBase, sizeof(MdlBase));
MmInitializeMdl(Mdl, NULL, PAGE_SIZE);
MmBuildMdlFromPages(Mdl, &Page);
Mdl->MdlFlags |= MDL_PAGES_LOCKED;
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Status = IoSynchronousPageWrite(FileObject, Mdl, &FileOffset, &Event, &IoStatus);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatus.Status;
}
if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
{
MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
}
return Status;
}
/*
References:
[1] Microsoft Corporation, "Microsoft Portable Executable and Common Object
File Format Specification", revision 6.0 (February 1999)
*/
NTSTATUS NTAPI PeFmtCreateSection(IN CONST VOID * FileHeader,
IN SIZE_T FileHeaderSize,
IN PVOID File,
OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
OUT PULONG Flags,
IN PEXEFMT_CB_READ_FILE ReadFileCb,
IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb)
{
NTSTATUS nStatus;
ULONG cbFileHeaderOffsetSize = 0;
ULONG cbSectionHeadersOffset = 0;
ULONG cbSectionHeadersSize;
ULONG cbSectionHeadersOffsetSize = 0;
ULONG cbOptHeaderSize;
ULONG cbHeadersSize = 0;
ULONG nSectionAlignment;
ULONG nFileAlignment;
ULONG_PTR ImageBase = 0;
const IMAGE_DOS_HEADER * pidhDosHeader;
const IMAGE_NT_HEADERS32 * pinhNtHeader;
const IMAGE_OPTIONAL_HEADER32 * piohOptHeader;
const IMAGE_SECTION_HEADER * pishSectionHeaders;
PMM_SECTION_SEGMENT pssSegments;
LARGE_INTEGER lnOffset;
PVOID pBuffer;
SIZE_T nPrevVirtualEndOfSegment = 0;
ULONG nFileSizeOfHeaders = 0;
ULONG i;
ULONG AlignedLength;
ASSERT(FileHeader);
ASSERT(FileHeaderSize > 0);
ASSERT(File);
ASSERT(ImageSectionObject);
ASSERT(ReadFileCb);
ASSERT(AllocateSegmentsCb);
ASSERT(Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize));
ASSERT(((UINT_PTR)FileHeader % TYPE_ALIGNMENT(IMAGE_DOS_HEADER)) == 0);
#define DIE(ARGS_) { DPRINT ARGS_; goto l_Return; }
pBuffer = NULL;
pidhDosHeader = FileHeader;
/* DOS HEADER */
nStatus = STATUS_ROS_EXEFMT_UNKNOWN_FORMAT;
/* image too small to be an MZ executable */
if(FileHeaderSize < sizeof(IMAGE_DOS_HEADER))
DIE(("Too small to be an MZ executable, size is %lu\n", FileHeaderSize));
/* no MZ signature */
if(pidhDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
DIE(("No MZ signature found, e_magic is %hX\n", pidhDosHeader->e_magic));
/* NT HEADER */
nStatus = STATUS_INVALID_IMAGE_PROTECT;
/* not a Windows executable */
if(pidhDosHeader->e_lfanew <= 0)
DIE(("Not a Windows executable, e_lfanew is %d\n", pidhDosHeader->e_lfanew));
if(!Intsafe_AddULong32(&cbFileHeaderOffsetSize, pidhDosHeader->e_lfanew, RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader)))
DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
if(FileHeaderSize < cbFileHeaderOffsetSize)
pinhNtHeader = NULL;
else
{
/*
* we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
* and FileHeaderSize >= cbFileHeaderOffsetSize, so this holds true too
*/
ASSERT(Intsafe_CanOffsetPointer(FileHeader, pidhDosHeader->e_lfanew));
pinhNtHeader = (PVOID)((UINT_PTR)FileHeader + pidhDosHeader->e_lfanew);
}
/*
* the buffer doesn't contain the NT file header, or the alignment is wrong: we
* need to read the header from the file
*/
if(FileHeaderSize < cbFileHeaderOffsetSize ||
(UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
{
ULONG cbNtHeaderSize;
ULONG cbReadSize;
PVOID pData;
l_ReadHeaderFromFile:
cbNtHeaderSize = 0;
lnOffset.QuadPart = pidhDosHeader->e_lfanew;
/* read the header from the file */
nStatus = ReadFileCb(File, &lnOffset, sizeof(IMAGE_NT_HEADERS64), &pData, &pBuffer, &cbReadSize);
if(!NT_SUCCESS(nStatus))
{
NTSTATUS ReturnedStatus = nStatus;
/* If it attempted to read past the end of the file, it means e_lfanew is invalid */
if (ReturnedStatus == STATUS_END_OF_FILE) nStatus = STATUS_INVALID_IMAGE_PROTECT;
DIE(("ReadFile failed, status %08X\n", ReturnedStatus));
}
ASSERT(pData);
ASSERT(pBuffer);
ASSERT(cbReadSize > 0);
nStatus = STATUS_INVALID_IMAGE_FORMAT;
/* the buffer doesn't contain the file header */
if(cbReadSize < RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader))
DIE(("The file doesn't contain the PE file header\n"));
pinhNtHeader = pData;
/* object still not aligned: copy it to the beginning of the buffer */
if((UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
{
ASSERT((UINT_PTR)pBuffer % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) == 0);
RtlMoveMemory(pBuffer, pData, cbReadSize);
pinhNtHeader = pBuffer;
}
/* invalid NT header */
nStatus = STATUS_INVALID_IMAGE_PROTECT;
if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
nStatus = STATUS_INVALID_IMAGE_FORMAT;
if(!Intsafe_AddULong32(&cbNtHeaderSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
DIE(("The full NT header is too large\n"));
/* the buffer doesn't contain the whole NT header */
if(cbReadSize < cbNtHeaderSize)
DIE(("The file doesn't contain the full NT header\n"));
}
else
{
ULONG cbOptHeaderOffsetSize = 0;
nStatus = STATUS_INVALID_IMAGE_PROTECT;
/* don't trust an invalid NT header */
if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
nStatus = STATUS_INVALID_IMAGE_FORMAT;
if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, cbOptHeaderOffsetSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
DIE(("The NT header is too large, SizeOfOptionalHeader is %X\n", pinhNtHeader->FileHeader.SizeOfOptionalHeader));
/* the buffer doesn't contain the whole NT header: read it from the file */
if(cbOptHeaderOffsetSize > FileHeaderSize)
goto l_ReadHeaderFromFile;
}
/* read information from the NT header */
piohOptHeader = &pinhNtHeader->OptionalHeader;
cbOptHeaderSize = pinhNtHeader->FileHeader.SizeOfOptionalHeader;
nStatus = STATUS_INVALID_IMAGE_FORMAT;
if(!RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Magic))
DIE(("The optional header doesn't contain the Magic field, SizeOfOptionalHeader is %X\n", cbOptHeaderSize));
/* ASSUME: RtlZeroMemory(ImageSectionObject, sizeof(*ImageSectionObject)); */
switch(piohOptHeader->Magic)
{
case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
#ifndef _WIN64
nStatus = STATUS_INVALID_IMAGE_WIN_64;
DIE(("Win64 optional header, unsupported\n"));
#else
// Fall through.
#endif
case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
break;
default:
DIE(("Unrecognized optional header, Magic is %X\n", piohOptHeader->Magic));
}
if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SectionAlignment) &&
RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, FileAlignment))
{
/* See [1], section 3.4.2 */
if(piohOptHeader->SectionAlignment < PAGE_SIZE)
{
if(piohOptHeader->FileAlignment != piohOptHeader->SectionAlignment)
DIE(("Sections aren't page-aligned and the file alignment isn't the same\n"));
}
else if(piohOptHeader->SectionAlignment < piohOptHeader->FileAlignment)
DIE(("The section alignment is smaller than the file alignment\n"));
nSectionAlignment = piohOptHeader->SectionAlignment;
nFileAlignment = piohOptHeader->FileAlignment;
if(!IsPowerOf2(nSectionAlignment) || !IsPowerOf2(nFileAlignment))
DIE(("The section alignment (%u) and file alignment (%u) aren't both powers of 2\n", nSectionAlignment, nFileAlignment));
}
else
{
nSectionAlignment = PAGE_SIZE;
nFileAlignment = PAGE_SIZE;
}
ASSERT(IsPowerOf2(nSectionAlignment));
ASSERT(IsPowerOf2(nFileAlignment));
switch(piohOptHeader->Magic)
{
/* PE32 */
case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
{
if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, ImageBase))
ImageBase = piohOptHeader->ImageBase;
if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfImage))
ImageSectionObject->ImageInformation.ImageFileSize = piohOptHeader->SizeOfImage;
if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackReserve))
ImageSectionObject->ImageInformation.MaximumStackSize = piohOptHeader->SizeOfStackReserve;
if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackCommit))
ImageSectionObject->ImageInformation.CommittedStackSize = piohOptHeader->SizeOfStackCommit;
if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Subsystem))
{
ImageSectionObject->ImageInformation.SubSystemType = piohOptHeader->Subsystem;
if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MinorSubsystemVersion) &&
RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MajorSubsystemVersion))
{
ImageSectionObject->ImageInformation.SubSystemMinorVersion = piohOptHeader->MinorSubsystemVersion;
ImageSectionObject->ImageInformation.SubSystemMajorVersion = piohOptHeader->MajorSubsystemVersion;
}
}
if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, AddressOfEntryPoint))
{
ImageSectionObject->ImageInformation.TransferAddress = (PVOID) (ImageBase +
piohOptHeader->AddressOfEntryPoint);
}
if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfCode))
ImageSectionObject->ImageInformation.ImageContainsCode = piohOptHeader->SizeOfCode != 0;
else
ImageSectionObject->ImageInformation.ImageContainsCode = TRUE;
if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, AddressOfEntryPoint))
{
if (piohOptHeader->AddressOfEntryPoint == 0)
{
ImageSectionObject->ImageInformation.ImageContainsCode = FALSE;
}
}
if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, LoaderFlags))
ImageSectionObject->ImageInformation.LoaderFlags = piohOptHeader->LoaderFlags;
if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, DllCharacteristics))
{
ImageSectionObject->ImageInformation.DllCharacteristics = piohOptHeader->DllCharacteristics;
/*
* Since we don't really implement SxS yet and LD doesn't supoprt /ALLOWISOLATION:NO, hard-code
* this flag here, which will prevent the loader and other code from doing any .manifest or SxS
* magic to any binary.
*
* This will break applications that depend on SxS when running with real Windows Kernel32/SxS/etc
* but honestly that's not tested. It will also break them when running no ReactOS once we implement
* the SxS support -- at which point, duh, this should be removed.
*
* But right now, any app depending on SxS is already broken anyway, so this flag only helps.
*/
ImageSectionObject->ImageInformation.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_NO_ISOLATION;
}
break;
}
#ifdef _WIN64
/* PE64 */
case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
{
const IMAGE_OPTIONAL_HEADER64 * pioh64OptHeader;
pioh64OptHeader = (const IMAGE_OPTIONAL_HEADER64 *)piohOptHeader;
if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, ImageBase))
{
ImageBase = pioh64OptHeader->ImageBase;
if(pioh64OptHeader->ImageBase > MAXULONG_PTR)
DIE(("ImageBase exceeds the address space\n"));
}
if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfImage))
{
if(pioh64OptHeader->SizeOfImage > MAXULONG_PTR)
DIE(("SizeOfImage exceeds the address space\n"));
ImageSectionObject->ImageInformation.ImageFileSize = pioh64OptHeader->SizeOfImage;
}
if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackReserve))
{
if(pioh64OptHeader->SizeOfStackReserve > MAXULONG_PTR)
DIE(("SizeOfStackReserve exceeds the address space\n"));
ImageSectionObject->ImageInformation.MaximumStackSize = (ULONG_PTR) pioh64OptHeader->SizeOfStackReserve;
}
if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackCommit))
{
if(pioh64OptHeader->SizeOfStackCommit > MAXULONG_PTR)
DIE(("SizeOfStackCommit exceeds the address space\n"));
ImageSectionObject->ImageInformation.CommittedStackSize = (ULONG_PTR) pioh64OptHeader->SizeOfStackCommit;
}
if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, Subsystem))
{
ImageSectionObject->ImageInformation.SubSystemType = pioh64OptHeader->Subsystem;
if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, MinorSubsystemVersion) &&
RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, MajorSubsystemVersion))
{
ImageSectionObject->ImageInformation.SubSystemMinorVersion = pioh64OptHeader->MinorSubsystemVersion;
ImageSectionObject->ImageInformation.SubSystemMajorVersion = pioh64OptHeader->MajorSubsystemVersion;
}
}
if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, AddressOfEntryPoint))
{
ImageSectionObject->ImageInformation.TransferAddress = (PVOID) (ImageBase +
pioh64OptHeader->AddressOfEntryPoint);
}
if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfCode))
ImageSectionObject->ImageInformation.ImageContainsCode = pioh64OptHeader->SizeOfCode != 0;
else
ImageSectionObject->ImageInformation.ImageContainsCode = TRUE;
if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, AddressOfEntryPoint))
{
if (pioh64OptHeader->AddressOfEntryPoint == 0)
{
ImageSectionObject->ImageInformation.ImageContainsCode = FALSE;
}
}
if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, LoaderFlags))
ImageSectionObject->ImageInformation.LoaderFlags = pioh64OptHeader->LoaderFlags;
if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, DllCharacteristics))
ImageSectionObject->ImageInformation.DllCharacteristics = pioh64OptHeader->DllCharacteristics;
break;
}
#endif // _WIN64
}
/* [1], section 3.4.2 */
if((ULONG_PTR)ImageBase % 0x10000)
DIE(("ImageBase is not aligned on a 64KB boundary"));
ImageSectionObject->ImageInformation.ImageCharacteristics = pinhNtHeader->FileHeader.Characteristics;
ImageSectionObject->ImageInformation.Machine = pinhNtHeader->FileHeader.Machine;
ImageSectionObject->ImageInformation.GpValue = 0;
ImageSectionObject->ImageInformation.ZeroBits = 0;
ImageSectionObject->BasedAddress = (PVOID)ImageBase;
/* SECTION HEADERS */
nStatus = STATUS_INVALID_IMAGE_FORMAT;
/* see [1], section 3.3 */
if(pinhNtHeader->FileHeader.NumberOfSections > 96)
DIE(("Too many sections, NumberOfSections is %u\n", pinhNtHeader->FileHeader.NumberOfSections));
/*
* the additional segment is for the file's headers. They need to be present for
* the benefit of the dynamic loader (to locate exports, defaults for thread
* parameters, resources, etc.)
*/
ImageSectionObject->NrSegments = pinhNtHeader->FileHeader.NumberOfSections + 1;
/* file offset for the section headers */
if(!Intsafe_AddULong32(&cbSectionHeadersOffset, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
DIE(("Offset overflow\n"));
if(!Intsafe_AddULong32(&cbSectionHeadersOffset, cbSectionHeadersOffset, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
DIE(("Offset overflow\n"));
/* size of the section headers */
ASSERT(Intsafe_CanMulULong32(pinhNtHeader->FileHeader.NumberOfSections, sizeof(IMAGE_SECTION_HEADER)));
cbSectionHeadersSize = pinhNtHeader->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
if(!Intsafe_AddULong32(&cbSectionHeadersOffsetSize, cbSectionHeadersOffset, cbSectionHeadersSize))
DIE(("Section headers too large\n"));
/* size of the executable's headers */
if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfHeaders))
{
// if(!IsAligned(piohOptHeader->SizeOfHeaders, nFileAlignment))
// DIE(("SizeOfHeaders is not aligned\n"));
if(cbSectionHeadersSize > piohOptHeader->SizeOfHeaders)
DIE(("The section headers overflow SizeOfHeaders\n"));
cbHeadersSize = piohOptHeader->SizeOfHeaders;
}
else if(!AlignUp(&cbHeadersSize, cbSectionHeadersOffsetSize, nFileAlignment))
DIE(("Overflow aligning the size of headers\n"));
if(pBuffer)
{
ExFreePool(pBuffer);
pBuffer = NULL;
}
/* WARNING: pinhNtHeader IS NO LONGER USABLE */
/* WARNING: piohOptHeader IS NO LONGER USABLE */
/* WARNING: pioh64OptHeader IS NO LONGER USABLE */
if(FileHeaderSize < cbSectionHeadersOffsetSize)
pishSectionHeaders = NULL;
else
{
/*
* we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
* and FileHeaderSize >= cbSectionHeadersOffsetSize, so this holds true too
*/
ASSERT(Intsafe_CanOffsetPointer(FileHeader, cbSectionHeadersOffset));
pishSectionHeaders = (PVOID)((UINT_PTR)FileHeader + cbSectionHeadersOffset);
}
/*
* the buffer doesn't contain the section headers, or the alignment is wrong:
* read the headers from the file
*/
if(FileHeaderSize < cbSectionHeadersOffsetSize ||
(UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0)
{
PVOID pData;
ULONG cbReadSize;
lnOffset.QuadPart = cbSectionHeadersOffset;
/* read the header from the file */
nStatus = ReadFileCb(File, &lnOffset, cbSectionHeadersSize, &pData, &pBuffer, &cbReadSize);
if(!NT_SUCCESS(nStatus))
DIE(("ReadFile failed with status %08X\n", nStatus));
ASSERT(pData);
ASSERT(pBuffer);
ASSERT(cbReadSize > 0);
nStatus = STATUS_INVALID_IMAGE_FORMAT;
/* the buffer doesn't contain all the section headers */
if(cbReadSize < cbSectionHeadersSize)
DIE(("The file doesn't contain all of the section headers\n"));
pishSectionHeaders = pData;
/* object still not aligned: copy it to the beginning of the buffer */
if((UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0)
{
ASSERT((UINT_PTR)pBuffer % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) == 0);
RtlMoveMemory(pBuffer, pData, cbReadSize);
pishSectionHeaders = pBuffer;
}
}
/* SEGMENTS */
/* allocate the segments */
nStatus = STATUS_INSUFFICIENT_RESOURCES;
ImageSectionObject->Segments = AllocateSegmentsCb(ImageSectionObject->NrSegments);
if(ImageSectionObject->Segments == NULL)
DIE(("AllocateSegments failed\n"));
/* initialize the headers segment */
pssSegments = ImageSectionObject->Segments;
// ASSERT(IsAligned(cbHeadersSize, nFileAlignment));
if(!AlignUp(&nFileSizeOfHeaders, cbHeadersSize, nFileAlignment))
DIE(("Cannot align the size of the section headers\n"));
nPrevVirtualEndOfSegment = ALIGN_UP_BY(cbHeadersSize, nSectionAlignment);
if (nPrevVirtualEndOfSegment < cbHeadersSize)
DIE(("Cannot align the size of the section headers\n"));
pssSegments[0].Image.FileOffset = 0;
pssSegments[0].Protection = PAGE_READONLY;
pssSegments[0].Length.QuadPart = nPrevVirtualEndOfSegment;
pssSegments[0].RawLength.QuadPart = nFileSizeOfHeaders;
pssSegments[0].Image.VirtualAddress = 0;
pssSegments[0].Image.Characteristics = 0;
pssSegments[0].WriteCopy = TRUE;
/* skip the headers segment */
++ pssSegments;
nStatus = STATUS_INVALID_IMAGE_FORMAT;
ASSERT(ImageSectionObject->RefCount > 0);
/* convert the executable sections into segments. See also [1], section 4 */
for(i = 0; i < ImageSectionObject->NrSegments - 1; ++ i)
{
ULONG nCharacteristics;
/* validate the alignment */
if(!IsAligned(pishSectionHeaders[i].VirtualAddress, nSectionAlignment))
DIE(("Image.VirtualAddress[%u] is not aligned\n", i));
/* sections must be contiguous, ordered by base address and non-overlapping */
if(pishSectionHeaders[i].VirtualAddress != nPrevVirtualEndOfSegment)
DIE(("Memory gap between section %u and the previous\n", i));
/* ignore explicit BSS sections */
if(pishSectionHeaders[i].SizeOfRawData != 0)
{
/* validate the alignment */
#if 0
/* Yes, this should be a multiple of FileAlignment, but there's
* stuff out there that isn't. We can cope with that
*/
if(!IsAligned(pishSectionHeaders[i].SizeOfRawData, nFileAlignment))
DIE(("SizeOfRawData[%u] is not aligned\n", i));
#endif
// if(!IsAligned(pishSectionHeaders[i].PointerToRawData, nFileAlignment))
// DIE(("PointerToRawData[%u] is not aligned\n", i));
if(!Intsafe_CanAddULong32(pishSectionHeaders[i].PointerToRawData, pishSectionHeaders[i].SizeOfRawData))
DIE(("SizeOfRawData[%u] too large\n", i));
/* conversion */
pssSegments[i].Image.FileOffset = pishSectionHeaders[i].PointerToRawData;
pssSegments[i].RawLength.QuadPart = pishSectionHeaders[i].SizeOfRawData;
}
else
{
/* FIXME: Should reset PointerToRawData to 0 in the image mapping */
ASSERT(pssSegments[i].Image.FileOffset == 0);
ASSERT(pssSegments[i].RawLength.QuadPart == 0);
}
ASSERT(Intsafe_CanAddLong64(pssSegments[i].Image.FileOffset, pssSegments[i].RawLength.QuadPart));
nCharacteristics = pishSectionHeaders[i].Characteristics;
/* no explicit protection */
if((nCharacteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)) == 0)
{
if(nCharacteristics & IMAGE_SCN_CNT_CODE)
nCharacteristics |= IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ;
if(nCharacteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
if(nCharacteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
}
/* see table above */
pssSegments[i].Protection = SectionCharacteristicsToProtect[nCharacteristics >> 28];
pssSegments[i].WriteCopy = !(nCharacteristics & IMAGE_SCN_MEM_SHARED);
if(pishSectionHeaders[i].Misc.VirtualSize == 0)
pssSegments[i].Length.QuadPart = pishSectionHeaders[i].SizeOfRawData;
else
pssSegments[i].Length.QuadPart = pishSectionHeaders[i].Misc.VirtualSize;
AlignedLength = ALIGN_UP_BY(pssSegments[i].Length.LowPart, nSectionAlignment);
if(AlignedLength < pssSegments[i].Length.LowPart)
DIE(("Cannot align the virtual size of section %u\n", i));
pssSegments[i].Length.LowPart = AlignedLength;
if(pssSegments[i].Length.QuadPart == 0)
DIE(("Virtual size of section %u is null\n", i));
pssSegments[i].Image.VirtualAddress = pishSectionHeaders[i].VirtualAddress;
pssSegments[i].Image.Characteristics = pishSectionHeaders[i].Characteristics;
/* ensure the memory image is no larger than 4GB */
nPrevVirtualEndOfSegment = (ULONG_PTR)(pssSegments[i].Image.VirtualAddress + pssSegments[i].Length.QuadPart);
if (nPrevVirtualEndOfSegment < pssSegments[i].Image.VirtualAddress)
DIE(("The image is too large\n"));
}
if(nSectionAlignment >= PAGE_SIZE)
*Flags |= EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED;
/* Success */
nStatus = STATUS_SUCCESS;// STATUS_ROS_EXEFMT_LOADED_FORMAT | EXEFMT_LOADED_PE32;
l_Return:
if(pBuffer)
ExFreePool(pBuffer);
return nStatus;
}
/*
* FUNCTION: Waits in kernel mode indefinitely for a file object lock.
* ARGUMENTS: PFILE_OBJECT to wait for.
* RETURNS: Status of the wait.
*/
NTSTATUS
MmspWaitForFileLock(PFILE_OBJECT File)
{
return STATUS_SUCCESS;
//return KeWaitForSingleObject(&File->Lock, 0, KernelMode, FALSE, NULL);
}
VOID
NTAPI
MmpFreePageFileSegment(PMM_SECTION_SEGMENT Segment)
{
ULONG Length;
LARGE_INTEGER Offset;
ULONG_PTR Entry;
SWAPENTRY SavedSwapEntry;
PFN_NUMBER Page;
Page = 0;
MmLockSectionSegment(Segment);
Length = PAGE_ROUND_UP(Segment->Length.QuadPart);
for (Offset.QuadPart = 0; Offset.QuadPart < Length; Offset.QuadPart += PAGE_SIZE)
{
Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
if (Entry)
{
MmSetPageEntrySectionSegment(Segment, &Offset, 0);
if (IS_SWAP_FROM_SSE(Entry))
{
MmFreeSwapPage(SWAPENTRY_FROM_SSE(Entry));
}
else
{
Page = PFN_FROM_SSE(Entry);
SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
if (SavedSwapEntry != 0)
{
MmSetSavedSwapEntryPage(Page, 0);
MmFreeSwapPage(SavedSwapEntry);
}
MmReleasePageMemoryConsumer(MC_USER, Page);
}
}
}
MmUnlockSectionSegment(Segment);
}
static
VOID
NTAPI
FreeSegmentPage(PMM_SECTION_SEGMENT Segment, PLARGE_INTEGER Offset)
{
ULONG_PTR Entry;
PFN_NUMBER Page;
MmLockSectionSegment(Segment);
Entry = MmGetPageEntrySectionSegment(Segment, Offset);
MmUnlockSectionSegment(Segment);
/* This must be either a valid entry or nothing */
ASSERT(!IS_SWAP_FROM_SSE(Entry));
/* There should be no reference anymore */
ASSERT(SHARE_COUNT_FROM_SSE(Entry) == 0);
Page = PFN_FROM_SSE(Entry);
/* If there is a page, this must be because it's still dirty */
ASSERT(Page != 0);
/* Write the page */
if (IS_DIRTY_SSE(Entry))
MiWritePage(Segment, Offset->QuadPart, Page);
MmReleasePageMemoryConsumer(MC_USER, Page);
}
VOID
NTAPI
MmDereferenceSegment(PMM_SECTION_SEGMENT Segment)
{
KIRQL OldIrql;
/* Lock the PFN lock because we mess around with SectionObjectPointers */
OldIrql = MiAcquirePfnLock();
if (InterlockedDecrement64(Segment->ReferenceCount) > 0)
{
/* Nothing to do yet */
MiReleasePfnLock(OldIrql);
return;
}
*Segment->Flags |= MM_SEGMENT_INDELETE;
MiReleasePfnLock(OldIrql);
/* Flush the segment */
if (*Segment->Flags & MM_DATAFILE_SEGMENT)
{
/* Free the page table. This will flush any remaining dirty data */
MmFreePageTablesSectionSegment(Segment, FreeSegmentPage);
OldIrql = MiAcquirePfnLock();
/* Delete the pointer on the file */
ASSERT(Segment->FileObject->SectionObjectPointer->DataSectionObject == Segment);
Segment->FileObject->SectionObjectPointer->DataSectionObject = NULL;
MiReleasePfnLock(OldIrql);
ObDereferenceObject(Segment->FileObject);
ExFreePoolWithTag(Segment, TAG_MM_SECTION_SEGMENT);
}
else
{
/* Most grotesque thing ever */
PMM_IMAGE_SECTION_OBJECT ImageSectionObject = CONTAINING_RECORD(Segment->ReferenceCount, MM_IMAGE_SECTION_OBJECT, RefCount);
PMM_SECTION_SEGMENT SectionSegments;
ULONG NrSegments;
ULONG i;
OldIrql = MiAcquirePfnLock();
/* Delete the pointer on the file */
ASSERT(ImageSectionObject->FileObject->SectionObjectPointer->ImageSectionObject == ImageSectionObject);
ImageSectionObject->FileObject->SectionObjectPointer->ImageSectionObject = NULL;
MiReleasePfnLock(OldIrql);
ObDereferenceObject(ImageSectionObject->FileObject);
NrSegments = ImageSectionObject->NrSegments;
SectionSegments = ImageSectionObject->Segments;
for (i = 0; i < NrSegments; i++)
{
if (SectionSegments[i].Image.Characteristics & IMAGE_SCN_MEM_SHARED)
{
MmpFreePageFileSegment(&SectionSegments[i]);
}
MmFreePageTablesSectionSegment(&SectionSegments[i], NULL);
}
ExFreePoolWithTag(ImageSectionObject->Segments, TAG_MM_SECTION_SEGMENT);
ExFreePoolWithTag(ImageSectionObject, TAG_MM_SECTION_SEGMENT);
}
}
VOID
NTAPI
MmSharePageEntrySectionSegment(PMM_SECTION_SEGMENT Segment,
PLARGE_INTEGER Offset)
{
ULONG_PTR Entry;
Entry = MmGetPageEntrySectionSegment(Segment, Offset);
if (Entry == 0)
{
DPRINT1("Entry == 0 for MmSharePageEntrySectionSegment\n");
KeBugCheck(MEMORY_MANAGEMENT);
}
if (SHARE_COUNT_FROM_SSE(Entry) == MAX_SHARE_COUNT)
{
DPRINT1("Maximum share count reached\n");
KeBugCheck(MEMORY_MANAGEMENT);
}
if (IS_SWAP_FROM_SSE(Entry))
{
KeBugCheck(MEMORY_MANAGEMENT);
}
MmSetPageEntrySectionSegment(Segment, Offset, BUMPREF_SSE(Entry));
}
BOOLEAN
NTAPI
MmUnsharePageEntrySectionSegment(PMEMORY_AREA MemoryArea,
PMM_SECTION_SEGMENT Segment,
PLARGE_INTEGER Offset,
BOOLEAN Dirty,
BOOLEAN PageOut,
ULONG_PTR *InEntry)
{
ULONG_PTR Entry = InEntry ? *InEntry : MmGetPageEntrySectionSegment(Segment, Offset);
PFN_NUMBER Page = PFN_FROM_SSE(Entry);
BOOLEAN IsDataMap = BooleanFlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT);
if (Entry == 0)
{
DPRINT1("Entry == 0 for MmUnsharePageEntrySectionSegment\n");
KeBugCheck(MEMORY_MANAGEMENT);
}
if (SHARE_COUNT_FROM_SSE(Entry) == 0)
{
DPRINT1("Zero share count for unshare (Seg %p Offset %x Page %x)\n", Segment, Offset->LowPart, PFN_FROM_SSE(Entry));
KeBugCheck(MEMORY_MANAGEMENT);
}
if (IS_SWAP_FROM_SSE(Entry))
{
KeBugCheck(MEMORY_MANAGEMENT);
}
Entry = DECREF_SSE(Entry);
if (Dirty) Entry = DIRTY_SSE(Entry);
/* If we are paging-out, pruning the page for real will be taken care of in MmCheckDirtySegment */
if ((SHARE_COUNT_FROM_SSE(Entry) > 0) || PageOut)
{
/* Update the page mapping in the segment and we're done */
MmSetPageEntrySectionSegment(Segment, Offset, Entry);
return FALSE;
}
/* We are pruning the last mapping on this page. See if we can keep it a bit more. */
ASSERT(!PageOut);
if (IsDataMap)
{
/* We can always keep memory in for data maps */
MmSetPageEntrySectionSegment(Segment, Offset, Entry);
return FALSE;
}
if (!BooleanFlagOn(Segment->Image.Characteristics, IMAGE_SCN_MEM_SHARED))
{
/* So this must have been a read-only page. Keep it ! */
ASSERT(Segment->WriteCopy);
ASSERT(!IS_DIRTY_SSE(Entry));
ASSERT(MmGetSavedSwapEntryPage(Page) == 0);
MmSetPageEntrySectionSegment(Segment, Offset, Entry);
return FALSE;
}
/*
* So this is a page for a shared section of a DLL.
* We can keep it if it is not dirty.
*/
SWAPENTRY SwapEntry = MmGetSavedSwapEntryPage(Page);
if ((SwapEntry == 0) && !IS_DIRTY_SSE(Entry))
{
MmSetPageEntrySectionSegment(Segment, Offset, Entry);
return FALSE;
}
/* No more processes are referencing this shared dirty page. Ditch it. */
if (SwapEntry)
{
MmSetSavedSwapEntryPage(Page, 0);
MmFreeSwapPage(SwapEntry);
}
MmSetPageEntrySectionSegment(Segment, Offset, 0);
MmReleasePageMemoryConsumer(MC_USER, Page);
return TRUE;
}
static
NTSTATUS
MiCopyFromUserPage(PFN_NUMBER DestPage, const VOID *SrcAddress)
{
PEPROCESS Process;
KIRQL Irql;
PVOID DestAddress;
Process = PsGetCurrentProcess();
DestAddress = MiMapPageInHyperSpace(Process, DestPage, &Irql);
if (DestAddress == NULL)
{
return STATUS_NO_MEMORY;
}
ASSERT((ULONG_PTR)DestAddress % PAGE_SIZE == 0);
ASSERT((ULONG_PTR)SrcAddress % PAGE_SIZE == 0);
RtlCopyMemory(DestAddress, SrcAddress, PAGE_SIZE);
MiUnmapPageInHyperSpace(Process, DestAddress, Irql);
return STATUS_SUCCESS;
}
static
NTSTATUS
NTAPI
MmMakeSegmentResident(
_In_ PMM_SECTION_SEGMENT Segment,
_In_ LONGLONG Offset,
_In_ ULONG Length,
_In_opt_ PLARGE_INTEGER ValidDataLength)
{
/* Let's use a 64K granularity. */
LONGLONG RangeStart, RangeEnd;
NTSTATUS Status;
PFILE_OBJECT FileObject = Segment->FileObject;
/* Calculate our range, aligned on 64K if possible. */
Status = RtlLongLongAdd(Offset, Length, &RangeEnd);
ASSERT(NT_SUCCESS(Status));
if (!NT_SUCCESS(Status))
return Status;
RangeStart = Offset - (Offset % _64K);
if (RangeEnd % _64K)
RangeEnd += _64K - (RangeEnd % _64K);
/* Clamp if needed */
if (!FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT))
{
if (RangeEnd > Segment->RawLength.QuadPart)
RangeEnd = Segment->RawLength.QuadPart;
}
/* Let's gooooooooo */
for ( ; RangeStart < RangeEnd; RangeStart += _64K)
{
/* First take a look at where we miss pages */
ULONG ToReadPageBits = 0;
LONGLONG ChunkEnd = RangeStart + _64K;
if (ChunkEnd > RangeEnd)
ChunkEnd = RangeEnd;
MmLockSectionSegment(Segment);
for (LONGLONG ChunkOffset = RangeStart; ChunkOffset < ChunkEnd; ChunkOffset += PAGE_SIZE)
{
LARGE_INTEGER CurrentOffset;
CurrentOffset.QuadPart = ChunkOffset;
ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &CurrentOffset);
/* Let any pending read proceed */
while (MM_IS_WAIT_PTE(Entry))
{
MmUnlockSectionSegment(Segment);
KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
MmLockSectionSegment(Segment);
Entry = MmGetPageEntrySectionSegment(Segment, &CurrentOffset);
}
if (Entry != 0)
{
/* There is a page here. Or a swap entry. Or whatever... */
continue;
}
ToReadPageBits |= 1UL << ((ChunkOffset - RangeStart) >> PAGE_SHIFT);
/* Put a wait entry here */
MmSetPageEntrySectionSegment(Segment, &CurrentOffset, MAKE_SWAP_SSE(MM_WAIT_ENTRY));
}
MmUnlockSectionSegment(Segment);
if (ToReadPageBits == 0)
{
/* Nothing to do for this chunk */
continue;
}
/* Now perform the actual read */
LONGLONG ChunkOffset = RangeStart;
while (ChunkOffset < ChunkEnd)
{
/* Move forward if there is a hole */
ULONG BitSet;
if (!_BitScanForward(&BitSet, ToReadPageBits))
{
/* Nothing more to read */
break;
}
ToReadPageBits >>= BitSet;
ChunkOffset += BitSet * PAGE_SIZE;
ASSERT(ChunkOffset < ChunkEnd);
/* Get the range we have to read */
_BitScanForward(&BitSet, ~ToReadPageBits);
ULONG ReadLength = BitSet * PAGE_SIZE;
ASSERT(ReadLength <= _64K);
/* Clamp (This is for image mappings */
if ((ChunkOffset + ReadLength) > ChunkEnd)
ReadLength = ChunkEnd - ChunkOffset;
ASSERT(ReadLength != 0);
/* Allocate a MDL */
PMDL Mdl = IoAllocateMdl(NULL, ReadLength, FALSE, FALSE, NULL);
if (!Mdl)
{
/* Damn. Roll-back. */
MmLockSectionSegment(Segment);
while (ChunkOffset < ChunkEnd)
{
if (ToReadPageBits & 1)
{
LARGE_INTEGER CurrentOffset;
CurrentOffset.QuadPart = ChunkOffset;
ASSERT(MM_IS_WAIT_PTE(MmGetPageEntrySectionSegment(Segment, &CurrentOffset)));
MmSetPageEntrySectionSegment(Segment, &CurrentOffset, 0);
}
ToReadPageBits >>= 1;
ChunkOffset += PAGE_SIZE;
}
MmUnlockSectionSegment(Segment);
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Get our pages */
PPFN_NUMBER Pages = MmGetMdlPfnArray(Mdl);
RtlZeroMemory(Pages, BYTES_TO_PAGES(ReadLength) * sizeof(PFN_NUMBER));
for (UINT i = 0; i < BYTES_TO_PAGES(ReadLength); i++)
{
/* MmRequestPageMemoryConsumer succeeds or bugchecks */
(void)MmRequestPageMemoryConsumer(MC_USER, FALSE, &Pages[i]);
}
Mdl->MdlFlags |= MDL_PAGES_LOCKED | MDL_IO_PAGE_READ;
LARGE_INTEGER FileOffset;
FileOffset.QuadPart = Segment->Image.FileOffset + ChunkOffset;
/* Clamp to VDL */
if (ValidDataLength && ((FileOffset.QuadPart + ReadLength) > ValidDataLength->QuadPart))
{
if (FileOffset.QuadPart > ValidDataLength->QuadPart)
{
/* Great, nothing to read. */
goto AssignPagesToSegment;
}
Mdl->Size = (FileOffset.QuadPart + ReadLength) - ValidDataLength->QuadPart;
}
KEVENT Event;
KeInitializeEvent(&Event, NotificationEvent, FALSE);
/* Disable APCs */
KIRQL OldIrql;
KeRaiseIrql(APC_LEVEL, &OldIrql);
IO_STATUS_BLOCK Iosb;
Status = IoPageRead(FileObject, Mdl, &FileOffset, &Event, &Iosb);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event, WrPageIn, KernelMode, FALSE, NULL);
Status = Iosb.Status;
}
if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
{
MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
}
KeLowerIrql(OldIrql);
if (Status == STATUS_END_OF_FILE)
{
DPRINT1("Got STATUS_END_OF_FILE at offset %I64d for file %wZ.\n", FileOffset.QuadPart, &FileObject->FileName);
Status = STATUS_SUCCESS;
}
if (!NT_SUCCESS(Status))
{
/* Damn. Roll back. */
for (UINT i = 0; i < BYTES_TO_PAGES(ReadLength); i++)
MmReleasePageMemoryConsumer(MC_USER, Pages[i]);
MmLockSectionSegment(Segment);
while (ChunkOffset < ChunkEnd)
{
if (ToReadPageBits & 1)
{
LARGE_INTEGER CurrentOffset;
CurrentOffset.QuadPart = ChunkOffset;
ASSERT(MM_IS_WAIT_PTE(MmGetPageEntrySectionSegment(Segment, &CurrentOffset)));
MmSetPageEntrySectionSegment(Segment, &CurrentOffset, 0);
}
ToReadPageBits >>= 1;
ChunkOffset += PAGE_SIZE;
}
MmUnlockSectionSegment(Segment);
IoFreeMdl(Mdl);;
return Status;
}
AssignPagesToSegment:
MmLockSectionSegment(Segment);
for (UINT i = 0; i < BYTES_TO_PAGES(ReadLength); i++)
{
LARGE_INTEGER CurrentOffset;
CurrentOffset.QuadPart = ChunkOffset + (i * PAGE_SIZE);
ASSERT(MM_IS_WAIT_PTE(MmGetPageEntrySectionSegment(Segment, &CurrentOffset)));
MmSetPageEntrySectionSegment(Segment, &CurrentOffset, MAKE_SSE(Pages[i] << PAGE_SHIFT, 0));
}
MmUnlockSectionSegment(Segment);
IoFreeMdl(Mdl);
ToReadPageBits >>= BitSet;
ChunkOffset += BitSet * PAGE_SIZE;
}
}
return STATUS_SUCCESS;
}
static VOID
MmAlterViewAttributes(PMMSUPPORT AddressSpace,
PVOID BaseAddress,
SIZE_T RegionSize,
ULONG OldType,
ULONG OldProtect,
ULONG NewType,
ULONG NewProtect)
{
PMEMORY_AREA MemoryArea;
PMM_SECTION_SEGMENT Segment;
BOOLEAN DoCOW = FALSE;
ULONG i;
PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
ASSERT(MemoryArea != NULL);
Segment = MemoryArea->SectionData.Segment;
MmLockSectionSegment(Segment);
if ((Segment->WriteCopy) &&
(NewProtect == PAGE_READWRITE || NewProtect == PAGE_EXECUTE_READWRITE))
{
DoCOW = TRUE;
}
if (OldProtect != NewProtect)
{
for (i = 0; i < PAGE_ROUND_UP(RegionSize) / PAGE_SIZE; i++)
{
SWAPENTRY SwapEntry;
PVOID Address = (char*)BaseAddress + (i * PAGE_SIZE);
ULONG Protect = NewProtect;
/* Wait for a wait entry to disappear */
do
{
MmGetPageFileMapping(Process, Address, &SwapEntry);
if (SwapEntry != MM_WAIT_ENTRY)
break;
MiWaitForPageEvent(Process, Address);
}
while (TRUE);
/*
* If we doing COW for this segment then check if the page is
* already private.
*/
if (DoCOW && MmIsPagePresent(Process, Address))
{
LARGE_INTEGER Offset;
ULONG_PTR Entry;
PFN_NUMBER Page;
Offset.QuadPart = (ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea)
+ MemoryArea->SectionData.ViewOffset;
Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
/*
* An MM_WAIT_ENTRY is ok in this case... It'll just count as
* IS_SWAP_FROM_SSE and we'll do the right thing.
*/
Page = MmGetPfnForProcess(Process, Address);
Protect = PAGE_READONLY;
if (IS_SWAP_FROM_SSE(Entry) || PFN_FROM_SSE(Entry) != Page)
{
Protect = NewProtect;
}
}
if (MmIsPagePresent(Process, Address) || MmIsDisabledPage(Process, Address))
{
MmSetPageProtect(Process, Address,
Protect);
}
}
}
MmUnlockSectionSegment(Segment);
}
NTSTATUS
NTAPI
MmNotPresentFaultSectionView(PMMSUPPORT AddressSpace,
MEMORY_AREA* MemoryArea,
PVOID Address,
BOOLEAN Locked)
{
LARGE_INTEGER Offset;
PFN_NUMBER Page;
NTSTATUS Status;
PMM_SECTION_SEGMENT Segment;
ULONG_PTR Entry;
ULONG_PTR Entry1;
ULONG Attributes;
PMM_REGION Region;
BOOLEAN HasSwapEntry;
PVOID PAddress;
PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
SWAPENTRY SwapEntry;
/*
* There is a window between taking the page fault and locking the
* address space when another thread could load the page so we check
* that.
*/
if (MmIsPagePresent(Process, Address))
{
return STATUS_SUCCESS;
}
if (MmIsDisabledPage(Process, Address))
{
return STATUS_ACCESS_VIOLATION;
}
/*
* Check for the virtual memory area being deleted.
*/
if (MemoryArea->DeleteInProgress)
{
return STATUS_UNSUCCESSFUL;
}
PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
Offset.QuadPart = (ULONG_PTR)PAddress - MA_GetStartingAddress(MemoryArea)
+ MemoryArea->SectionData.ViewOffset;
Segment = MemoryArea->SectionData.Segment;
Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
&MemoryArea->SectionData.RegionListHead,
Address, NULL);
ASSERT(Region != NULL);
/* Check for a NOACCESS mapping */
if (Region->Protect & PAGE_NOACCESS)
{
return STATUS_ACCESS_VIOLATION;
}
if (Region->Protect & PAGE_GUARD)
{
/* Remove it */
Status = MmAlterRegion(AddressSpace, (PVOID)MA_GetStartingAddress(MemoryArea),
&MemoryArea->SectionData.RegionListHead,
Address, PAGE_SIZE, Region->Type, Region->Protect & ~PAGE_GUARD,
MmAlterViewAttributes);
if (!NT_SUCCESS(Status))
{
DPRINT1("Removing PAGE_GUARD protection failed : 0x%08x.\n", Status);
}
return STATUS_GUARD_PAGE_VIOLATION;
}
/*
* Lock the segment
*/
MmLockSectionSegment(Segment);
Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
/*
* Check if this page needs to be mapped COW
*/
if ((Segment->WriteCopy) &&
(Region->Protect == PAGE_READWRITE ||
Region->Protect == PAGE_EXECUTE_READWRITE))
{
Attributes = Region->Protect == PAGE_READWRITE ? PAGE_READONLY : PAGE_EXECUTE_READ;
}
else
{
Attributes = Region->Protect;
}
/*
* Check if someone else is already handling this fault, if so wait
* for them
*/
if (Entry && MM_IS_WAIT_PTE(Entry))
{
MmUnlockSectionSegment(Segment);
MmUnlockAddressSpace(AddressSpace);
MiWaitForPageEvent(NULL, NULL);
MmLockAddressSpace(AddressSpace);
DPRINT("Address 0x%p\n", Address);
return STATUS_MM_RESTART_OPERATION;
}
HasSwapEntry = MmIsPageSwapEntry(Process, Address);
/* See if we should use a private page */
if (HasSwapEntry)
{
SWAPENTRY DummyEntry;
MmGetPageFileMapping(Process, Address, &SwapEntry);
if (SwapEntry == MM_WAIT_ENTRY)
{
MmUnlockSectionSegment(Segment);
MmUnlockAddressSpace(AddressSpace);
MiWaitForPageEvent(NULL, NULL);
MmLockAddressSpace(AddressSpace);
return STATUS_MM_RESTART_OPERATION;
}
/*
* Must be private page we have swapped out.
*/
/*
* Sanity check
*/
MmDeletePageFileMapping(Process, Address, &SwapEntry);
MmUnlockSectionSegment(Segment);
/* Tell everyone else we are serving the fault. */
MmCreatePageFileMapping(Process, Address, MM_WAIT_ENTRY);
MmUnlockAddressSpace(AddressSpace);
MI_SET_USAGE(MI_USAGE_SECTION);
if (Process) MI_SET_PROCESS2(Process->ImageFileName);
if (!Process) MI_SET_PROCESS2("Kernel Section");
Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
if (!NT_SUCCESS(Status))
{
KeBugCheck(MEMORY_MANAGEMENT);
}
if (HasSwapEntry)
{
Status = MmReadFromSwapPage(SwapEntry, Page);
if (!NT_SUCCESS(Status))
{
DPRINT1("MmReadFromSwapPage failed, status = %x\n", Status);
KeBugCheck(MEMORY_MANAGEMENT);
}
}
MmLockAddressSpace(AddressSpace);
MmDeletePageFileMapping(Process, PAddress, &DummyEntry);
Status = MmCreateVirtualMapping(Process,
PAddress,
Region->Protect,
&Page,
1);
if (!NT_SUCCESS(Status))
{
DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
KeBugCheck(MEMORY_MANAGEMENT);
return Status;
}
/*
* Store the swap entry for later use.
*/
if (HasSwapEntry)
MmSetSavedSwapEntryPage(Page, SwapEntry);
/*
* Add the page to the process's working set
*/
if (Process) MmInsertRmap(Page, Process, Address);
/*
* Finish the operation
*/
MiSetPageEvent(Process, Address);
DPRINT("Address 0x%p\n", Address);
return STATUS_SUCCESS;
}
/*
* Satisfying a page fault on a map of /Device/PhysicalMemory is easy
*/
if ((*Segment->Flags) & MM_PHYSICALMEMORY_SEGMENT)
{
MmUnlockSectionSegment(Segment);
/*
* Just map the desired physical page
*/
Page = (PFN_NUMBER)(Offset.QuadPart >> PAGE_SHIFT);
Status = MmCreateVirtualMappingUnsafe(Process,
PAddress,
Region->Protect,
&Page,
1);
if (!NT_SUCCESS(Status))
{
DPRINT("MmCreateVirtualMappingUnsafe failed, not out of memory\n");
KeBugCheck(MEMORY_MANAGEMENT);
return Status;
}
/*
* Cleanup and release locks
*/
MiSetPageEvent(Process, Address);
DPRINT("Address 0x%p\n", Address);
return STATUS_SUCCESS;
}
/*
* Get the entry corresponding to the offset within the section
*/
Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
if (Entry == 0)
{
/*
* If the entry is zero, then we need to load the page.
*/
if ((Offset.QuadPart >= (LONGLONG)PAGE_ROUND_UP(Segment->RawLength.QuadPart)) && (MemoryArea->VadNode.u.VadFlags.VadType == VadImageMap))
{
/* We are beyond the data which is on file. Just get a new page. */
MI_SET_USAGE(MI_USAGE_SECTION);
if (Process) MI_SET_PROCESS2(Process->ImageFileName);
if (!Process) MI_SET_PROCESS2("Kernel Section");
MmRequestPageMemoryConsumer(MC_USER, FALSE, &Page);
MmSetPageEntrySectionSegment(Segment, &Offset, MAKE_SSE(Page << PAGE_SHIFT, 1));
MmUnlockSectionSegment(Segment);
Status = MmCreateVirtualMapping(Process, PAddress, Attributes, &Page, 1);
if (!NT_SUCCESS(Status))
{
DPRINT1("Unable to create virtual mapping\n");
KeBugCheck(MEMORY_MANAGEMENT);
}
ASSERT(MmIsPagePresent(Process, PAddress));
if (Process)
MmInsertRmap(Page, Process, Address);
MiSetPageEvent(Process, Address);
DPRINT("Address 0x%p\n", Address);
return STATUS_SUCCESS;
}
MmUnlockSectionSegment(Segment);
MmUnlockAddressSpace(AddressSpace);
/* The data must be paged in. Lock the file, so that the VDL doesn't get updated behind us. */
FsRtlAcquireFileExclusive(Segment->FileObject);
PFSRTL_COMMON_FCB_HEADER FcbHeader = Segment->FileObject->FsContext;
Status = MmMakeSegmentResident(Segment, Offset.QuadPart, PAGE_SIZE, &FcbHeader->ValidDataLength);
FsRtlReleaseFile(Segment->FileObject);
/* Lock address space again */
MmLockAddressSpace(AddressSpace);
if (!NT_SUCCESS(Status))
{
/* Damn */
DPRINT1("Failed to page data in!\n");
return STATUS_IN_PAGE_ERROR;
}
/* Everything went fine. Restart the operation */
return STATUS_MM_RESTART_OPERATION;
}
else if (IS_SWAP_FROM_SSE(Entry))
{
SWAPENTRY SwapEntry;
SwapEntry = SWAPENTRY_FROM_SSE(Entry);
/* See if a page op is running on this segment. */
if (SwapEntry == MM_WAIT_ENTRY)
{
MmUnlockSectionSegment(Segment);
MmUnlockAddressSpace(AddressSpace);
MiWaitForPageEvent(NULL, NULL);
MmLockAddressSpace(AddressSpace);
return STATUS_MM_RESTART_OPERATION;
}
/*
* Release all our locks and read in the page from disk
*/
MmUnlockSectionSegment(Segment);
MmUnlockAddressSpace(AddressSpace);
MI_SET_USAGE(MI_USAGE_SECTION);
if (Process) MI_SET_PROCESS2(Process->ImageFileName);
if (!Process) MI_SET_PROCESS2("Kernel Section");
Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
if (!NT_SUCCESS(Status))
{
KeBugCheck(MEMORY_MANAGEMENT);
}
Status = MmReadFromSwapPage(SwapEntry, Page);
if (!NT_SUCCESS(Status))
{
KeBugCheck(MEMORY_MANAGEMENT);
}
/*
* Relock the address space and segment
*/
MmLockAddressSpace(AddressSpace);
MmLockSectionSegment(Segment);
/*
* Check the entry. No one should change the status of a page
* that has a pending page-in.
*/
Entry1 = MmGetPageEntrySectionSegment(Segment, &Offset);
if (Entry != Entry1)
{
DPRINT1("Someone changed ppte entry while we slept (%x vs %x)\n", Entry, Entry1);
KeBugCheck(MEMORY_MANAGEMENT);
}
/*
* Save the swap entry.
*/
MmSetSavedSwapEntryPage(Page, SwapEntry);
/* Map the page into the process address space */
Status = MmCreateVirtualMapping(Process,
PAddress,
Attributes,
&Page,
1);
if (!NT_SUCCESS(Status))
{
DPRINT1("Unable to create virtual mapping\n");
KeBugCheck(MEMORY_MANAGEMENT);
}
if (Process)
MmInsertRmap(Page, Process, Address);
/*
* Mark the offset within the section as having valid, in-memory
* data
*/
Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
MmSetPageEntrySectionSegment(Segment, &Offset, Entry);
MmUnlockSectionSegment(Segment);
MiSetPageEvent(Process, Address);
DPRINT("Address 0x%p\n", Address);
return STATUS_SUCCESS;
}
else
{
/* We already have a page on this section offset. Map it into the process address space. */
Page = PFN_FROM_SSE(Entry);
Status = MmCreateVirtualMapping(Process,
PAddress,
Attributes,
&Page,
1);
if (!NT_SUCCESS(Status))
{
DPRINT1("Unable to create virtual mapping\n");
KeBugCheck(MEMORY_MANAGEMENT);
}
if (Process)
MmInsertRmap(Page, Process, Address);
/* Take a reference on it */
MmSharePageEntrySectionSegment(Segment, &Offset);
MmUnlockSectionSegment(Segment);
MiSetPageEvent(Process, Address);
DPRINT("Address 0x%p\n", Address);
return STATUS_SUCCESS;
}
}
NTSTATUS
NTAPI
MmAccessFaultSectionView(PMMSUPPORT AddressSpace,
MEMORY_AREA* MemoryArea,
PVOID Address)
{
PMM_SECTION_SEGMENT Segment;
PFN_NUMBER OldPage;
PFN_NUMBER NewPage;
NTSTATUS Status;
PVOID PAddress;
LARGE_INTEGER Offset;
PMM_REGION Region;
ULONG_PTR Entry;
PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
DPRINT("MmAccessFaultSectionView(%p, %p, %p)\n", AddressSpace, MemoryArea, Address);
/* Make sure we have a page mapping for this address. */
Status = MmNotPresentFaultSectionView(AddressSpace, MemoryArea, Address, TRUE);
if (!NT_SUCCESS(Status))
{
/* This is invalid access ! */
return Status;
}
/*
* Check if the page has already been set readwrite
*/
if (MmGetPageProtect(Process, Address) & PAGE_READWRITE)
{
DPRINT("Address 0x%p\n", Address);
return STATUS_SUCCESS;
}
/*
* Find the offset of the page
*/
PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
Offset.QuadPart = (ULONG_PTR)PAddress - MA_GetStartingAddress(MemoryArea)
+ MemoryArea->SectionData.ViewOffset;
Segment = MemoryArea->SectionData.Segment;
Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
&MemoryArea->SectionData.RegionListHead,
Address, NULL);
ASSERT(Region != NULL);
/*
* Check if we are doing COW
*/
if (!((Segment->WriteCopy) &&
(Region->Protect == PAGE_READWRITE ||
Region->Protect == PAGE_EXECUTE_READWRITE)))
{
DPRINT("Address 0x%p\n", Address);
return STATUS_ACCESS_VIOLATION;
}
/* Get the page mapping this section offset. */
MmLockSectionSegment(Segment);
Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
/* Get the current page mapping for the process */
ASSERT(MmIsPagePresent(Process, PAddress));
OldPage = MmGetPfnForProcess(Process, PAddress);
ASSERT(OldPage != 0);
if (IS_SWAP_FROM_SSE(Entry) ||
PFN_FROM_SSE(Entry) != OldPage)
{
MmUnlockSectionSegment(Segment);
/* This is a private page. We must only change the page protection. */
MmSetPageProtect(Process, PAddress, Region->Protect);
return STATUS_SUCCESS;
}
/*
* Allocate a page
*/
MI_SET_USAGE(MI_USAGE_SECTION);
if (Process) MI_SET_PROCESS2(Process->ImageFileName);
if (!Process) MI_SET_PROCESS2("Kernel Section");
Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &NewPage);
if (!NT_SUCCESS(Status))
{
KeBugCheck(MEMORY_MANAGEMENT);
}
/*
* Copy the old page
*/
NT_VERIFY(NT_SUCCESS(MiCopyFromUserPage(NewPage, PAddress)));
/*
* Unshare the old page.
*/
DPRINT("Swapping page (Old %x New %x)\n", OldPage, NewPage);
MmDeleteVirtualMapping(Process, PAddress, NULL, NULL);
if (Process)
MmDeleteRmap(OldPage, Process, PAddress);
MmUnsharePageEntrySectionSegment(MemoryArea, Segment, &Offset, FALSE, FALSE, NULL);
MmUnlockSectionSegment(Segment);
/*
* Set the PTE to point to the new page
*/
Status = MmCreateVirtualMapping(Process,
PAddress,
Region->Protect,
&NewPage,
1);
if (!NT_SUCCESS(Status))
{
DPRINT1("MmCreateVirtualMapping failed, unable to create virtual mapping, not out of memory\n");
KeBugCheck(MEMORY_MANAGEMENT);
return Status;
}
if (Process)
MmInsertRmap(NewPage, Process, PAddress);
MiSetPageEvent(Process, Address);
DPRINT("Address 0x%p\n", Address);
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
MmProtectSectionView(PMMSUPPORT AddressSpace,
PMEMORY_AREA MemoryArea,
PVOID BaseAddress,
SIZE_T Length,
ULONG Protect,
PULONG OldProtect)
{
PMM_REGION Region;
NTSTATUS Status;
ULONG_PTR MaxLength;
MaxLength = MA_GetEndingAddress(MemoryArea) - (ULONG_PTR)BaseAddress;
if (Length > MaxLength)
Length = (ULONG)MaxLength;
Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
&MemoryArea->SectionData.RegionListHead,
BaseAddress, NULL);
ASSERT(Region != NULL);
if ((MemoryArea->Flags & SEC_NO_CHANGE) &&
Region->Protect != Protect)
{
return STATUS_INVALID_PAGE_PROTECTION;
}
*OldProtect = Region->Protect;
Status = MmAlterRegion(AddressSpace, (PVOID)MA_GetStartingAddress(MemoryArea),
&MemoryArea->SectionData.RegionListHead,
BaseAddress, Length, Region->Type, Protect,
MmAlterViewAttributes);
return Status;
}
NTSTATUS NTAPI
MmQuerySectionView(PMEMORY_AREA MemoryArea,
PVOID Address,
PMEMORY_BASIC_INFORMATION Info,
PSIZE_T ResultLength)
{
PMM_REGION Region;
PVOID RegionBaseAddress;
PMM_SECTION_SEGMENT Segment;
Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
&MemoryArea->SectionData.RegionListHead,
Address, &RegionBaseAddress);
if (Region == NULL)
{
return STATUS_UNSUCCESSFUL;
}
if (MemoryArea->VadNode.u.VadFlags.VadType == VadImageMap)
{
Segment = MemoryArea->SectionData.Segment;
Info->AllocationBase = (PUCHAR)MA_GetStartingAddress(MemoryArea) - Segment->Image.VirtualAddress;
Info->Type = MEM_IMAGE;
}
else
{
Info->AllocationBase = (PVOID)MA_GetStartingAddress(MemoryArea);
Info->Type = MEM_MAPPED;
}
Info->BaseAddress = RegionBaseAddress;
Info->AllocationProtect = MmProtectToValue[MemoryArea->VadNode.u.VadFlags.Protection];
Info->RegionSize = Region->Length;
Info->State = MEM_COMMIT;
Info->Protect = Region->Protect;
*ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
return STATUS_SUCCESS;
}
VOID NTAPI
MmpDeleteSection(PVOID ObjectBody)
{
PSECTION Section = ObjectBody;
/* Check if it's an ARM3, or ReactOS section */
if (!MiIsRosSectionObject(Section))
{
MiDeleteARM3Section(ObjectBody);
return;
}
DPRINT("MmpDeleteSection(ObjectBody %p)\n", ObjectBody);
if (Section->u.Flags.Image)
{
PMM_IMAGE_SECTION_OBJECT ImageSectionObject = (PMM_IMAGE_SECTION_OBJECT)Section->Segment;
/*
* NOTE: Section->ImageSection can be NULL for short time
* during the section creating. If we fail for some reason
* until the image section is properly initialized we shouldn't
* process further here.
*/
if (Section->Segment == NULL)
return;
/* We just dereference the first segment */
ASSERT(ImageSectionObject->RefCount > 0);
MmDereferenceSegment(ImageSectionObject->Segments);
}
else
{
PMM_SECTION_SEGMENT Segment = (PMM_SECTION_SEGMENT)Section->Segment;
/*
* NOTE: Section->Segment can be NULL for short time
* during the section creating.
*/
if (Segment == NULL)
return;
Segment->SectionCount--;
MmDereferenceSegment(Segment);
}
}
VOID NTAPI
MmpCloseSection(IN PEPROCESS Process OPTIONAL,
IN PVOID Object,
IN ACCESS_MASK GrantedAccess,
IN ULONG ProcessHandleCount,
IN ULONG SystemHandleCount)
{
DPRINT("MmpCloseSection(OB %p, HC %lu)\n", Object, ProcessHandleCount);
}
CODE_SEG("INIT")
NTSTATUS
NTAPI
MmCreatePhysicalMemorySection(VOID)
{
PSECTION PhysSection;
NTSTATUS Status;
OBJECT_ATTRIBUTES Obj;
UNICODE_STRING Name = RTL_CONSTANT_STRING(L"\\Device\\PhysicalMemory");
LARGE_INTEGER SectionSize;
HANDLE Handle;
PMM_SECTION_SEGMENT Segment;
/*
* Create the section mapping physical memory
*/
SectionSize.QuadPart = MmHighestPhysicalPage * PAGE_SIZE;
InitializeObjectAttributes(&Obj,
&Name,
OBJ_PERMANENT | OBJ_KERNEL_EXCLUSIVE,
NULL,
NULL);
/*
* Create the Object
*/
Status = ObCreateObject(KernelMode,
MmSectionObjectType,
&Obj,
ExGetPreviousMode(),
NULL,
sizeof(*PhysSection),
0,
0,
(PVOID*)&PhysSection);
if (!NT_SUCCESS(Status))
{
DPRINT1("MmCreatePhysicalMemorySection: failed to create object (0x%lx)\n", Status);
return Status;
}
/*
* Initialize it
*/
RtlZeroMemory(PhysSection, sizeof(*PhysSection));
/* Mark this as a "ROS Section" */
PhysSection->u.Flags.filler = 1;
PhysSection->InitialPageProtection = PAGE_EXECUTE_READWRITE;
PhysSection->u.Flags.PhysicalMemory = 1;
PhysSection->SizeOfSection = SectionSize;
Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
TAG_MM_SECTION_SEGMENT);
if (Segment == NULL)
{
ObDereferenceObject(PhysSection);
return STATUS_NO_MEMORY;
}
RtlZeroMemory(Segment, sizeof(MM_SECTION_SEGMENT));
PhysSection->Segment = (PSEGMENT)Segment;
Segment->RefCount = 1;
Segment->ReferenceCount = &Segment->RefCount;
Segment->Flags = &Segment->SegFlags;
ExInitializeFastMutex(&Segment->Lock);
Segment->Image.FileOffset = 0;
Segment->Protection = PAGE_EXECUTE_READWRITE;
Segment->RawLength = SectionSize;
Segment->Length = SectionSize;
Segment->SegFlags = MM_PHYSICALMEMORY_SEGMENT;
Segment->WriteCopy = FALSE;
Segment->Image.VirtualAddress = 0;
Segment->Image.Characteristics = 0;
MiInitializeSectionPageTable(Segment);
Status = ObInsertObject(PhysSection,
NULL,
SECTION_ALL_ACCESS,
0,
NULL,
&Handle);
if (!NT_SUCCESS(Status))
{
ObDereferenceObject(PhysSection);
return Status;
}
ObCloseHandle(Handle, KernelMode);
return STATUS_SUCCESS;
}
CODE_SEG("INIT")
NTSTATUS
NTAPI
MmInitSectionImplementation(VOID)
{
OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
UNICODE_STRING Name;
DPRINT("Creating Section Object Type\n");
/* Initialize the section based root */
ASSERT(MmSectionBasedRoot.NumberGenericTableElements == 0);
MmSectionBasedRoot.BalancedRoot.u1.Parent = &MmSectionBasedRoot.BalancedRoot;
/* Initialize the Section object type */
RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
RtlInitUnicodeString(&Name, L"Section");
ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(SECTION);
ObjectTypeInitializer.PoolType = PagedPool;
ObjectTypeInitializer.UseDefaultObject = TRUE;
ObjectTypeInitializer.GenericMapping = MmpSectionMapping;
ObjectTypeInitializer.DeleteProcedure = MmpDeleteSection;
ObjectTypeInitializer.CloseProcedure = MmpCloseSection;
ObjectTypeInitializer.ValidAccessMask = SECTION_ALL_ACCESS;
ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &MmSectionObjectType);
MmCreatePhysicalMemorySection();
return STATUS_SUCCESS;
}
static
NTSTATUS
NTAPI
MmCreateDataFileSection(PSECTION *SectionObject,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
PLARGE_INTEGER UMaximumSize,
ULONG SectionPageProtection,
ULONG AllocationAttributes,
PFILE_OBJECT FileObject,
BOOLEAN GotFileHandle)
/*
* Create a section backed by a data file
*/
{
PSECTION Section;
NTSTATUS Status;
LARGE_INTEGER MaximumSize;
PMM_SECTION_SEGMENT Segment;
KIRQL OldIrql;
/*
* Create the section
*/
Status = ObCreateObject(ExGetPreviousMode(),
MmSectionObjectType,
ObjectAttributes,
ExGetPreviousMode(),
NULL,
sizeof(*Section),
0,
0,
(PVOID*)&Section);
if (!NT_SUCCESS(Status))
{
return Status;
}
/*
* Initialize it
*/
RtlZeroMemory(Section, sizeof(*Section));
/* Mark this as a "ROS" section */
Section->u.Flags.filler = 1;
Section->InitialPageProtection = SectionPageProtection;
Section->u.Flags.File = 1;
if (AllocationAttributes & SEC_NO_CHANGE)
Section->u.Flags.NoChange = 1;
if (AllocationAttributes & SEC_RESERVE)
Section->u.Flags.Reserve = 1;
if (!GotFileHandle)
{
ASSERT(UMaximumSize != NULL);
// ASSERT(UMaximumSize->QuadPart != 0);
MaximumSize = *UMaximumSize;
}
else
{
LARGE_INTEGER FileSize;
Status = FsRtlGetFileSize(FileObject, &FileSize);
if (!NT_SUCCESS(Status))
{
ObDereferenceObject(Section);
return Status;
}
/*
* FIXME: Revise this once a locking order for file size changes is
* decided
*/
if ((UMaximumSize != NULL) && (UMaximumSize->QuadPart != 0))
{
MaximumSize = *UMaximumSize;
}
else
{
MaximumSize = FileSize;
/* Mapping zero-sized files isn't allowed. */
if (MaximumSize.QuadPart == 0)
{
ObDereferenceObject(Section);
return STATUS_MAPPED_FILE_SIZE_ZERO;
}
}
if (MaximumSize.QuadPart > FileSize.QuadPart)
{
Status = IoSetInformation(FileObject,
FileEndOfFileInformation,
sizeof(LARGE_INTEGER),
&MaximumSize);
if (!NT_SUCCESS(Status))
{
ObDereferenceObject(Section);
return STATUS_SECTION_NOT_EXTENDED;
}
}
}
if (FileObject->SectionObjectPointer == NULL)
{
ObDereferenceObject(Section);
return STATUS_INVALID_FILE_FOR_SECTION;
}
/*
* Lock the file
*/
Status = MmspWaitForFileLock(FileObject);
if (Status != STATUS_SUCCESS)
{
ObDereferenceObject(Section);
return Status;
}
/* Lock the PFN lock while messing with Section Object pointers */
OldIrql = MiAcquirePfnLock();
Segment = FileObject->SectionObjectPointer->DataSectionObject;
while (Segment && (Segment->SegFlags & (MM_SEGMENT_INDELETE | MM_SEGMENT_INCREATE)))
{
MiReleasePfnLock(OldIrql);
KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
OldIrql = MiAcquirePfnLock();
Segment = FileObject->SectionObjectPointer->DataSectionObject;
}
/*
* If this file hasn't been mapped as a data file before then allocate a
* section segment to describe the data file mapping
*/
if (Segment == NULL)
{
Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
TAG_MM_SECTION_SEGMENT);
if (Segment == NULL)
{
//KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
MiReleasePfnLock(OldIrql);
ObDereferenceObject(Section);
return STATUS_NO_MEMORY;
}
/* We are creating it */
RtlZeroMemory(Segment, sizeof(*Segment));
Segment->SegFlags = MM_DATAFILE_SEGMENT | MM_SEGMENT_INCREATE;
Segment->RefCount = 1;
FileObject->SectionObjectPointer->DataSectionObject = Segment;
/* We're safe to release the lock now */
MiReleasePfnLock(OldIrql);
Section->Segment = (PSEGMENT)Segment;
/* Self-referencing segment */
Segment->Flags = &Segment->SegFlags;
Segment->ReferenceCount = &Segment->RefCount;
Segment->SectionCount = 1;
ExInitializeFastMutex(&Segment->Lock);
Segment->FileObject = FileObject;
ObReferenceObject(FileObject);
Segment->Image.FileOffset = 0;
Segment->Protection = SectionPageProtection;
Segment->Image.Characteristics = 0;
Segment->WriteCopy = (SectionPageProtection & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY));
if (AllocationAttributes & SEC_RESERVE)
{
Segment->Length.QuadPart = Segment->RawLength.QuadPart = 0;
}
else
{
Segment->RawLength.QuadPart = MaximumSize.QuadPart;
Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
}
Segment->Image.VirtualAddress = 0;
MiInitializeSectionPageTable(Segment);
/* We're good to use it now */
OldIrql = MiAcquirePfnLock();
Segment->SegFlags &= ~MM_SEGMENT_INCREATE;
MiReleasePfnLock(OldIrql);
}
else
{
Section->Segment = (PSEGMENT)Segment;
Segment->RefCount++;
InterlockedIncrementUL(&Segment->SectionCount);
MiReleasePfnLock(OldIrql);
MmLockSectionSegment(Segment);
if (MaximumSize.QuadPart > Segment->RawLength.QuadPart &&
!(AllocationAttributes & SEC_RESERVE))
{
Segment->RawLength.QuadPart = MaximumSize.QuadPart;
Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
}
MmUnlockSectionSegment(Segment);
}
Section->SizeOfSection = MaximumSize;
//KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
*SectionObject = Section;
return STATUS_SUCCESS;
}
/*
TODO: not that great (declaring loaders statically, having to declare all of
them, having to keep them extern, etc.), will fix in the future
*/
extern NTSTATUS NTAPI PeFmtCreateSection
(
IN CONST VOID * FileHeader,
IN SIZE_T FileHeaderSize,
IN PVOID File,
OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
OUT PULONG Flags,
IN PEXEFMT_CB_READ_FILE ReadFileCb,
IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
);
extern NTSTATUS NTAPI ElfFmtCreateSection
(
IN CONST VOID * FileHeader,
IN SIZE_T FileHeaderSize,
IN PVOID File,
OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
OUT PULONG Flags,
IN PEXEFMT_CB_READ_FILE ReadFileCb,
IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
);
static PEXEFMT_LOADER ExeFmtpLoaders[] =
{
PeFmtCreateSection,
#ifdef __ELF
ElfFmtCreateSection
#endif
};
static
PMM_SECTION_SEGMENT
NTAPI
ExeFmtpAllocateSegments(IN ULONG NrSegments)
{
SIZE_T SizeOfSegments;
PMM_SECTION_SEGMENT Segments;
/* TODO: check for integer overflow */
SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * NrSegments;
Segments = ExAllocatePoolWithTag(NonPagedPool,
SizeOfSegments,
TAG_MM_SECTION_SEGMENT);
if(Segments)
RtlZeroMemory(Segments, SizeOfSegments);
return Segments;
}
static
NTSTATUS
NTAPI
ExeFmtpReadFile(IN PVOID File,
IN PLARGE_INTEGER Offset,
IN ULONG Length,
OUT PVOID * Data,
OUT PVOID * AllocBase,
OUT PULONG ReadSize)
{
NTSTATUS Status;
LARGE_INTEGER FileOffset;
ULONG AdjustOffset;
ULONG OffsetAdjustment;
ULONG BufferSize;
ULONG UsedSize;
PVOID Buffer;
PFILE_OBJECT FileObject = File;
IO_STATUS_BLOCK Iosb;
ASSERT_IRQL_LESS(DISPATCH_LEVEL);
if(Length == 0)
{
KeBugCheck(MEMORY_MANAGEMENT);
}
FileOffset = *Offset;
/* Negative/special offset: it cannot be used in this context */
if(FileOffset.u.HighPart < 0)
{
KeBugCheck(MEMORY_MANAGEMENT);
}
AdjustOffset = PAGE_ROUND_DOWN(FileOffset.u.LowPart);
OffsetAdjustment = FileOffset.u.LowPart - AdjustOffset;
FileOffset.u.LowPart = AdjustOffset;
BufferSize = Length + OffsetAdjustment;
BufferSize = PAGE_ROUND_UP(BufferSize);
/*
* It's ok to use paged pool, because this is a temporary buffer only used in
* the loading of executables. The assumption is that MmCreateSection is
* always called at low IRQLs and that these buffers don't survive a brief
* initialization phase
*/
Buffer = ExAllocatePoolWithTag(PagedPool, BufferSize, 'rXmM');
if (!Buffer)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
Status = MiSimpleRead(FileObject, &FileOffset, Buffer, BufferSize, TRUE, &Iosb);
UsedSize = (ULONG)Iosb.Information;
if(NT_SUCCESS(Status) && UsedSize < OffsetAdjustment)
{
Status = STATUS_IN_PAGE_ERROR;
ASSERT(!NT_SUCCESS(Status));
}
if(NT_SUCCESS(Status))
{
*Data = (PVOID)((ULONG_PTR)Buffer + OffsetAdjustment);
*AllocBase = Buffer;
*ReadSize = UsedSize - OffsetAdjustment;
}
else
{
ExFreePoolWithTag(Buffer, 'rXmM');
}
return Status;
}
#ifdef NASSERT
# define MmspAssertSegmentsSorted(OBJ_) ((void)0)
# define MmspAssertSegmentsNoOverlap(OBJ_) ((void)0)
# define MmspAssertSegmentsPageAligned(OBJ_) ((void)0)
#else
static
VOID
NTAPI
MmspAssertSegmentsSorted(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
{
ULONG i;
for( i = 1; i < ImageSectionObject->NrSegments; ++ i )
{
ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >=
ImageSectionObject->Segments[i - 1].Image.VirtualAddress);
}
}
static
VOID
NTAPI
MmspAssertSegmentsNoOverlap(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
{
ULONG i;
MmspAssertSegmentsSorted(ImageSectionObject);
for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
{
ASSERT(ImageSectionObject->Segments[i].Length.QuadPart > 0);
if(i > 0)
{
ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >=
(ImageSectionObject->Segments[i - 1].Image.VirtualAddress +
ImageSectionObject->Segments[i - 1].Length.QuadPart));
}
}
}
static
VOID
NTAPI
MmspAssertSegmentsPageAligned(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
{
ULONG i;
for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
{
ASSERT((ImageSectionObject->Segments[i].Image.VirtualAddress % PAGE_SIZE) == 0);
ASSERT((ImageSectionObject->Segments[i].Length.QuadPart % PAGE_SIZE) == 0);
}
}
#endif
static
int
__cdecl
MmspCompareSegments(const void * x,
const void * y)
{
const MM_SECTION_SEGMENT *Segment1 = (const MM_SECTION_SEGMENT *)x;
const MM_SECTION_SEGMENT *Segment2 = (const MM_SECTION_SEGMENT *)y;
if (Segment1->Image.VirtualAddress > Segment2->Image.VirtualAddress)
return 1;
else if (Segment1->Image.VirtualAddress < Segment2->Image.VirtualAddress)
return -1;
else
return 0;
}
/*
* Ensures an image section's segments are sorted in memory
*/
static
VOID
NTAPI
MmspSortSegments(IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
IN ULONG Flags)
{
if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_SORTED)
{
MmspAssertSegmentsSorted(ImageSectionObject);
}
else
{
qsort(ImageSectionObject->Segments,
ImageSectionObject->NrSegments,
sizeof(ImageSectionObject->Segments[0]),
MmspCompareSegments);
}
}
/*
* Ensures an image section's segments don't overlap in memory and don't have
* gaps and don't have a null size. We let them map to overlapping file regions,
* though - that's not necessarily an error
*/
static
BOOLEAN
NTAPI
MmspCheckSegmentBounds
(
IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
IN ULONG Flags
)
{
ULONG i;
if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_NO_OVERLAP)
{
MmspAssertSegmentsNoOverlap(ImageSectionObject);
return TRUE;
}
ASSERT(ImageSectionObject->NrSegments >= 1);
for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
{
if(ImageSectionObject->Segments[i].Length.QuadPart == 0)
{
return FALSE;
}
if(i > 0)
{
/*
* TODO: relax the limitation on gaps. For example, gaps smaller than a
* page could be OK (Windows seems to be OK with them), and larger gaps
* could lead to image sections spanning several discontiguous regions
* (NtMapViewOfSection could then refuse to map them, and they could
* e.g. only be allowed as parameters to NtCreateProcess, like on UNIX)
*/
if ((ImageSectionObject->Segments[i - 1].Image.VirtualAddress +
ImageSectionObject->Segments[i - 1].Length.QuadPart) !=
ImageSectionObject->Segments[i].Image.VirtualAddress)
{
return FALSE;
}
}
}
return TRUE;
}
/*
* Merges and pads an image section's segments until they all are page-aligned
* and have a size that is a multiple of the page size
*/
static
BOOLEAN
NTAPI
MmspPageAlignSegments
(
IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
IN ULONG Flags
)
{
ULONG i;
ULONG LastSegment;
PMM_SECTION_SEGMENT EffectiveSegment;
if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED)
{
MmspAssertSegmentsPageAligned(ImageSectionObject);
return TRUE;
}
LastSegment = 0;
EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
{
/*
* The first segment requires special handling
*/
if (i == 0)
{
ULONG_PTR VirtualAddress;
ULONG_PTR VirtualOffset;
VirtualAddress = EffectiveSegment->Image.VirtualAddress;
/* Round down the virtual address to the nearest page */
EffectiveSegment->Image.VirtualAddress = PAGE_ROUND_DOWN(VirtualAddress);
/* Round up the virtual size to the nearest page */
EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(VirtualAddress + EffectiveSegment->Length.QuadPart) -
EffectiveSegment->Image.VirtualAddress;
/* Adjust the raw address and size */
VirtualOffset = VirtualAddress - EffectiveSegment->Image.VirtualAddress;
if (EffectiveSegment->Image.FileOffset < VirtualOffset)
{
return FALSE;
}
/*
* Garbage in, garbage out: unaligned base addresses make the file
* offset point in curious and odd places, but that's what we were
* asked for
*/
EffectiveSegment->Image.FileOffset -= VirtualOffset;
EffectiveSegment->RawLength.QuadPart += VirtualOffset;
}
else
{
PMM_SECTION_SEGMENT Segment = &ImageSectionObject->Segments[i];
ULONG_PTR EndOfEffectiveSegment;
EndOfEffectiveSegment = (ULONG_PTR)(EffectiveSegment->Image.VirtualAddress + EffectiveSegment->Length.QuadPart);
ASSERT((EndOfEffectiveSegment % PAGE_SIZE) == 0);
/*
* The current segment begins exactly where the current effective
* segment ended, therefore beginning a new effective segment
*/
if (EndOfEffectiveSegment == Segment->Image.VirtualAddress)
{
LastSegment ++;
ASSERT(LastSegment <= i);
ASSERT(LastSegment < ImageSectionObject->NrSegments);
EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
if (LastSegment != i)
{
/*
* Copy the current segment. If necessary, the effective segment
* will be expanded later
*/
*EffectiveSegment = *Segment;
}
/*
* Page-align the virtual size. We know for sure the virtual address
* already is
*/
ASSERT((EffectiveSegment->Image.VirtualAddress % PAGE_SIZE) == 0);
EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(EffectiveSegment->Length.QuadPart);
}
/*
* The current segment is still part of the current effective segment:
* extend the effective segment to reflect this
*/
else if (EndOfEffectiveSegment > Segment->Image.VirtualAddress)
{
static const ULONG FlagsToProtection[16] =
{
PAGE_NOACCESS,
PAGE_READONLY,
PAGE_READWRITE,
PAGE_READWRITE,
PAGE_EXECUTE_READ,
PAGE_EXECUTE_READ,
PAGE_EXECUTE_READWRITE,
PAGE_EXECUTE_READWRITE,
PAGE_WRITECOPY,
PAGE_WRITECOPY,
PAGE_WRITECOPY,
PAGE_WRITECOPY,
PAGE_EXECUTE_WRITECOPY,
PAGE_EXECUTE_WRITECOPY,
PAGE_EXECUTE_WRITECOPY,
PAGE_EXECUTE_WRITECOPY
};
unsigned ProtectionFlags;
/*
* Extend the file size
*/
/* Unaligned segments must be contiguous within the file */
if (Segment->Image.FileOffset != (EffectiveSegment->Image.FileOffset +
EffectiveSegment->RawLength.QuadPart))
{
return FALSE;
}
EffectiveSegment->RawLength.QuadPart += Segment->RawLength.QuadPart;
/*
* Extend the virtual size
*/
ASSERT(PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) >= EndOfEffectiveSegment);
EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) -
EffectiveSegment->Image.VirtualAddress;
/*
* Merge the protection
*/
EffectiveSegment->Protection |= Segment->Protection;
/* Clean up redundance */
ProtectionFlags = 0;
if(EffectiveSegment->Protection & PAGE_IS_READABLE)
ProtectionFlags |= 1 << 0;
if(EffectiveSegment->Protection & PAGE_IS_WRITABLE)
ProtectionFlags |= 1 << 1;
if(EffectiveSegment->Protection & PAGE_IS_EXECUTABLE)
ProtectionFlags |= 1 << 2;
if(EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
ProtectionFlags |= 1 << 3;
ASSERT(ProtectionFlags < 16);
EffectiveSegment->Protection = FlagsToProtection[ProtectionFlags];
/* If a segment was required to be shared and cannot, fail */
if(!(Segment->Protection & PAGE_IS_WRITECOPY) &&
EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
{
return FALSE;
}
}
/*
* We assume no holes between segments at this point
*/
else
{
KeBugCheck(MEMORY_MANAGEMENT);
}
}
}
ImageSectionObject->NrSegments = LastSegment + 1;
return TRUE;
}
NTSTATUS
ExeFmtpCreateImageSection(PFILE_OBJECT FileObject,
PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
{
LARGE_INTEGER Offset;
PVOID FileHeader;
PVOID FileHeaderBuffer;
ULONG FileHeaderSize;
ULONG Flags;
ULONG OldNrSegments;
NTSTATUS Status;
ULONG i;
/*
* Read the beginning of the file (2 pages). Should be enough to contain
* all (or most) of the headers
*/
Offset.QuadPart = 0;
Status = ExeFmtpReadFile (FileObject,
&Offset,
PAGE_SIZE * 2,
&FileHeader,
&FileHeaderBuffer,
&FileHeaderSize);
if (!NT_SUCCESS(Status))
return Status;
if (FileHeaderSize == 0)
{
ExFreePool(FileHeaderBuffer);
return STATUS_UNSUCCESSFUL;
}
/*
* Look for a loader that can handle this executable
*/
for (i = 0; i < RTL_NUMBER_OF(ExeFmtpLoaders); ++ i)
{
Flags = 0;
Status = ExeFmtpLoaders[i](FileHeader,
FileHeaderSize,
FileObject,
ImageSectionObject,
&Flags,
ExeFmtpReadFile,
ExeFmtpAllocateSegments);
if (!NT_SUCCESS(Status))
{
if (ImageSectionObject->Segments)
{
ExFreePool(ImageSectionObject->Segments);
ImageSectionObject->Segments = NULL;
}
}
if (Status != STATUS_ROS_EXEFMT_UNKNOWN_FORMAT)
break;
}
ExFreePoolWithTag(FileHeaderBuffer, 'rXmM');
/*
* No loader handled the format
*/
if (Status == STATUS_ROS_EXEFMT_UNKNOWN_FORMAT)
{
Status = STATUS_INVALID_IMAGE_NOT_MZ;
ASSERT(!NT_SUCCESS(Status));
}
if (!NT_SUCCESS(Status))
return Status;
ASSERT(ImageSectionObject->Segments != NULL);
ASSERT(ImageSectionObject->RefCount > 0);
/*
* Some defaults
*/
/* FIXME? are these values platform-dependent? */
if (ImageSectionObject->ImageInformation.MaximumStackSize == 0)
ImageSectionObject->ImageInformation.MaximumStackSize = 0x40000;
if(ImageSectionObject->ImageInformation.CommittedStackSize == 0)
ImageSectionObject->ImageInformation.CommittedStackSize = 0x1000;
if(ImageSectionObject->BasedAddress == NULL)
{
if(ImageSectionObject->ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL)
ImageSectionObject->BasedAddress = (PVOID)0x10000000;
else
ImageSectionObject->BasedAddress = (PVOID)0x00400000;
}
/*
* And now the fun part: fixing the segments
*/
/* Sort them by virtual address */
MmspSortSegments(ImageSectionObject, Flags);
/* Ensure they don't overlap in memory */
if (!MmspCheckSegmentBounds(ImageSectionObject, Flags))
return STATUS_INVALID_IMAGE_FORMAT;
/* Ensure they are aligned */
OldNrSegments = ImageSectionObject->NrSegments;
if (!MmspPageAlignSegments(ImageSectionObject, Flags))
return STATUS_INVALID_IMAGE_FORMAT;
/* Trim them if the alignment phase merged some of them */
if (ImageSectionObject->NrSegments < OldNrSegments)
{
PMM_SECTION_SEGMENT Segments;
SIZE_T SizeOfSegments;
SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * ImageSectionObject->NrSegments;
Segments = ExAllocatePoolWithTag(PagedPool,
SizeOfSegments,
TAG_MM_SECTION_SEGMENT);
if (Segments == NULL)
return STATUS_INSUFFICIENT_RESOURCES;
RtlCopyMemory(Segments, ImageSectionObject->Segments, SizeOfSegments);
ExFreePool(ImageSectionObject->Segments);
ImageSectionObject->Segments = Segments;
}
/* And finish their initialization */
for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
{
ExInitializeFastMutex(&ImageSectionObject->Segments[i].Lock);
ImageSectionObject->Segments[i].ReferenceCount = &ImageSectionObject->RefCount;
ImageSectionObject->Segments[i].Flags = &ImageSectionObject->SegFlags;
MiInitializeSectionPageTable(&ImageSectionObject->Segments[i]);
ImageSectionObject->Segments[i].FileObject = FileObject;
}
ASSERT(ImageSectionObject->RefCount > 0);
ImageSectionObject->FileObject = FileObject;
ASSERT(NT_SUCCESS(Status));
return Status;
}
NTSTATUS
MmCreateImageSection(PSECTION *SectionObject,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
PLARGE_INTEGER UMaximumSize,
ULONG SectionPageProtection,
ULONG AllocationAttributes,
PFILE_OBJECT FileObject)
{
PSECTION Section;
NTSTATUS Status;
PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
KIRQL OldIrql;
if (FileObject == NULL)
return STATUS_INVALID_FILE_FOR_SECTION;
if (FileObject->SectionObjectPointer == NULL)
{
DPRINT1("Denying section creation due to missing cache initialization\n");
return STATUS_INVALID_FILE_FOR_SECTION;
}
/*
* Create the section
*/
Status = ObCreateObject (ExGetPreviousMode(),
MmSectionObjectType,
ObjectAttributes,
ExGetPreviousMode(),
NULL,
sizeof(*Section),
0,
0,
(PVOID*)(PVOID)&Section);
if (!NT_SUCCESS(Status))
{
return Status;
}
/*
* Initialize it
*/
RtlZeroMemory(Section, sizeof(*Section));
/* Mark this as a "ROS" Section */
Section->u.Flags.filler = 1;
Section->InitialPageProtection = SectionPageProtection;
Section->u.Flags.File = 1;
Section->u.Flags.Image = 1;
if (AllocationAttributes & SEC_NO_CHANGE)
Section->u.Flags.NoChange = 1;
OldIrql = MiAcquirePfnLock();
/* Wait for it to be properly created or deleted */
ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
while(ImageSectionObject && (ImageSectionObject->SegFlags & (MM_SEGMENT_INDELETE | MM_SEGMENT_INCREATE)))
{
MiReleasePfnLock(OldIrql);
KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
OldIrql = MiAcquirePfnLock();
ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
}
if (ImageSectionObject == NULL)
{
NTSTATUS StatusExeFmt;
ImageSectionObject = ExAllocatePoolZero(NonPagedPool, sizeof(MM_IMAGE_SECTION_OBJECT), TAG_MM_SECTION_SEGMENT);
if (ImageSectionObject == NULL)
{
MiReleasePfnLock(OldIrql);
ObDereferenceObject(Section);
return STATUS_NO_MEMORY;
}
ImageSectionObject->SegFlags = MM_SEGMENT_INCREATE;
ImageSectionObject->RefCount = 1;
FileObject->SectionObjectPointer->ImageSectionObject = ImageSectionObject;
MiReleasePfnLock(OldIrql);
/* Purge the cache */
CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, NULL);
StatusExeFmt = ExeFmtpCreateImageSection(FileObject, ImageSectionObject);
if (!NT_SUCCESS(StatusExeFmt))
{
/* Unset */
OldIrql = MiAcquirePfnLock();
FileObject->SectionObjectPointer->ImageSectionObject = NULL;
MiReleasePfnLock(OldIrql);
if(ImageSectionObject->Segments != NULL)
ExFreePool(ImageSectionObject->Segments);
/*
* If image file is empty, then return that the file is invalid for section
*/
Status = StatusExeFmt;
if (StatusExeFmt == STATUS_END_OF_FILE)
{
Status = STATUS_INVALID_FILE_FOR_SECTION;
}
ExFreePoolWithTag(ImageSectionObject, TAG_MM_SECTION_SEGMENT);
ObDereferenceObject(Section);
return Status;
}
Section->Segment = (PSEGMENT)ImageSectionObject;
ASSERT(ImageSectionObject->Segments);
ASSERT(ImageSectionObject->RefCount > 0);
/*
* Lock the file
*/
Status = MmspWaitForFileLock(FileObject);
if (!NT_SUCCESS(Status))
{
/* Unset */
OldIrql = MiAcquirePfnLock();
FileObject->SectionObjectPointer->ImageSectionObject = NULL;
MiReleasePfnLock(OldIrql);
ExFreePool(ImageSectionObject->Segments);
ExFreePool(ImageSectionObject);
ObDereferenceObject(Section);
return Status;
}
OldIrql = MiAcquirePfnLock();
ImageSectionObject->SegFlags &= ~MM_SEGMENT_INCREATE;
/* Take a ref on the file on behalf of the newly created structure */
ObReferenceObject(FileObject);
MiReleasePfnLock(OldIrql);
Status = StatusExeFmt;
}
else
{
/* Take one ref */
ImageSectionObject->RefCount++;
MiReleasePfnLock(OldIrql);
Section->Segment = (PSEGMENT)ImageSectionObject;
Status = STATUS_SUCCESS;
}
//KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
*SectionObject = Section;
ASSERT(ImageSectionObject->RefCount > 0);
return Status;
}
static NTSTATUS
MmMapViewOfSegment(
PMMSUPPORT AddressSpace,
BOOLEAN AsImage,
PMM_SECTION_SEGMENT Segment,
PVOID* BaseAddress,
SIZE_T ViewSize,
ULONG Protect,
LONGLONG ViewOffset,
ULONG AllocationType)
{
PMEMORY_AREA MArea;
NTSTATUS Status;
ULONG Granularity;
ASSERT(ViewSize != 0);
if (Segment->WriteCopy)
{
/* We have to do this because the not present fault
* and access fault handlers depend on the protection
* that should be granted AFTER the COW fault takes
* place to be in Region->Protect. The not present fault
* handler changes this to the correct protection for COW when
* mapping the pages into the process's address space. If a COW
* fault takes place, the access fault handler sets the page protection
* to these values for the newly copied pages
*/
if (Protect == PAGE_WRITECOPY)
Protect = PAGE_READWRITE;
else if (Protect == PAGE_EXECUTE_WRITECOPY)
Protect = PAGE_EXECUTE_READWRITE;
}
if (*BaseAddress == NULL)
Granularity = MM_ALLOCATION_GRANULARITY;
else
Granularity = PAGE_SIZE;
#ifdef NEWCC
if (Segment->Flags & MM_DATAFILE_SEGMENT)
{
LARGE_INTEGER FileOffset;
FileOffset.QuadPart = ViewOffset;
ObReferenceObject(Section);
return _MiMapViewOfSegment(AddressSpace, Segment, BaseAddress, ViewSize, Protect, &FileOffset, AllocationType, __FILE__, __LINE__);
}
#endif
Status = MmCreateMemoryArea(AddressSpace,
MEMORY_AREA_SECTION_VIEW,
BaseAddress,
ViewSize,
Protect,
&MArea,
AllocationType,
Granularity);
if (!NT_SUCCESS(Status))
{
DPRINT1("Mapping between 0x%p and 0x%p failed (%X).\n",
(*BaseAddress), (char*)(*BaseAddress) + ViewSize, Status);
return Status;
}
InterlockedIncrement64(Segment->ReferenceCount);
MArea->SectionData.Segment = Segment;
MArea->SectionData.ViewOffset = ViewOffset;
if (AsImage)
{
MArea->VadNode.u.VadFlags.VadType = VadImageMap;
}
MmInitializeRegion(&MArea->SectionData.RegionListHead,
ViewSize, 0, Protect);
return STATUS_SUCCESS;
}
static VOID
MmFreeSectionPage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
PFN_NUMBER Page, SWAPENTRY SwapEntry, BOOLEAN Dirty)
{
ULONG_PTR Entry;
LARGE_INTEGER Offset;
SWAPENTRY SavedSwapEntry;
PMM_SECTION_SEGMENT Segment;
PMMSUPPORT AddressSpace;
PEPROCESS Process;
AddressSpace = (PMMSUPPORT)Context;
Process = MmGetAddressSpaceOwner(AddressSpace);
Address = (PVOID)PAGE_ROUND_DOWN(Address);
Offset.QuadPart = ((ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea)) +
MemoryArea->SectionData.ViewOffset;
Segment = MemoryArea->SectionData.Segment;
Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
while (Entry && MM_IS_WAIT_PTE(Entry))
{
MmUnlockSectionSegment(Segment);
MmUnlockAddressSpace(AddressSpace);
MiWaitForPageEvent(NULL, NULL);
MmLockAddressSpace(AddressSpace);
MmLockSectionSegment(Segment);
Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
}
/*
* For a dirty, datafile, non-private page, there shoulkd be no swap entry
*/
if (*Segment->Flags & MM_DATAFILE_SEGMENT)
{
if (Page == PFN_FROM_SSE(Entry) && Dirty)
{
ASSERT(SwapEntry == 0);
}
}
if (SwapEntry != 0)
{
/*
* Sanity check
*/
MmFreeSwapPage(SwapEntry);
}
else if (Page != 0)
{
if (IS_SWAP_FROM_SSE(Entry) ||
Page != PFN_FROM_SSE(Entry))
{
ASSERT(Process != NULL);
/*
* Just dereference private pages
*/
SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
if (SavedSwapEntry != 0)
{
MmFreeSwapPage(SavedSwapEntry);
MmSetSavedSwapEntryPage(Page, 0);
}
MmDeleteRmap(Page, Process, Address);
MmReleasePageMemoryConsumer(MC_USER, Page);
}
else
{
if (Process)
{
MmDeleteRmap(Page, Process, Address);
}
/* We don't dirtify for System Space Maps. We let Cc manage that */
MmUnsharePageEntrySectionSegment(MemoryArea, Segment, &Offset, Process ? Dirty : FALSE, FALSE, NULL);
}
}
}
static NTSTATUS
MmUnmapViewOfSegment(PMMSUPPORT AddressSpace,
PVOID BaseAddress)
{
NTSTATUS Status;
PMEMORY_AREA MemoryArea;
PMM_SECTION_SEGMENT Segment;
PLIST_ENTRY CurrentEntry;
PMM_REGION CurrentRegion;
PLIST_ENTRY RegionListHead;
MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace,
BaseAddress);
if (MemoryArea == NULL)
{
return STATUS_UNSUCCESSFUL;
}
Segment = MemoryArea->SectionData.Segment;
#ifdef NEWCC
if (Segment->Flags & MM_DATAFILE_SEGMENT)
{
MmUnlockAddressSpace(AddressSpace);
Status = MmUnmapViewOfCacheSegment(AddressSpace, BaseAddress);
MmLockAddressSpace(AddressSpace);
return Status;
}
#endif
MemoryArea->DeleteInProgress = TRUE;
MmLockSectionSegment(Segment);
RegionListHead = &MemoryArea->SectionData.RegionListHead;
while (!IsListEmpty(RegionListHead))
{
CurrentEntry = RemoveHeadList(RegionListHead);
CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION, RegionListEntry);
ExFreePoolWithTag(CurrentRegion, TAG_MM_REGION);
}
if ((*Segment->Flags) & MM_PHYSICALMEMORY_SEGMENT)
{
Status = MmFreeMemoryArea(AddressSpace,
MemoryArea,
NULL,
NULL);
}
else
{
Status = MmFreeMemoryArea(AddressSpace,
MemoryArea,
MmFreeSectionPage,
AddressSpace);
}
MmUnlockSectionSegment(Segment);
MmDereferenceSegment(Segment);
return Status;
}
/* This functions must be called with a locked address space */
NTSTATUS
NTAPI
MiRosUnmapViewOfSection(IN PEPROCESS Process,
IN PVOID BaseAddress,
IN BOOLEAN SkipDebuggerNotify)
{
NTSTATUS Status;
PMEMORY_AREA MemoryArea;
PMMSUPPORT AddressSpace;
PVOID ImageBaseAddress = 0;
DPRINT("Opening memory area Process %p BaseAddress %p\n",
Process, BaseAddress);
ASSERT(Process);
AddressSpace = Process ? &Process->Vm : MmGetKernelAddressSpace();
MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace,
BaseAddress);
if (MemoryArea == NULL ||
#ifdef NEWCC
((MemoryArea->Type != MEMORY_AREA_SECTION_VIEW) && (MemoryArea->Type != MEMORY_AREA_CACHE)) ||
#else
(MemoryArea->Type != MEMORY_AREA_SECTION_VIEW) ||
#endif
MemoryArea->DeleteInProgress)
{
if (MemoryArea) ASSERT(MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3);
DPRINT1("Unable to find memory area at address %p.\n", BaseAddress);
return STATUS_NOT_MAPPED_VIEW;
}
if (MemoryArea->VadNode.u.VadFlags.VadType == VadImageMap)
{
ULONG i;
ULONG NrSegments;
PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
PMM_SECTION_SEGMENT SectionSegments;
PMM_SECTION_SEGMENT Segment;
Segment = MemoryArea->SectionData.Segment;
ImageSectionObject = ImageSectionObjectFromSegment(Segment);
SectionSegments = ImageSectionObject->Segments;
NrSegments = ImageSectionObject->NrSegments;
MemoryArea->DeleteInProgress = TRUE;
/* Search for the current segment within the section segments
* and calculate the image base address */
for (i = 0; i < NrSegments; i++)
{
if (Segment == &SectionSegments[i])
{
ImageBaseAddress = (char*)BaseAddress - (ULONG_PTR)SectionSegments[i].Image.VirtualAddress;
break;
}
}
if (i >= NrSegments)
{
KeBugCheck(MEMORY_MANAGEMENT);
}
for (i = 0; i < NrSegments; i++)
{
PVOID SBaseAddress = (PVOID)
((char*)ImageBaseAddress + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
Status = MmUnmapViewOfSegment(AddressSpace, SBaseAddress);
if (!NT_SUCCESS(Status))
{
DPRINT1("MmUnmapViewOfSegment failed for %p (Process %p) with %lx\n",
SBaseAddress, Process, Status);
ASSERT(NT_SUCCESS(Status));
}
}
}
else
{
Status = MmUnmapViewOfSegment(AddressSpace, BaseAddress);
if (!NT_SUCCESS(Status))
{
DPRINT1("MmUnmapViewOfSegment failed for %p (Process %p) with %lx\n",
BaseAddress, Process, Status);
ASSERT(NT_SUCCESS(Status));
}
}
/* Notify debugger */
if (ImageBaseAddress && !SkipDebuggerNotify) DbgkUnMapViewOfSection(ImageBaseAddress);
return STATUS_SUCCESS;
}
/**
* Queries the information of a section object.
*
* @param SectionHandle
* Handle to the section object. It must be opened with SECTION_QUERY
* access.
* @param SectionInformationClass
* Index to a certain information structure. Can be either
* SectionBasicInformation or SectionImageInformation. The latter
* is valid only for sections that were created with the SEC_IMAGE
* flag.
* @param SectionInformation
* Caller supplies storage for resulting information.
* @param Length
* Size of the supplied storage.
* @param ResultLength
* Data written.
*
* @return Status.
*
* @implemented
*/
NTSTATUS
NTAPI
NtQuerySection(
_In_ HANDLE SectionHandle,
_In_ SECTION_INFORMATION_CLASS SectionInformationClass,
_Out_ PVOID SectionInformation,
_In_ SIZE_T SectionInformationLength,
_Out_opt_ PSIZE_T ResultLength)
{
PSECTION Section;
KPROCESSOR_MODE PreviousMode;
NTSTATUS Status;
PAGED_CODE();
PreviousMode = ExGetPreviousMode();
if (PreviousMode != KernelMode)
{
_SEH2_TRY
{
ProbeForWrite(SectionInformation,
SectionInformationLength,
__alignof(ULONG));
if (ResultLength != NULL)
{
ProbeForWrite(ResultLength,
sizeof(*ResultLength),
__alignof(SIZE_T));
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
}
if (SectionInformationClass == SectionBasicInformation)
{
if (SectionInformationLength < sizeof(SECTION_BASIC_INFORMATION))
{
return STATUS_INFO_LENGTH_MISMATCH;
}
}
else if (SectionInformationClass == SectionImageInformation)
{
if (SectionInformationLength < sizeof(SECTION_IMAGE_INFORMATION))
{
return STATUS_INFO_LENGTH_MISMATCH;
}
}
else
{
return STATUS_INVALID_INFO_CLASS;
}
Status = ObReferenceObjectByHandle(SectionHandle,
SECTION_QUERY,
MmSectionObjectType,
PreviousMode,
(PVOID*)(PVOID)&Section,
NULL);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to reference section: 0x%lx\n", Status);
return Status;
}
switch(SectionInformationClass)
{
case SectionBasicInformation:
{
SECTION_BASIC_INFORMATION Sbi;
Sbi.Size = Section->SizeOfSection;
Sbi.BaseAddress = (PVOID)Section->Address.StartingVpn;
Sbi.Attributes = 0;
if (Section->u.Flags.File)
Sbi.Attributes |= SEC_FILE;
if (Section->u.Flags.Image)
Sbi.Attributes |= SEC_IMAGE;
/* Those are not set *************
if (Section->u.Flags.Commit)
Sbi.Attributes |= SEC_COMMIT;
if (Section->u.Flags.Reserve)
Sbi.Attributes |= SEC_RESERVE;
**********************************/
if (Section->u.Flags.Image)
{
if (MiIsRosSectionObject(Section))
{
PMM_IMAGE_SECTION_OBJECT ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
Sbi.BaseAddress = 0;
Sbi.Size.QuadPart = ImageSectionObject->ImageInformation.ImageFileSize;
}
else
{
/* Not supported yet */
ASSERT(FALSE);
}
}
else if (MiIsRosSectionObject(Section))
{
Sbi.BaseAddress = (PVOID)((PMM_SECTION_SEGMENT)Section->Segment)->Image.VirtualAddress;
Sbi.Size.QuadPart = ((PMM_SECTION_SEGMENT)Section->Segment)->RawLength.QuadPart;
}
else
{
DPRINT1("Unimplemented code path!");
}
_SEH2_TRY
{
*((SECTION_BASIC_INFORMATION*)SectionInformation) = Sbi;
if (ResultLength != NULL)
{
*ResultLength = sizeof(Sbi);
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
break;
}
case SectionImageInformation:
{
if (!Section->u.Flags.Image)
{
Status = STATUS_SECTION_NOT_IMAGE;
}
else if (MiIsRosSectionObject(Section))
{
PMM_IMAGE_SECTION_OBJECT ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
_SEH2_TRY
{
PSECTION_IMAGE_INFORMATION Sii = (PSECTION_IMAGE_INFORMATION)SectionInformation;
*Sii = ImageSectionObject->ImageInformation;
if (ResultLength != NULL)
{
*ResultLength = sizeof(*Sii);
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
}
else
{
_SEH2_TRY
{
PSECTION_IMAGE_INFORMATION Sii = (PSECTION_IMAGE_INFORMATION)SectionInformation;
*Sii = *Section->Segment->u2.ImageInformation;
if (ResultLength != NULL)
*ResultLength = sizeof(*Sii);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
}
break;
}
default:
DPRINT1("Unknown SectionInformationClass: %d\n", SectionInformationClass);
Status = STATUS_NOT_SUPPORTED;
}
ObDereferenceObject(Section);
return Status;
}
/**********************************************************************
* NAME EXPORTED
* MmMapViewOfSection
*
* DESCRIPTION
* Maps a view of a section into the virtual address space of a
* process.
*
* ARGUMENTS
* Section
* Pointer to the section object.
*
* ProcessHandle
* Pointer to the process.
*
* BaseAddress
* Desired base address (or NULL) on entry;
* Actual base address of the view on exit.
*
* ZeroBits
* Number of high order address bits that must be zero.
*
* CommitSize
* Size in bytes of the initially committed section of
* the view.
*
* SectionOffset
* Offset in bytes from the beginning of the section
* to the beginning of the view.
*
* ViewSize
* Desired length of map (or zero to map all) on entry
* Actual length mapped on exit.
*
* InheritDisposition
* Specified how the view is to be shared with
* child processes.
*
* AllocationType
* Type of allocation for the pages.
*
* Protect
* Protection for the committed region of the view.
*
* RETURN VALUE
* Status.
*
* @implemented
*/
NTSTATUS NTAPI
MmMapViewOfSection(IN PVOID SectionObject,
IN PEPROCESS Process,
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)
{
PSECTION Section;
PMMSUPPORT AddressSpace;
NTSTATUS Status = STATUS_SUCCESS;
BOOLEAN NotAtBase = FALSE;
if (MiIsRosSectionObject(SectionObject) == FALSE)
{
DPRINT("Mapping ARM3 section into %s\n", Process->ImageFileName);
return MmMapViewOfArm3Section(SectionObject,
Process,
BaseAddress,
ZeroBits,
CommitSize,
SectionOffset,
ViewSize,
InheritDisposition,
AllocationType,
Protect);
}
ASSERT(Process);
if (!Protect || Protect & ~PAGE_FLAGS_VALID_FOR_SECTION)
{
return STATUS_INVALID_PAGE_PROTECTION;
}
/* FIXME: We should keep this, but it would break code checking equality */
Protect &= ~PAGE_NOCACHE;
Section = SectionObject;
AddressSpace = &Process->Vm;
if (Section->u.Flags.NoChange)
AllocationType |= SEC_NO_CHANGE;
MmLockAddressSpace(AddressSpace);
if (Section->u.Flags.Image)
{
ULONG i;
ULONG NrSegments;
ULONG_PTR ImageBase;
SIZE_T ImageSize;
PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
PMM_SECTION_SEGMENT SectionSegments;
ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
SectionSegments = ImageSectionObject->Segments;
NrSegments = ImageSectionObject->NrSegments;
ASSERT(ImageSectionObject->RefCount > 0);
ImageBase = (ULONG_PTR)*BaseAddress;
if (ImageBase == 0)
{
ImageBase = (ULONG_PTR)ImageSectionObject->BasedAddress;
}
ImageSize = 0;
for (i = 0; i < NrSegments; i++)
{
ULONG_PTR MaxExtent;
MaxExtent = (ULONG_PTR)(SectionSegments[i].Image.VirtualAddress +
SectionSegments[i].Length.QuadPart);
ImageSize = max(ImageSize, MaxExtent);
}
ImageSectionObject->ImageInformation.ImageFileSize = (ULONG)ImageSize;
/* Check for an illegal base address */
if (((ImageBase + ImageSize) > (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS) ||
((ImageBase + ImageSize) < ImageSize))
{
ASSERT(*BaseAddress == NULL);
ImageBase = ALIGN_DOWN_BY((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - ImageSize,
MM_VIRTMEM_GRANULARITY);
NotAtBase = TRUE;
}
else if (ImageBase != ALIGN_DOWN_BY(ImageBase, MM_VIRTMEM_GRANULARITY))
{
ASSERT(*BaseAddress == NULL);
ImageBase = ALIGN_DOWN_BY(ImageBase, MM_VIRTMEM_GRANULARITY);
NotAtBase = TRUE;
}
/* Check there is enough space to map the section at that point. */
if (MmLocateMemoryAreaByRegion(AddressSpace, (PVOID)ImageBase,
PAGE_ROUND_UP(ImageSize)) != NULL)
{
/* Fail if the user requested a fixed base address. */
if ((*BaseAddress) != NULL)
{
MmUnlockAddressSpace(AddressSpace);
return STATUS_CONFLICTING_ADDRESSES;
}
/* Otherwise find a gap to map the image. */
ImageBase = (ULONG_PTR)MmFindGap(AddressSpace, PAGE_ROUND_UP(ImageSize), MM_VIRTMEM_GRANULARITY, FALSE);
if (ImageBase == 0)
{
MmUnlockAddressSpace(AddressSpace);
return STATUS_CONFLICTING_ADDRESSES;
}
/* Remember that we loaded image at a different base address */
NotAtBase = TRUE;
}
for (i = 0; i < NrSegments; i++)
{
PVOID SBaseAddress = (PVOID)
((char*)ImageBase + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
MmLockSectionSegment(&SectionSegments[i]);
Status = MmMapViewOfSegment(AddressSpace,
TRUE,
&SectionSegments[i],
&SBaseAddress,
SectionSegments[i].Length.QuadPart,
SectionSegments[i].Protection,
0,
0);
MmUnlockSectionSegment(&SectionSegments[i]);
if (!NT_SUCCESS(Status))
{
/* roll-back */
while (i--)
{
SBaseAddress = ((char*)ImageBase + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
MmLockSectionSegment(&SectionSegments[i]);
MmUnmapViewOfSegment(AddressSpace, SBaseAddress);
MmUnlockSectionSegment(&SectionSegments[i]);
}
MmUnlockAddressSpace(AddressSpace);
return Status;
}
}
*BaseAddress = (PVOID)ImageBase;
*ViewSize = ImageSize;
}
else
{
PMM_SECTION_SEGMENT Segment = (PMM_SECTION_SEGMENT)Section->Segment;
LONGLONG ViewOffset;
ASSERT(Segment->RefCount > 0);
/* check for write access */
if ((Protect & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE)) &&
!(Section->InitialPageProtection & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE)))
{
MmUnlockAddressSpace(AddressSpace);
return STATUS_SECTION_PROTECTION;
}
/* check for read access */
if ((Protect & (PAGE_READONLY|PAGE_WRITECOPY|PAGE_EXECUTE_READ|PAGE_EXECUTE_WRITECOPY)) &&
!(Section->InitialPageProtection & (PAGE_READONLY|PAGE_READWRITE|PAGE_WRITECOPY|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY)))
{
MmUnlockAddressSpace(AddressSpace);
return STATUS_SECTION_PROTECTION;
}
/* check for execute access */
if ((Protect & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY)) &&
!(Section->InitialPageProtection & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY)))
{
MmUnlockAddressSpace(AddressSpace);
return STATUS_SECTION_PROTECTION;
}
if (SectionOffset == NULL)
{
ViewOffset = 0;
}
else
{
ViewOffset = SectionOffset->QuadPart;
}
if ((ViewOffset % PAGE_SIZE) != 0)
{
MmUnlockAddressSpace(AddressSpace);
return STATUS_MAPPED_ALIGNMENT;
}
if ((*ViewSize) == 0)
{
(*ViewSize) = Section->SizeOfSection.QuadPart - ViewOffset;
}
else if ((ExGetPreviousMode() == UserMode) &&
(((*ViewSize)+ViewOffset) > Section->SizeOfSection.QuadPart) &&
(!Section->u.Flags.Reserve))
{
/* Dubious */
(*ViewSize) = MIN(Section->SizeOfSection.QuadPart - ViewOffset, SIZE_T_MAX - PAGE_SIZE);
}
*ViewSize = PAGE_ROUND_UP(*ViewSize);
MmLockSectionSegment(Segment);
Status = MmMapViewOfSegment(AddressSpace,
FALSE,
Segment,
BaseAddress,
*ViewSize,
Protect,
ViewOffset,
AllocationType & (MEM_TOP_DOWN|SEC_NO_CHANGE));
MmUnlockSectionSegment(Segment);
if (!NT_SUCCESS(Status))
{
MmUnlockAddressSpace(AddressSpace);
return Status;
}
}
MmUnlockAddressSpace(AddressSpace);
if (NotAtBase)
Status = STATUS_IMAGE_NOT_AT_BASE;
else
Status = STATUS_SUCCESS;
return Status;
}
/*
* @unimplemented
*/
BOOLEAN NTAPI
MmCanFileBeTruncated (IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
IN PLARGE_INTEGER NewFileSize)
{
BOOLEAN Ret;
PMM_SECTION_SEGMENT Segment;
/* Check whether an ImageSectionObject exists */
if (SectionObjectPointer->ImageSectionObject != NULL)
{
DPRINT1("ERROR: File can't be truncated because it has an image section\n");
return FALSE;
}
Segment = MiGrabDataSection(SectionObjectPointer);
if (!Segment)
{
/* There is no data section. It's fine to do anything. */
return TRUE;
}
MmLockSectionSegment(Segment);
if ((Segment->SectionCount == 0) ||
((Segment->SectionCount == 1) && (SectionObjectPointer->SharedCacheMap != NULL)))
{
/* If the cache is the only one holding a reference to the segment, then it's fine to resize */
Ret = TRUE;
}
else
{
/* We can't shrink, but we can extend */
Ret = NewFileSize->QuadPart >= Segment->RawLength.QuadPart;
#if DBG
if (!Ret)
{
DPRINT1("Cannot truncate data: New Size %I64d, Segment Size %I64d\n", NewFileSize->QuadPart, Segment->RawLength.QuadPart);
}
#endif
}
MmUnlockSectionSegment(Segment);
MmDereferenceSegment(Segment);
DPRINT("FIXME: didn't check for outstanding write probes\n");
return Ret;
}
/*
* @implemented
*/
BOOLEAN NTAPI
MmFlushImageSection (IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
IN MMFLUSH_TYPE FlushType)
{
switch(FlushType)
{
case MmFlushForDelete:
case MmFlushForWrite:
{
BOOLEAN Ret = TRUE;
KIRQL OldIrql = MiAcquirePfnLock();
if (SectionObjectPointer->ImageSectionObject)
{
PMM_IMAGE_SECTION_OBJECT ImageSectionObject = SectionObjectPointer->ImageSectionObject;
if (!(ImageSectionObject->SegFlags & MM_SEGMENT_INDELETE))
Ret = FALSE;
}
MiReleasePfnLock(OldIrql);
return Ret;
}
}
return FALSE;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
MmMapViewInSystemSpace (IN PVOID SectionObject,
OUT PVOID * MappedBase,
IN OUT PSIZE_T ViewSize)
{
LARGE_INTEGER SectionOffset;
SectionOffset.QuadPart = 0;
return MmMapViewInSystemSpaceEx(SectionObject, MappedBase, ViewSize, &SectionOffset, 0);
}
NTSTATUS
NTAPI
MmMapViewInSystemSpaceEx (
_In_ PVOID SectionObject,
_Outptr_result_bytebuffer_ (*ViewSize) PVOID *MappedBase,
_Inout_ PSIZE_T ViewSize,
_Inout_ PLARGE_INTEGER SectionOffset,
_In_ ULONG_PTR Flags
)
{
PSECTION Section = SectionObject;
PMM_SECTION_SEGMENT Segment;
PMMSUPPORT AddressSpace;
NTSTATUS Status;
UNREFERENCED_PARAMETER(Flags);
PAGED_CODE();
if (MiIsRosSectionObject(SectionObject) == FALSE)
{
return MiMapViewInSystemSpace(SectionObject,
&MmSession,
MappedBase,
ViewSize,
SectionOffset);
}
DPRINT("MmMapViewInSystemSpaceEx() called\n");
Section = SectionObject;
Segment = (PMM_SECTION_SEGMENT)Section->Segment;
if (*ViewSize == 0)
{
LONGLONG MapSizeLL;
/* Page-align the mapping */
SectionOffset->LowPart = PAGE_ROUND_DOWN(SectionOffset->LowPart);
if (!NT_SUCCESS(RtlLongLongSub(Section->SizeOfSection.QuadPart, SectionOffset->QuadPart, &MapSizeLL)))
return STATUS_INVALID_VIEW_SIZE;
if (!NT_SUCCESS(RtlLongLongToSIZET(MapSizeLL, ViewSize)))
return STATUS_INVALID_VIEW_SIZE;
}
else
{
LONGLONG HelperLL;
/* Get the map end */
if (!NT_SUCCESS(RtlLongLongAdd(SectionOffset->QuadPart, *ViewSize, &HelperLL)))
return STATUS_INVALID_VIEW_SIZE;
/* Round it up, if needed */
if (HelperLL % PAGE_SIZE)
{
if (!NT_SUCCESS(RtlLongLongAdd(HelperLL, PAGE_SIZE - (HelperLL % PAGE_SIZE), &HelperLL)))
return STATUS_INVALID_VIEW_SIZE;
}
/* Now that we have the mapping end, we can align down its start */
SectionOffset->LowPart = PAGE_ROUND_DOWN(SectionOffset->LowPart);
/* Get the new size */
if (!NT_SUCCESS(RtlLongLongSub(HelperLL, SectionOffset->QuadPart, &HelperLL)))
return STATUS_INVALID_VIEW_SIZE;
if (!NT_SUCCESS(RtlLongLongToSIZET(HelperLL, ViewSize)))
return STATUS_INVALID_VIEW_SIZE;
}
AddressSpace = MmGetKernelAddressSpace();
MmLockAddressSpace(AddressSpace);
MmLockSectionSegment(Segment);
Status = MmMapViewOfSegment(AddressSpace,
Section->u.Flags.Image,
Segment,
MappedBase,
*ViewSize,
PAGE_READWRITE,
SectionOffset->QuadPart,
SEC_RESERVE);
MmUnlockSectionSegment(Segment);
MmUnlockAddressSpace(AddressSpace);
return Status;
}
/* This function must be called with adress space lock held */
NTSTATUS
NTAPI
MiRosUnmapViewInSystemSpace(IN PVOID MappedBase)
{
DPRINT("MmUnmapViewInSystemSpace() called\n");
return MmUnmapViewOfSegment(MmGetKernelAddressSpace(), MappedBase);
}
/**********************************************************************
* NAME EXPORTED
* MmCreateSection@
*
* DESCRIPTION
* Creates a section object.
*
* ARGUMENTS
* SectionObject (OUT)
* Caller supplied storage for the resulting pointer
* to a SECTION_OBJECT instance;
*
* DesiredAccess
* Specifies the desired access to the section can be a
* combination of:
* STANDARD_RIGHTS_REQUIRED |
* SECTION_QUERY |
* SECTION_MAP_WRITE |
* SECTION_MAP_READ |
* SECTION_MAP_EXECUTE
*
* ObjectAttributes [OPTIONAL]
* Initialized attributes for the object can be used
* to create a named section;
*
* MaximumSize
* Maximizes the size of the memory section. Must be
* non-NULL for a page-file backed section.
* If value specified for a mapped file and the file is
* not large enough, file will be extended.
*
* SectionPageProtection
* Can be a combination of:
* PAGE_READONLY |
* PAGE_READWRITE |
* PAGE_WRITEONLY |
* PAGE_WRITECOPY
*
* AllocationAttributes
* Can be a combination of:
* SEC_IMAGE |
* SEC_RESERVE
*
* FileHandle
* Handle to a file to create a section mapped to a file
* instead of a memory backed section;
*
* File
* Unknown.
*
* RETURN VALUE
* Status.
*
* @implemented
*/
NTSTATUS NTAPI
MmCreateSection (OUT PVOID * Section,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN PLARGE_INTEGER MaximumSize,
IN ULONG SectionPageProtection,
IN ULONG AllocationAttributes,
IN HANDLE FileHandle OPTIONAL,
IN PFILE_OBJECT FileObject OPTIONAL)
{
NTSTATUS Status;
ULONG Protection;
PSECTION *SectionObject = (PSECTION *)Section;
BOOLEAN FileLock = FALSE;
/* Check if an ARM3 section is being created instead */
if (!(AllocationAttributes & (SEC_IMAGE | SEC_PHYSICALMEMORY)))
{
if (!(FileObject) && !(FileHandle))
{
return MmCreateArm3Section(Section,
DesiredAccess,
ObjectAttributes,
MaximumSize,
SectionPageProtection,
AllocationAttributes &~ 1,
FileHandle,
FileObject);
}
}
/* Convert section flag to page flag */
if (AllocationAttributes & SEC_NOCACHE) SectionPageProtection |= PAGE_NOCACHE;
/* Check to make sure the protection is correct. Nt* does this already */
Protection = MiMakeProtectionMask(SectionPageProtection);
if (Protection == MM_INVALID_PROTECTION)
{
DPRINT1("Page protection is invalid\n");
return STATUS_INVALID_PAGE_PROTECTION;
}
/* Check if this is going to be a data or image backed file section */
if ((FileHandle) || (FileObject))
{
/* These cannot be mapped with large pages */
if (AllocationAttributes & SEC_LARGE_PAGES)
{
DPRINT1("Large pages cannot be used with an image mapping\n");
return STATUS_INVALID_PARAMETER_6;
}
/* Did the caller pass a file object ? */
if (FileObject)
{
/* Reference the object directly */
ObReferenceObject(FileObject);
/* We don't create image mappings with file objects */
AllocationAttributes &= ~SEC_IMAGE;
}
else
{
/* Reference the file handle to get the object */
Status = ObReferenceObjectByHandle(FileHandle,
MmMakeFileAccess[Protection],
IoFileObjectType,
ExGetPreviousMode(),
(PVOID*)&FileObject,
NULL);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to get a handle to the FO: %lx\n", Status);
return Status;
}
/* Lock the file */
Status = FsRtlAcquireToCreateMappedSection(FileObject, SectionPageProtection);
if (!NT_SUCCESS(Status))
{
ObDereferenceObject(FileObject);
return Status;
}
FileLock = TRUE;
/* Deny access if there are writes on the file */
#if 0
if ((AllocationAttributes & SEC_IMAGE) && (Status == STATUS_FILE_LOCKED_WITH_WRITERS))
{
DPRINT1("Cannot create image maps with writers open on the file!\n");
Status = STATUS_ACCESS_DENIED;
goto Quit;
}
#else
if ((AllocationAttributes & SEC_IMAGE) && (Status == STATUS_FILE_LOCKED_WITH_WRITERS))
DPRINT1("Creating image map with writers open on the file!\n");
#endif
}
}
else
{
/* A handle must be supplied with SEC_IMAGE, as this is the no-handle path */
if (AllocationAttributes & SEC_IMAGE) return STATUS_INVALID_FILE_FOR_SECTION;
}
if (AllocationAttributes & SEC_IMAGE)
{
Status = MmCreateImageSection(SectionObject,
DesiredAccess,
ObjectAttributes,
MaximumSize,
SectionPageProtection,
AllocationAttributes,
FileObject);
}
#ifndef NEWCC
else if (FileObject != NULL)
{
Status = MmCreateDataFileSection(SectionObject,
DesiredAccess,
ObjectAttributes,
MaximumSize,
SectionPageProtection,
AllocationAttributes,
FileObject,
FileHandle != NULL);
}
#else
else if (FileHandle != NULL || FileObject != NULL)
{
Status = MmCreateCacheSection(SectionObject,
DesiredAccess,
ObjectAttributes,
MaximumSize,
SectionPageProtection,
AllocationAttributes,
FileObject);
}
#endif
else
{
/* All cases should be handled above */
Status = STATUS_INVALID_PARAMETER;
}
if (FileLock)
FsRtlReleaseFile(FileObject);
if (FileObject)
ObDereferenceObject(FileObject);
return Status;
}
BOOLEAN
NTAPI
MmArePagesResident(
_In_ PEPROCESS Process,
_In_ PVOID Address,
_In_ ULONG Length)
{
PMEMORY_AREA MemoryArea;
BOOLEAN Ret = TRUE;
PMM_SECTION_SEGMENT Segment;
LARGE_INTEGER SegmentOffset, RangeEnd;
PMMSUPPORT AddressSpace = Process ? &Process->Vm : MmGetKernelAddressSpace();
MmLockAddressSpace(AddressSpace);
MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
if (MemoryArea == NULL)
{
MmUnlockAddressSpace(AddressSpace);
return FALSE;
}
/* Only supported in old Mm for now */
ASSERT(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW);
/* For file mappings */
ASSERT(MemoryArea->VadNode.u.VadFlags.VadType != VadImageMap);
Segment = MemoryArea->SectionData.Segment;
MmLockSectionSegment(Segment);
SegmentOffset.QuadPart = PAGE_ROUND_DOWN(Address) - MA_GetStartingAddress(MemoryArea)
+ MemoryArea->SectionData.ViewOffset;
RangeEnd.QuadPart = PAGE_ROUND_UP((ULONG_PTR)Address + Length) - MA_GetStartingAddress(MemoryArea)
+ MemoryArea->SectionData.ViewOffset;
while (SegmentOffset.QuadPart < RangeEnd.QuadPart)
{
ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &SegmentOffset);
if ((Entry == 0) || IS_SWAP_FROM_SSE(Entry))
{
Ret = FALSE;
break;
}
SegmentOffset.QuadPart += PAGE_SIZE;
}
MmUnlockSectionSegment(Segment);
MmUnlockAddressSpace(AddressSpace);
return Ret;
}
NTSTATUS
NTAPI
MmRosFlushVirtualMemory(
_In_ PEPROCESS Process,
_Inout_ PVOID* Address,
_Inout_ PSIZE_T Length,
_Out_ PIO_STATUS_BLOCK Iosb)
{
PMEMORY_AREA MemoryArea;
PMM_SECTION_SEGMENT Segment;
LARGE_INTEGER SegmentOffset, RangeEnd;
PMMSUPPORT AddressSpace = Process ? &Process->Vm : MmGetKernelAddressSpace();
PVOID CurrentAddress;
PAGED_CODE();
MmLockAddressSpace(AddressSpace);
DPRINT("Flushing Process %p at %p --> 0x%x", Process, *Address, *Length);
MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *Address);
if ((MemoryArea == NULL) || (MemoryArea->Type != MEMORY_AREA_SECTION_VIEW) ||
(MemoryArea->VadNode.u.VadFlags.VadType == VadImageMap))
{
DPRINT1("Unable to find memory area at address %p.\n", Address);
MmUnlockAddressSpace(AddressSpace);
return STATUS_NOT_MAPPED_VIEW;
}
Segment = MemoryArea->SectionData.Segment;
SegmentOffset.QuadPart = PAGE_ROUND_DOWN(*Address) - MA_GetStartingAddress(MemoryArea)
+ MemoryArea->SectionData.ViewOffset;
RangeEnd.QuadPart = PAGE_ROUND_UP((ULONG_PTR)*Address + *Length) - MA_GetStartingAddress(MemoryArea)
+ MemoryArea->SectionData.ViewOffset;
CurrentAddress = *Address;
MmUnlockAddressSpace(AddressSpace);
MmLockSectionSegment(Segment);
Iosb->Information = 0;
while (SegmentOffset.QuadPart < RangeEnd.QuadPart)
{
ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &SegmentOffset);
/* Let any pending read proceed */
while (MM_IS_WAIT_PTE(Entry))
{
MmUnlockSectionSegment(Segment);
MiWaitForPageEvent(NULL, NULL);
MmLockSectionSegment(Segment);
Entry = MmGetPageEntrySectionSegment(Segment, &SegmentOffset);
}
/* We are called from Cc, this can't be backed by the page files */
ASSERT(!IS_SWAP_FROM_SSE(Entry));
/* At this point, there may be a valid page there */
if (Entry != 0)
{
/* This will write the page to disk, if needed */
MmCheckDirtySegment(Segment, &SegmentOffset, Process ? MmIsDirtyPage(Process, CurrentAddress) : FALSE, FALSE);
Iosb->Information += PAGE_SIZE;
}
SegmentOffset.QuadPart += PAGE_SIZE;
CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + PAGE_SIZE);
}
MmUnlockSectionSegment(Segment);
return STATUS_SUCCESS;
}
/* Like CcPurgeCache but for the in-memory segment */
BOOLEAN
NTAPI
MmPurgeSegment(
_In_ PSECTION_OBJECT_POINTERS SectionObjectPointer,
_In_opt_ PLARGE_INTEGER Offset,
_In_ ULONG Length)
{
LARGE_INTEGER PurgeStart, PurgeEnd;
PMM_SECTION_SEGMENT Segment;
Segment = MiGrabDataSection(SectionObjectPointer);
if (!Segment)
{
/* Nothing to purge */
return STATUS_SUCCESS;
}
PurgeStart.QuadPart = Offset ? Offset->QuadPart : 0LL;
if (Length && Offset)
{
if (!NT_SUCCESS(RtlLongLongAdd(PurgeStart.QuadPart, Length, &PurgeEnd.QuadPart)))
return FALSE;
}
MmLockSectionSegment(Segment);
if (!Length || !Offset)
{
/* We must calculate the length for ourselves */
/* FIXME: All of this is suboptimal */
ULONG ElemCount = RtlNumberGenericTableElements(&Segment->PageTable);
/* No page. Nothing to purge */
if (!ElemCount)
{
MmUnlockSectionSegment(Segment);
MmDereferenceSegment(Segment);
return TRUE;
}
PCACHE_SECTION_PAGE_TABLE PageTable = RtlGetElementGenericTable(&Segment->PageTable, ElemCount - 1);
PurgeEnd.QuadPart = PageTable->FileOffset.QuadPart + _countof(PageTable->PageEntries) * PAGE_SIZE;
}
while (PurgeStart.QuadPart < PurgeEnd.QuadPart)
{
ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &PurgeStart);
if (Entry == 0)
{
PurgeStart.QuadPart += PAGE_SIZE;
continue;
}
if (IS_SWAP_FROM_SSE(Entry))
{
ASSERT(SWAPENTRY_FROM_SSE(Entry) == MM_WAIT_ENTRY);
/* The page is currently being read. Meaning someone will need it soon. Bad luck */
MmUnlockSectionSegment(Segment);
MmDereferenceSegment(Segment);
return FALSE;
}
if (IS_WRITE_SSE(Entry))
{
/* We're trying to purge an entry which is being written. Restart this loop iteration */
MmUnlockSectionSegment(Segment);
KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
MmLockSectionSegment(Segment);
continue;
}
if (SHARE_COUNT_FROM_SSE(Entry) > 0)
{
/* This page is currently in use. Bad luck */
MmUnlockSectionSegment(Segment);
MmDereferenceSegment(Segment);
return FALSE;
}
/* We can let this page go */
MmSetPageEntrySectionSegment(Segment, &PurgeStart, 0);
MmReleasePageMemoryConsumer(MC_USER, PFN_FROM_SSE(Entry));
PurgeStart.QuadPart += PAGE_SIZE;
}
/* This page is currently in use. Bad luck */
MmUnlockSectionSegment(Segment);
MmDereferenceSegment(Segment);
return TRUE;
}
NTSTATUS
NTAPI
MmMakeDataSectionResident(
_In_ PSECTION_OBJECT_POINTERS SectionObjectPointer,
_In_ LONGLONG Offset,
_In_ ULONG Length,
_In_ PLARGE_INTEGER ValidDataLength)
{
PMM_SECTION_SEGMENT Segment = MiGrabDataSection(SectionObjectPointer);
/* There must be a segment for this call */
ASSERT(Segment);
NTSTATUS Status = MmMakeSegmentResident(Segment, Offset, Length, ValidDataLength);
MmDereferenceSegment(Segment);
return Status;
}
NTSTATUS
NTAPI
MmFlushSegment(
_In_ PSECTION_OBJECT_POINTERS SectionObjectPointer,
_In_opt_ PLARGE_INTEGER Offset,
_In_ ULONG Length,
_In_opt_ PIO_STATUS_BLOCK Iosb)
{
LARGE_INTEGER FlushStart, FlushEnd;
NTSTATUS Status;
if (Offset)
{
FlushStart = *Offset;
Status = RtlLongLongAdd(FlushStart.QuadPart, Length, &FlushEnd.QuadPart);
if (!NT_SUCCESS(Status))
return Status;
}
if (Iosb)
Iosb->Information = 0;
PMM_SECTION_SEGMENT Segment = MiGrabDataSection(SectionObjectPointer);
if (!Segment)
{
/* Nothing to flush */
if (Iosb)
Iosb->Status = STATUS_SUCCESS;
return STATUS_SUCCESS;
}
ASSERT(*Segment->Flags & MM_DATAFILE_SEGMENT);
MmLockSectionSegment(Segment);
if (!Offset)
{
FlushStart.QuadPart = 0;
/* FIXME: All of this is suboptimal */
ULONG ElemCount = RtlNumberGenericTableElements(&Segment->PageTable);
/* No page. Nothing to flush */
if (!ElemCount)
{
MmUnlockSectionSegment(Segment);
MmDereferenceSegment(Segment);
if (Iosb)
{
Iosb->Status = STATUS_SUCCESS;
Iosb->Information = 0;
}
return STATUS_SUCCESS;
}
PCACHE_SECTION_PAGE_TABLE PageTable = RtlGetElementGenericTable(&Segment->PageTable, ElemCount - 1);
FlushEnd.QuadPart = PageTable->FileOffset.QuadPart + _countof(PageTable->PageEntries) * PAGE_SIZE;
}
FlushStart.QuadPart >>= PAGE_SHIFT;
FlushStart.QuadPart <<= PAGE_SHIFT;
while (FlushStart.QuadPart < FlushEnd.QuadPart)
{
ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &FlushStart);
if (IS_DIRTY_SSE(Entry))
{
MmCheckDirtySegment(Segment, &FlushStart, FALSE, FALSE);
if (Iosb)
Iosb->Information += PAGE_SIZE;
}
FlushStart.QuadPart += PAGE_SIZE;
}
MmUnlockSectionSegment(Segment);
MmDereferenceSegment(Segment);
if (Iosb)
Iosb->Status = STATUS_SUCCESS;
return STATUS_SUCCESS;
}
_Requires_exclusive_lock_held_(Segment->Lock)
BOOLEAN
NTAPI
MmCheckDirtySegment(
PMM_SECTION_SEGMENT Segment,
PLARGE_INTEGER Offset,
BOOLEAN ForceDirty,
BOOLEAN PageOut)
{
ULONG_PTR Entry;
NTSTATUS Status;
PFN_NUMBER Page;
ASSERT(Segment->Locked);
ASSERT((Offset->QuadPart % PAGE_SIZE) == 0);
DPRINT("Checking segment for file %wZ at offset 0x%I64X.\n", &Segment->FileObject->FileName, Offset->QuadPart);
Entry = MmGetPageEntrySectionSegment(Segment, Offset);
if (Entry == 0)
return FALSE;
Page = PFN_FROM_SSE(Entry);
if ((IS_DIRTY_SSE(Entry)) || ForceDirty)
{
BOOLEAN DirtyAgain;
/*
* We got a dirty entry. This path is for the shared data,
* be-it regular file maps or shared sections of DLLs
*/
ASSERT(!Segment->WriteCopy);
ASSERT(FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT) || FlagOn(Segment->Image.Characteristics, IMAGE_SCN_MEM_SHARED));
/* Insert the cleaned entry back. Mark it as write in progress, and clear the dirty bit. */
Entry = MAKE_SSE(PAGE_FROM_SSE(Entry), SHARE_COUNT_FROM_SSE(Entry) + 1);
Entry = WRITE_SSE(Entry);
MmSetPageEntrySectionSegment(Segment, Offset, Entry);
/* Tell the other users that we are clean again */
MmSetCleanAllRmaps(Page);
MmUnlockSectionSegment(Segment);
if (FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT))
{
/* We have to write it back to the file. Tell the FS driver who we are */
if (PageOut)
IoSetTopLevelIrp((PIRP)FSRTL_MOD_WRITE_TOP_LEVEL_IRP);
/* Go ahead and write the page */
DPRINT("Writing page at offset %I64d for file %wZ, Pageout: %s\n",
Offset->QuadPart, &Segment->FileObject->FileName, PageOut ? "TRUE" : "FALSE");
Status = MiWritePage(Segment, Offset->QuadPart, Page);
if (PageOut)
IoSetTopLevelIrp(NULL);
}
else
{
/* This must only be called by the page-out path */
ASSERT(PageOut);
/* And this must be for a shared section in a DLL */
ASSERT(Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED);
SWAPENTRY SwapEntry = MmGetSavedSwapEntryPage(Page);
if (!SwapEntry)
{
SwapEntry = MmAllocSwapPage();
}
if (SwapEntry)
{
Status = MmWriteToSwapPage(SwapEntry, Page);
if (NT_SUCCESS(Status))
{
MmSetSavedSwapEntryPage(Page, SwapEntry);
}
else
{
MmFreeSwapPage(SwapEntry);
}
}
else
{
DPRINT1("Failed to allocate a swap page!\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
MmLockSectionSegment(Segment);
/* Get the entry again */
Entry = MmGetPageEntrySectionSegment(Segment, Offset);
ASSERT(PFN_FROM_SSE(Entry) == Page);
if (!NT_SUCCESS(Status))
{
/* Damn, this failed. Consider this page as still dirty */
DPRINT1("MiWritePage FAILED: Status 0x%08x!\n", Status);
DirtyAgain = TRUE;
}
else
{
/* Check if someone dirtified this page while we were not looking */
DirtyAgain = IS_DIRTY_SSE(Entry) || MmIsDirtyPageRmap(Page);
}
/* Drop the reference we got, deleting the write altogether. */
Entry = MAKE_SSE(Page << PAGE_SHIFT, SHARE_COUNT_FROM_SSE(Entry) - 1);
if (DirtyAgain)
{
Entry = DIRTY_SSE(Entry);
}
MmSetPageEntrySectionSegment(Segment, Offset, Entry);
}
/* Were this page hanging there just for the sake of being present ? */
if (!IS_DIRTY_SSE(Entry) && (SHARE_COUNT_FROM_SSE(Entry) == 0) && PageOut)
{
ULONG_PTR NewEntry = 0;
/* Restore the swap entry here */
if (!FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT))
{
SWAPENTRY SwapEntry = MmGetSavedSwapEntryPage(Page);
if (SwapEntry)
NewEntry = MAKE_SWAP_SSE(SwapEntry);
}
/* Yes. Release it */
MmSetPageEntrySectionSegment(Segment, Offset, NewEntry);
MmReleasePageMemoryConsumer(MC_USER, Page);
/* Tell the caller we released the page */
return TRUE;
}
return FALSE;
}
NTSTATUS
NTAPI
MmMakePagesDirty(
_In_ PEPROCESS Process,
_In_ PVOID Address,
_In_ ULONG Length)
{
PMEMORY_AREA MemoryArea;
PMM_SECTION_SEGMENT Segment;
LARGE_INTEGER SegmentOffset, RangeEnd;
PMMSUPPORT AddressSpace = Process ? &Process->Vm : MmGetKernelAddressSpace();
MmLockAddressSpace(AddressSpace);
MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
if (MemoryArea == NULL)
{
DPRINT1("Unable to find memory area at address %p.\n", Address);
MmUnlockAddressSpace(AddressSpace);
return STATUS_NOT_MAPPED_VIEW;
}
/* Only supported in old Mm for now */
ASSERT(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW);
/* For file mappings */
ASSERT(MemoryArea->VadNode.u.VadFlags.VadType != VadImageMap);
Segment = MemoryArea->SectionData.Segment;
MmLockSectionSegment(Segment);
SegmentOffset.QuadPart = PAGE_ROUND_DOWN(Address) - MA_GetStartingAddress(MemoryArea)
+ MemoryArea->SectionData.ViewOffset;
RangeEnd.QuadPart = PAGE_ROUND_UP((ULONG_PTR)Address + Length) - MA_GetStartingAddress(MemoryArea)
+ MemoryArea->SectionData.ViewOffset;
DPRINT("MmMakePagesResident: Segment %p, 0x%I64x -> 0x%I64x\n", Segment, SegmentOffset.QuadPart, RangeEnd.QuadPart);
while (SegmentOffset.QuadPart < RangeEnd.QuadPart)
{
ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &SegmentOffset);
/* Let any pending read proceed */
while (MM_IS_WAIT_PTE(Entry))
{
MmUnlockSectionSegment(Segment);
MmUnlockAddressSpace(AddressSpace);
MiWaitForPageEvent(NULL, NULL);
MmLockAddressSpace(AddressSpace);
MmLockSectionSegment(Segment);
Entry = MmGetPageEntrySectionSegment(Segment, &SegmentOffset);
}
/* We are called from Cc, this can't be backed by the page files */
ASSERT(!IS_SWAP_FROM_SSE(Entry));
/* If there is no page there, there is nothing to make dirty */
if (Entry != 0)
{
/* Dirtify the entry */
MmSetPageEntrySectionSegment(Segment, &SegmentOffset, DIRTY_SSE(Entry));
}
SegmentOffset.QuadPart += PAGE_SIZE;
}
MmUnlockSectionSegment(Segment);
MmUnlockAddressSpace(AddressSpace);
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
MmExtendSection(
_In_ PVOID _Section,
_Inout_ PLARGE_INTEGER NewSize)
{
PSECTION Section = _Section;
/* It makes no sense to extend an image mapping */
if (Section->u.Flags.Image)
return STATUS_SECTION_NOT_EXTENDED;
/* Nor is it possible to extend a page file mapping */
if (!Section->u.Flags.File)
return STATUS_SECTION_NOT_EXTENDED;
if (!MiIsRosSectionObject(Section))
return STATUS_NOT_IMPLEMENTED;
/* We just extend the sizes. Shrinking is a no-op ? */
if (NewSize->QuadPart > Section->SizeOfSection.QuadPart)
{
PMM_SECTION_SEGMENT Segment = (PMM_SECTION_SEGMENT)Section->Segment;
Section->SizeOfSection = *NewSize;
if (!Section->u.Flags.Reserve)
{
MmLockSectionSegment(Segment);
if (Segment->RawLength.QuadPart < NewSize->QuadPart)
{
Segment->RawLength = *NewSize;
Segment->Length.QuadPart = (NewSize->QuadPart + PAGE_SIZE - 1) & ~((LONGLONG)PAGE_SIZE);
}
MmUnlockSectionSegment(Segment);
}
}
return STATUS_SUCCESS;
}
/* EOF */