mirror of
https://github.com/reactos/reactos.git
synced 2024-12-22 18:43:30 +08:00
5067 lines
164 KiB
C
5067 lines
164 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;
|
|
|
|
#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
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MiMapViewInSystemSpace(IN PVOID Section,
|
|
IN PVOID Session,
|
|
OUT PVOID *MappedBase,
|
|
IN OUT PSIZE_T ViewSize);
|
|
|
|
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 *****************************************************************/
|
|
|
|
|
|
/*
|
|
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;
|
|
|
|
/* 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
|
|
MmFreeSectionSegments(PFILE_OBJECT FileObject)
|
|
{
|
|
if (FileObject->SectionObjectPointer->ImageSectionObject != NULL)
|
|
{
|
|
PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
|
|
PMM_SECTION_SEGMENT SectionSegments;
|
|
ULONG NrSegments;
|
|
ULONG i;
|
|
|
|
ImageSectionObject = (PMM_IMAGE_SECTION_OBJECT)FileObject->SectionObjectPointer->ImageSectionObject;
|
|
NrSegments = ImageSectionObject->NrSegments;
|
|
SectionSegments = ImageSectionObject->Segments;
|
|
for (i = 0; i < NrSegments; i++)
|
|
{
|
|
if (SectionSegments[i].ReferenceCount != 0)
|
|
{
|
|
DPRINT1("Image segment %lu still referenced (was %lu)\n", i,
|
|
SectionSegments[i].ReferenceCount);
|
|
KeBugCheck(MEMORY_MANAGEMENT);
|
|
}
|
|
MmFreePageTablesSectionSegment(&SectionSegments[i], NULL);
|
|
}
|
|
ObDereferenceObject(ImageSectionObject->FileObject);
|
|
ExFreePool(ImageSectionObject->Segments);
|
|
ExFreePool(ImageSectionObject);
|
|
FileObject->SectionObjectPointer->ImageSectionObject = NULL;
|
|
}
|
|
if (FileObject->SectionObjectPointer->DataSectionObject != NULL)
|
|
{
|
|
PMM_SECTION_SEGMENT Segment;
|
|
|
|
Segment = (PMM_SECTION_SEGMENT)FileObject->SectionObjectPointer->
|
|
DataSectionObject;
|
|
|
|
if (Segment->ReferenceCount != 0)
|
|
{
|
|
DPRINT1("Data segment still referenced\n");
|
|
KeBugCheck(MEMORY_MANAGEMENT);
|
|
}
|
|
ObDereferenceObject(Segment->FileObject);
|
|
MmFreePageTablesSectionSegment(Segment, NULL);
|
|
ExFreePool(Segment);
|
|
FileObject->SectionObjectPointer->DataSectionObject = NULL;
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
Entry = MAKE_SSE(PAGE_FROM_SSE(Entry), SHARE_COUNT_FROM_SSE(Entry) + 1);
|
|
MmSetPageEntrySectionSegment(Segment, Offset, 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);
|
|
BOOLEAN IsDirectMapped = FALSE;
|
|
|
|
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 = MAKE_SSE(PAGE_FROM_SSE(Entry), SHARE_COUNT_FROM_SSE(Entry) - 1);
|
|
/*
|
|
* If we reducing the share count of this entry to zero then set the entry
|
|
* to zero and tell the cache the page is no longer mapped.
|
|
*/
|
|
if (SHARE_COUNT_FROM_SSE(Entry) == 0)
|
|
{
|
|
PFILE_OBJECT FileObject;
|
|
SWAPENTRY SavedSwapEntry;
|
|
PFN_NUMBER Page;
|
|
#ifndef NEWCC
|
|
PROS_SHARED_CACHE_MAP SharedCacheMap;
|
|
BOOLEAN IsImageSection;
|
|
LARGE_INTEGER FileOffset;
|
|
|
|
FileOffset.QuadPart = Offset->QuadPart + Segment->Image.FileOffset;
|
|
IsImageSection = MemoryArea->VadNode.u.VadFlags.VadType == VadImageMap;
|
|
#endif
|
|
|
|
Page = PFN_FROM_SSE(Entry);
|
|
FileObject = Segment->FileObject;
|
|
if (FileObject != NULL &&
|
|
!(Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
|
|
{
|
|
|
|
#ifndef NEWCC
|
|
if ((FileOffset.QuadPart % PAGE_SIZE) == 0 &&
|
|
(Offset->QuadPart + PAGE_SIZE <= Segment->RawLength.QuadPart || !IsImageSection))
|
|
{
|
|
NTSTATUS Status;
|
|
SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
|
|
IsDirectMapped = TRUE;
|
|
#ifndef NEWCC
|
|
Status = CcRosUnmapVacb(SharedCacheMap, FileOffset.QuadPart, Dirty);
|
|
#else
|
|
Status = STATUS_SUCCESS;
|
|
#endif
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("CcRosUnmapVacb failed, status = %x\n", Status);
|
|
KeBugCheck(MEMORY_MANAGEMENT);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
|
|
if (SavedSwapEntry == 0)
|
|
{
|
|
if (!PageOut && (Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
|
|
{
|
|
/*
|
|
* FIXME:
|
|
* Try to page out this page and set the swap entry
|
|
* within the section segment. There exist no rmap entry
|
|
* for this page. The pager thread can't page out a
|
|
* page without a rmap entry.
|
|
*/
|
|
MmSetPageEntrySectionSegment(Segment, Offset, Entry);
|
|
if (InEntry) *InEntry = Entry;
|
|
MiSetPageEvent(NULL, NULL);
|
|
}
|
|
else
|
|
{
|
|
MmSetPageEntrySectionSegment(Segment, Offset, 0);
|
|
if (InEntry) *InEntry = 0;
|
|
MiSetPageEvent(NULL, NULL);
|
|
if (!IsDirectMapped)
|
|
{
|
|
MmReleasePageMemoryConsumer(MC_USER, Page);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED)
|
|
{
|
|
if (!PageOut)
|
|
{
|
|
if (Dirty)
|
|
{
|
|
/*
|
|
* FIXME:
|
|
* We hold all locks. Nobody can do something with the current
|
|
* process and the current segment (also not within an other process).
|
|
*/
|
|
NTSTATUS Status;
|
|
Status = MmWriteToSwapPage(SavedSwapEntry, Page);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n", Status);
|
|
KeBugCheck(MEMORY_MANAGEMENT);
|
|
}
|
|
}
|
|
MmSetPageEntrySectionSegment(Segment, Offset, MAKE_SWAP_SSE(SavedSwapEntry));
|
|
if (InEntry) *InEntry = MAKE_SWAP_SSE(SavedSwapEntry);
|
|
MmSetSavedSwapEntryPage(Page, 0);
|
|
MiSetPageEvent(NULL, NULL);
|
|
}
|
|
MmReleasePageMemoryConsumer(MC_USER, Page);
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("Found a swapentry for a non private page in an image or data file sgment\n");
|
|
KeBugCheck(MEMORY_MANAGEMENT);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (InEntry)
|
|
*InEntry = Entry;
|
|
else
|
|
MmSetPageEntrySectionSegment(Segment, Offset, Entry);
|
|
}
|
|
return(SHARE_COUNT_FROM_SSE(Entry) > 0);
|
|
}
|
|
|
|
BOOLEAN MiIsPageFromCache(PMEMORY_AREA MemoryArea,
|
|
LONGLONG SegOffset)
|
|
{
|
|
#ifndef NEWCC
|
|
if (!(MemoryArea->SectionData.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
|
|
{
|
|
PROS_SHARED_CACHE_MAP SharedCacheMap;
|
|
PROS_VACB Vacb;
|
|
SharedCacheMap = MemoryArea->SectionData.Segment->FileObject->SectionObjectPointer->SharedCacheMap;
|
|
Vacb = CcRosLookupVacb(SharedCacheMap, SegOffset + MemoryArea->SectionData.Segment->Image.FileOffset);
|
|
if (Vacb)
|
|
{
|
|
CcRosReleaseVacb(SharedCacheMap, Vacb, Vacb->Valid, FALSE, TRUE);
|
|
return TRUE;
|
|
}
|
|
}
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
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);
|
|
}
|
|
|
|
#ifndef NEWCC
|
|
NTSTATUS
|
|
NTAPI
|
|
MiReadPage(PMEMORY_AREA MemoryArea,
|
|
LONGLONG SegOffset,
|
|
PPFN_NUMBER Page)
|
|
/*
|
|
* FUNCTION: Read a page for a section backed memory area.
|
|
* PARAMETERS:
|
|
* MemoryArea - Memory area to read the page for.
|
|
* Offset - Offset of the page to read.
|
|
* Page - Variable that receives a page contains the read data.
|
|
*/
|
|
{
|
|
LONGLONG BaseOffset;
|
|
LONGLONG FileOffset;
|
|
PVOID BaseAddress;
|
|
BOOLEAN UptoDate;
|
|
PROS_VACB Vacb;
|
|
PFILE_OBJECT FileObject;
|
|
NTSTATUS Status;
|
|
LONGLONG RawLength;
|
|
PROS_SHARED_CACHE_MAP SharedCacheMap;
|
|
BOOLEAN IsImageSection;
|
|
LONGLONG Length;
|
|
|
|
FileObject = MemoryArea->SectionData.Segment->FileObject;
|
|
SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
|
|
RawLength = MemoryArea->SectionData.Segment->RawLength.QuadPart;
|
|
FileOffset = SegOffset + MemoryArea->SectionData.Segment->Image.FileOffset;
|
|
IsImageSection = MemoryArea->VadNode.u.VadFlags.VadType == VadImageMap;
|
|
|
|
ASSERT(SharedCacheMap);
|
|
|
|
DPRINT("%S %I64x\n", FileObject->FileName.Buffer, FileOffset);
|
|
|
|
/*
|
|
* If the file system is letting us go directly to the cache and the
|
|
* memory area was mapped at an offset in the file which is page aligned
|
|
* then get the related VACB.
|
|
*/
|
|
if (((FileOffset % PAGE_SIZE) == 0) &&
|
|
((SegOffset + PAGE_SIZE <= RawLength) || !IsImageSection) &&
|
|
!(MemoryArea->SectionData.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
|
|
{
|
|
|
|
/*
|
|
* Get the related VACB; we use a lower level interface than
|
|
* filesystems do because it is safe for us to use an offset with an
|
|
* alignment less than the file system block size.
|
|
*/
|
|
Status = CcRosGetVacb(SharedCacheMap,
|
|
FileOffset,
|
|
&BaseOffset,
|
|
&BaseAddress,
|
|
&UptoDate,
|
|
&Vacb);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return(Status);
|
|
}
|
|
if (!UptoDate)
|
|
{
|
|
/*
|
|
* If the VACB isn't up to date then call the file
|
|
* system to read in the data.
|
|
*/
|
|
Status = CcReadVirtualAddress(Vacb);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
/* Probe the page, since it's PDE might not be synced */
|
|
(void)*((volatile char*)BaseAddress + FileOffset - BaseOffset);
|
|
|
|
/*
|
|
* Retrieve the page from the view that we actually want.
|
|
*/
|
|
(*Page) = MmGetPhysicalAddress((char*)BaseAddress +
|
|
FileOffset - BaseOffset).LowPart >> PAGE_SHIFT;
|
|
|
|
CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, TRUE);
|
|
}
|
|
else
|
|
{
|
|
PEPROCESS Process;
|
|
KIRQL Irql;
|
|
PVOID PageAddr;
|
|
LONGLONG VacbOffset;
|
|
|
|
/*
|
|
* Allocate a page, this is rather complicated by the possibility
|
|
* we might have to move other things out of memory
|
|
*/
|
|
MI_SET_USAGE(MI_USAGE_SECTION);
|
|
MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName);
|
|
Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, Page);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return(Status);
|
|
}
|
|
Status = CcRosGetVacb(SharedCacheMap,
|
|
FileOffset,
|
|
&BaseOffset,
|
|
&BaseAddress,
|
|
&UptoDate,
|
|
&Vacb);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return(Status);
|
|
}
|
|
if (!UptoDate)
|
|
{
|
|
/*
|
|
* If the VACB isn't up to date then call the file
|
|
* system to read in the data.
|
|
*/
|
|
Status = CcReadVirtualAddress(Vacb);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
Process = PsGetCurrentProcess();
|
|
PageAddr = MiMapPageInHyperSpace(Process, *Page, &Irql);
|
|
VacbOffset = BaseOffset + VACB_MAPPING_GRANULARITY - FileOffset;
|
|
Length = RawLength - SegOffset;
|
|
if (Length <= VacbOffset && Length <= PAGE_SIZE)
|
|
{
|
|
memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, Length);
|
|
}
|
|
else if (VacbOffset >= PAGE_SIZE)
|
|
{
|
|
memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, PAGE_SIZE);
|
|
}
|
|
else
|
|
{
|
|
memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, VacbOffset);
|
|
MiUnmapPageInHyperSpace(Process, PageAddr, Irql);
|
|
CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, FALSE);
|
|
Status = CcRosGetVacb(SharedCacheMap,
|
|
FileOffset + VacbOffset,
|
|
&BaseOffset,
|
|
&BaseAddress,
|
|
&UptoDate,
|
|
&Vacb);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return(Status);
|
|
}
|
|
if (!UptoDate)
|
|
{
|
|
/*
|
|
* If the VACB isn't up to date then call the file
|
|
* system to read in the data.
|
|
*/
|
|
Status = CcReadVirtualAddress(Vacb);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE);
|
|
return Status;
|
|
}
|
|
}
|
|
PageAddr = MiMapPageInHyperSpace(Process, *Page, &Irql);
|
|
if (Length < PAGE_SIZE)
|
|
{
|
|
memcpy((char*)PageAddr + VacbOffset, BaseAddress, Length - VacbOffset);
|
|
}
|
|
else
|
|
{
|
|
memcpy((char*)PageAddr + VacbOffset, BaseAddress, PAGE_SIZE - VacbOffset);
|
|
}
|
|
}
|
|
MiUnmapPageInHyperSpace(Process, PageAddr, Irql);
|
|
CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, FALSE);
|
|
}
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
#else
|
|
NTSTATUS
|
|
NTAPI
|
|
MiReadPage(PMEMORY_AREA MemoryArea,
|
|
LONGLONG SegOffset,
|
|
PPFN_NUMBER Page)
|
|
/*
|
|
* FUNCTION: Read a page for a section backed memory area.
|
|
* PARAMETERS:
|
|
* MemoryArea - Memory area to read the page for.
|
|
* Offset - Offset of the page to read.
|
|
* Page - Variable that receives a page contains the read data.
|
|
*/
|
|
{
|
|
MM_REQUIRED_RESOURCES Resources;
|
|
NTSTATUS Status;
|
|
|
|
RtlZeroMemory(&Resources, sizeof(MM_REQUIRED_RESOURCES));
|
|
|
|
Resources.Context = MemoryArea->SectionData.Section->FileObject;
|
|
Resources.FileOffset.QuadPart = SegOffset +
|
|
MemoryArea->SectionData.Segment->Image.FileOffset;
|
|
Resources.Consumer = MC_USER;
|
|
Resources.Amount = PAGE_SIZE;
|
|
|
|
DPRINT("%S, offset 0x%x, len 0x%x, page 0x%x\n", ((PFILE_OBJECT)Resources.Context)->FileName.Buffer, Resources.FileOffset.LowPart, Resources.Amount, Resources.Page[0]);
|
|
|
|
Status = MiReadFilePage(MmGetKernelAddressSpace(), MemoryArea, &Resources);
|
|
*Page = Resources.Page[0];
|
|
return Status;
|
|
}
|
|
#endif
|
|
|
|
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.QuadPart;
|
|
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;
|
|
PSECTION Section;
|
|
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.QuadPart;
|
|
|
|
Segment = MemoryArea->SectionData.Segment;
|
|
Section = MemoryArea->SectionData.Section;
|
|
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;
|
|
|
|
/*
|
|
* Is it a wait entry?
|
|
*/
|
|
if (HasSwapEntry)
|
|
{
|
|
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
|
|
*/
|
|
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 (Section->u.Flags.PhysicalMemory)
|
|
{
|
|
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)
|
|
{
|
|
SWAPENTRY FakeSwapEntry;
|
|
|
|
/*
|
|
* If the entry is zero (and it can't change because we have
|
|
* locked the segment) then we need to load the page.
|
|
*/
|
|
|
|
/*
|
|
* Release all our locks and read in the page from disk
|
|
*/
|
|
MmSetPageEntrySectionSegment(Segment, &Offset, MAKE_SWAP_SSE(MM_WAIT_ENTRY));
|
|
MmUnlockSectionSegment(Segment);
|
|
MmCreatePageFileMapping(Process, PAddress, MM_WAIT_ENTRY);
|
|
MmUnlockAddressSpace(AddressSpace);
|
|
|
|
if ((Offset.QuadPart >= (LONGLONG)PAGE_ROUND_UP(Segment->RawLength.QuadPart)) && (MemoryArea->VadNode.u.VadFlags.VadType == VadImageMap))
|
|
{
|
|
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))
|
|
{
|
|
DPRINT1("MmRequestPageMemoryConsumer failed (Status %x)\n", Status);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
Status = MiReadPage(MemoryArea, Offset.QuadPart, &Page);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("MiReadPage failed (Status %x)\n", Status);
|
|
}
|
|
}
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/*
|
|
* FIXME: What do we know in this case?
|
|
*/
|
|
/*
|
|
* Cleanup and release locks
|
|
*/
|
|
MmLockAddressSpace(AddressSpace);
|
|
MiSetPageEvent(Process, Address);
|
|
DPRINT("Address 0x%p\n", Address);
|
|
return(Status);
|
|
}
|
|
|
|
/* Lock both segment and process address space while we proceed. */
|
|
MmLockAddressSpace(AddressSpace);
|
|
MmLockSectionSegment(Segment);
|
|
|
|
MmDeletePageFileMapping(Process, PAddress, &FakeSwapEntry);
|
|
DPRINT("CreateVirtualMapping Page %x Process %p PAddress %p Attributes %x\n",
|
|
Page, Process, PAddress, Attributes);
|
|
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));
|
|
MmInsertRmap(Page, Process, Address);
|
|
|
|
/* Set this section offset has being backed by our new page. */
|
|
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 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,
|
|
Region->Protect,
|
|
&Page,
|
|
1);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Unable to create virtual mapping\n");
|
|
KeBugCheck(MEMORY_MANAGEMENT);
|
|
}
|
|
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);
|
|
}
|
|
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.QuadPart;
|
|
|
|
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);
|
|
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);
|
|
}
|
|
MmInsertRmap(NewPage, Process, PAddress);
|
|
|
|
MiSetPageEvent(Process, Address);
|
|
DPRINT("Address 0x%p\n", Address);
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
VOID
|
|
MmPageOutDeleteMapping(PVOID Context, PEPROCESS Process, PVOID Address)
|
|
{
|
|
MM_SECTION_PAGEOUT_CONTEXT* PageOutContext;
|
|
BOOLEAN WasDirty;
|
|
PFN_NUMBER Page = 0;
|
|
|
|
PageOutContext = (MM_SECTION_PAGEOUT_CONTEXT*)Context;
|
|
if (Process)
|
|
{
|
|
MmLockAddressSpace(&Process->Vm);
|
|
}
|
|
|
|
MmDeleteVirtualMapping(Process,
|
|
Address,
|
|
&WasDirty,
|
|
&Page);
|
|
if (WasDirty)
|
|
{
|
|
PageOutContext->WasDirty = TRUE;
|
|
}
|
|
if (!PageOutContext->Private)
|
|
{
|
|
MmLockSectionSegment(PageOutContext->Segment);
|
|
MmUnsharePageEntrySectionSegment(PageOutContext->MemoryArea,
|
|
PageOutContext->Segment,
|
|
&PageOutContext->Offset,
|
|
PageOutContext->WasDirty,
|
|
TRUE,
|
|
&PageOutContext->SectionEntry);
|
|
MmUnlockSectionSegment(PageOutContext->Segment);
|
|
}
|
|
if (Process)
|
|
{
|
|
MmUnlockAddressSpace(&Process->Vm);
|
|
}
|
|
|
|
if (PageOutContext->Private)
|
|
{
|
|
MmReleasePageMemoryConsumer(MC_USER, Page);
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MmPageOutSectionView(PMMSUPPORT AddressSpace,
|
|
MEMORY_AREA* MemoryArea,
|
|
PVOID Address, ULONG_PTR Entry)
|
|
{
|
|
PFN_NUMBER Page;
|
|
MM_SECTION_PAGEOUT_CONTEXT Context;
|
|
SWAPENTRY SwapEntry;
|
|
NTSTATUS Status;
|
|
#ifndef NEWCC
|
|
ULONGLONG FileOffset;
|
|
PFILE_OBJECT FileObject;
|
|
PROS_SHARED_CACHE_MAP SharedCacheMap = NULL;
|
|
BOOLEAN IsImageSection;
|
|
#endif
|
|
BOOLEAN DirectMapped;
|
|
PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
|
|
KIRQL OldIrql;
|
|
|
|
Address = (PVOID)PAGE_ROUND_DOWN(Address);
|
|
|
|
/*
|
|
* Get the segment and section.
|
|
*/
|
|
Context.Segment = MemoryArea->SectionData.Segment;
|
|
Context.MemoryArea = MemoryArea;
|
|
Context.SectionEntry = Entry;
|
|
Context.CallingProcess = Process;
|
|
|
|
Context.Offset.QuadPart = (ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea)
|
|
+ MemoryArea->SectionData.ViewOffset.QuadPart;
|
|
|
|
DirectMapped = FALSE;
|
|
|
|
MmLockSectionSegment(Context.Segment);
|
|
|
|
#ifndef NEWCC
|
|
FileOffset = Context.Offset.QuadPart + Context.Segment->Image.FileOffset;
|
|
IsImageSection = MemoryArea->VadNode.u.VadFlags.VadType == VadImageMap;
|
|
FileObject = Context.Segment->FileObject;
|
|
|
|
if (FileObject != NULL &&
|
|
!(Context.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
|
|
{
|
|
SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
|
|
|
|
/*
|
|
* If the file system is letting us go directly to the cache and the
|
|
* memory area was mapped at an offset in the file which is page aligned
|
|
* then note this is a direct mapped page.
|
|
*/
|
|
if ((FileOffset % PAGE_SIZE) == 0 &&
|
|
(Context.Offset.QuadPart + PAGE_SIZE <= Context.Segment->RawLength.QuadPart || !IsImageSection))
|
|
{
|
|
DirectMapped = TRUE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Get the section segment entry and the physical address.
|
|
*/
|
|
if (!MmIsPagePresent(Process, Address))
|
|
{
|
|
DPRINT1("Trying to page out not-present page at (%p,0x%p).\n",
|
|
Process ? Process->UniqueProcessId : 0, Address);
|
|
KeBugCheck(MEMORY_MANAGEMENT);
|
|
}
|
|
Page = MmGetPfnForProcess(Process, Address);
|
|
SwapEntry = MmGetSavedSwapEntryPage(Page);
|
|
|
|
/*
|
|
* Check the reference count to ensure this page can be paged out
|
|
*/
|
|
if (MmGetReferenceCountPage(Page) != 1)
|
|
{
|
|
DPRINT("Cannot page out locked section page: 0x%lu (RefCount: %lu)\n",
|
|
Page, MmGetReferenceCountPage(Page));
|
|
MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry);
|
|
MmUnlockSectionSegment(Context.Segment);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
/*
|
|
* Prepare the context structure for the rmap delete call.
|
|
*/
|
|
MmUnlockSectionSegment(Context.Segment);
|
|
Context.WasDirty = FALSE;
|
|
if (IS_SWAP_FROM_SSE(Entry) || PFN_FROM_SSE(Entry) != Page)
|
|
{
|
|
Context.Private = TRUE;
|
|
}
|
|
else
|
|
{
|
|
Context.Private = FALSE;
|
|
}
|
|
|
|
/*
|
|
* Take an additional reference to the page or the VACB.
|
|
*/
|
|
if (DirectMapped && !Context.Private)
|
|
{
|
|
if(!MiIsPageFromCache(MemoryArea, Context.Offset.QuadPart))
|
|
{
|
|
DPRINT1("Direct mapped non private page is not associated with the cache.\n");
|
|
KeBugCheck(MEMORY_MANAGEMENT);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OldIrql = MiAcquirePfnLock();
|
|
MmReferencePage(Page);
|
|
MiReleasePfnLock(OldIrql);
|
|
}
|
|
|
|
MmDeleteAllRmaps(Page, (PVOID)&Context, MmPageOutDeleteMapping);
|
|
|
|
/* Since we passed in a surrogate, we'll get back the page entry
|
|
* state in our context. This is intended to make intermediate
|
|
* decrements of share count not release the wait entry.
|
|
*/
|
|
Entry = Context.SectionEntry;
|
|
|
|
/*
|
|
* If this wasn't a private page then we should have reduced the entry to
|
|
* zero by deleting all the rmaps.
|
|
*/
|
|
if (!Context.Private && Entry != 0)
|
|
{
|
|
if (!(Context.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
|
|
{
|
|
KeBugCheckEx(MEMORY_MANAGEMENT, Entry, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If the page wasn't dirty then we can just free it as for a readonly page.
|
|
* Since we unmapped all the mappings above we know it will not suddenly
|
|
* become dirty.
|
|
* If the page is from a pagefile section and has no swap entry,
|
|
* we can't free the page at this point.
|
|
*/
|
|
SwapEntry = MmGetSavedSwapEntryPage(Page);
|
|
if (Context.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED)
|
|
{
|
|
if (Context.Private)
|
|
{
|
|
DPRINT1("Found a %s private page (address %p) in a shared section segment.\n",
|
|
Context.WasDirty ? "dirty" : "clean", Address);
|
|
KeBugCheckEx(MEMORY_MANAGEMENT, Page, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
|
|
}
|
|
if (!Context.WasDirty || SwapEntry != 0)
|
|
{
|
|
MmSetSavedSwapEntryPage(Page, 0);
|
|
if (SwapEntry != 0)
|
|
{
|
|
MmLockSectionSegment(Context.Segment);
|
|
MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, MAKE_SWAP_SSE(SwapEntry));
|
|
MmUnlockSectionSegment(Context.Segment);
|
|
}
|
|
MmReleasePageMemoryConsumer(MC_USER, Page);
|
|
MiSetPageEvent(NULL, NULL);
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
}
|
|
else if (!Context.Private && DirectMapped)
|
|
{
|
|
if (SwapEntry != 0)
|
|
{
|
|
DPRINT1("Found a swapentry for a non private and direct mapped page (address %p)\n",
|
|
Address);
|
|
KeBugCheckEx(MEMORY_MANAGEMENT, STATUS_UNSUCCESSFUL, SwapEntry, (ULONG_PTR)Process, (ULONG_PTR)Address);
|
|
}
|
|
#ifndef NEWCC
|
|
Status = CcRosUnmapVacb(SharedCacheMap, FileOffset, FALSE);
|
|
#else
|
|
Status = STATUS_SUCCESS;
|
|
#endif
|
|
#ifndef NEWCC
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("CcRosUnmapVacb failed, status = %x\n", Status);
|
|
KeBugCheckEx(MEMORY_MANAGEMENT, Status, (ULONG_PTR)SharedCacheMap, (ULONG_PTR)FileOffset, (ULONG_PTR)Address);
|
|
}
|
|
#endif
|
|
MiSetPageEvent(NULL, NULL);
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
else if (!Context.WasDirty && !DirectMapped && !Context.Private)
|
|
{
|
|
if (SwapEntry != 0)
|
|
{
|
|
DPRINT1("Found a swap entry for a non dirty, non private and not direct mapped page (address %p)\n",
|
|
Address);
|
|
KeBugCheckEx(MEMORY_MANAGEMENT, SwapEntry, Page, (ULONG_PTR)Process, (ULONG_PTR)Address);
|
|
}
|
|
MmReleasePageMemoryConsumer(MC_USER, Page);
|
|
MiSetPageEvent(NULL, NULL);
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
else if (!Context.WasDirty && Context.Private && SwapEntry != 0)
|
|
{
|
|
DPRINT("Not dirty and private and not swapped (%p:%p)\n", Process, Address);
|
|
MmSetSavedSwapEntryPage(Page, 0);
|
|
MmLockAddressSpace(AddressSpace);
|
|
Status = MmCreatePageFileMapping(Process,
|
|
Address,
|
|
SwapEntry);
|
|
MmUnlockAddressSpace(AddressSpace);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Status %x Swapping out %p:%p\n", Status, Process, Address);
|
|
KeBugCheckEx(MEMORY_MANAGEMENT, Status, (ULONG_PTR)Process, (ULONG_PTR)Address, SwapEntry);
|
|
}
|
|
MmReleasePageMemoryConsumer(MC_USER, Page);
|
|
MiSetPageEvent(NULL, NULL);
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* If necessary, allocate an entry in the paging file for this page
|
|
*/
|
|
if (SwapEntry == 0)
|
|
{
|
|
SwapEntry = MmAllocSwapPage();
|
|
if (SwapEntry == 0)
|
|
{
|
|
MmShowOutOfSpaceMessagePagingFile();
|
|
MmLockAddressSpace(AddressSpace);
|
|
/*
|
|
* For private pages restore the old mappings.
|
|
*/
|
|
if (Context.Private)
|
|
{
|
|
Status = MmCreateVirtualMapping(Process,
|
|
Address,
|
|
MmProtectToValue[MemoryArea->VadNode.u.VadFlags.Protection],
|
|
&Page,
|
|
1);
|
|
MmSetDirtyPage(Process, Address);
|
|
MmInsertRmap(Page,
|
|
Process,
|
|
Address);
|
|
}
|
|
else
|
|
{
|
|
ULONG_PTR OldEntry;
|
|
|
|
MmLockSectionSegment(Context.Segment);
|
|
|
|
/*
|
|
* For non-private pages if the page wasn't direct mapped then
|
|
* set it back into the section segment entry so we don't loose
|
|
* our copy. Otherwise it will be handled by the cache manager.
|
|
*/
|
|
Status = MmCreateVirtualMapping(Process,
|
|
Address,
|
|
MmProtectToValue[MemoryArea->VadNode.u.VadFlags.Protection],
|
|
&Page,
|
|
1);
|
|
MmSetDirtyPage(Process, Address);
|
|
MmInsertRmap(Page,
|
|
Process,
|
|
Address);
|
|
// If we got here, the previous entry should have been a wait
|
|
Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
|
|
OldEntry = MmGetPageEntrySectionSegment(Context.Segment, &Context.Offset);
|
|
ASSERT(OldEntry == 0 || OldEntry == MAKE_SWAP_SSE(MM_WAIT_ENTRY));
|
|
MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry);
|
|
MmUnlockSectionSegment(Context.Segment);
|
|
}
|
|
MmUnlockAddressSpace(AddressSpace);
|
|
MiSetPageEvent(NULL, NULL);
|
|
return(STATUS_PAGEFILE_QUOTA);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Write the page to the pagefile
|
|
*/
|
|
Status = MmWriteToSwapPage(SwapEntry, Page);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
|
|
Status);
|
|
/*
|
|
* As above: undo our actions.
|
|
* FIXME: Also free the swap page.
|
|
*/
|
|
MmLockAddressSpace(AddressSpace);
|
|
if (Context.Private)
|
|
{
|
|
Status = MmCreateVirtualMapping(Process,
|
|
Address,
|
|
MmProtectToValue[MemoryArea->VadNode.u.VadFlags.Protection],
|
|
&Page,
|
|
1);
|
|
MmSetDirtyPage(Process, Address);
|
|
MmInsertRmap(Page,
|
|
Process,
|
|
Address);
|
|
}
|
|
else
|
|
{
|
|
MmLockSectionSegment(Context.Segment);
|
|
Status = MmCreateVirtualMapping(Process,
|
|
Address,
|
|
MmProtectToValue[MemoryArea->VadNode.u.VadFlags.Protection],
|
|
&Page,
|
|
1);
|
|
MmSetDirtyPage(Process, Address);
|
|
MmInsertRmap(Page,
|
|
Process,
|
|
Address);
|
|
Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
|
|
MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry);
|
|
MmUnlockSectionSegment(Context.Segment);
|
|
}
|
|
MmUnlockAddressSpace(AddressSpace);
|
|
MiSetPageEvent(NULL, NULL);
|
|
return(STATUS_UNSUCCESSFUL);
|
|
}
|
|
|
|
/*
|
|
* Otherwise we have succeeded.
|
|
*/
|
|
DPRINT("MM: Wrote section page 0x%.8X to swap!\n", Page << PAGE_SHIFT);
|
|
MmSetSavedSwapEntryPage(Page, 0);
|
|
if (Context.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED)
|
|
{
|
|
MmLockSectionSegment(Context.Segment);
|
|
MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, MAKE_SWAP_SSE(SwapEntry));
|
|
MmUnlockSectionSegment(Context.Segment);
|
|
}
|
|
else
|
|
{
|
|
MmReleasePageMemoryConsumer(MC_USER, Page);
|
|
}
|
|
|
|
if (Context.Private)
|
|
{
|
|
MmLockAddressSpace(AddressSpace);
|
|
MmLockSectionSegment(Context.Segment);
|
|
Status = MmCreatePageFileMapping(Process,
|
|
Address,
|
|
SwapEntry);
|
|
/* We had placed a wait entry upon entry ... replace it before leaving */
|
|
MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry);
|
|
MmUnlockSectionSegment(Context.Segment);
|
|
MmUnlockAddressSpace(AddressSpace);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Status %x Creating page file mapping for %p:%p\n", Status, Process, Address);
|
|
KeBugCheckEx(MEMORY_MANAGEMENT, Status, (ULONG_PTR)Process, (ULONG_PTR)Address, SwapEntry);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MmLockAddressSpace(AddressSpace);
|
|
MmLockSectionSegment(Context.Segment);
|
|
Entry = MAKE_SWAP_SSE(SwapEntry);
|
|
/* We had placed a wait entry upon entry ... replace it before leaving */
|
|
MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry);
|
|
MmUnlockSectionSegment(Context.Segment);
|
|
MmUnlockAddressSpace(AddressSpace);
|
|
}
|
|
|
|
MiSetPageEvent(NULL, NULL);
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MmWritePageSectionView(PMMSUPPORT AddressSpace,
|
|
PMEMORY_AREA MemoryArea,
|
|
PVOID Address,
|
|
ULONG PageEntry)
|
|
{
|
|
LARGE_INTEGER Offset;
|
|
PMM_SECTION_SEGMENT Segment;
|
|
PFN_NUMBER Page;
|
|
SWAPENTRY SwapEntry;
|
|
ULONG_PTR Entry;
|
|
BOOLEAN Private;
|
|
NTSTATUS Status;
|
|
PFILE_OBJECT FileObject;
|
|
#ifndef NEWCC
|
|
PROS_SHARED_CACHE_MAP SharedCacheMap = NULL;
|
|
#endif
|
|
BOOLEAN DirectMapped;
|
|
BOOLEAN IsImageSection;
|
|
PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
|
|
|
|
Address = (PVOID)PAGE_ROUND_DOWN(Address);
|
|
|
|
Offset.QuadPart = (ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea)
|
|
+ MemoryArea->SectionData.ViewOffset.QuadPart;
|
|
|
|
/*
|
|
* Get the segment and section.
|
|
*/
|
|
Segment = MemoryArea->SectionData.Segment;
|
|
IsImageSection = MemoryArea->VadNode.u.VadFlags.VadType == VadImageMap;
|
|
|
|
FileObject = Segment->FileObject;
|
|
DirectMapped = FALSE;
|
|
if (FileObject != NULL &&
|
|
!(Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
|
|
{
|
|
#ifndef NEWCC
|
|
SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
|
|
#endif
|
|
|
|
/*
|
|
* If the file system is letting us go directly to the cache and the
|
|
* memory area was mapped at an offset in the file which is page aligned
|
|
* then note this is a direct mapped page.
|
|
*/
|
|
if (((Offset.QuadPart + Segment->Image.FileOffset) % PAGE_SIZE) == 0 &&
|
|
(Offset.QuadPart + PAGE_SIZE <= Segment->RawLength.QuadPart || !IsImageSection))
|
|
{
|
|
DirectMapped = TRUE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get the section segment entry and the physical address.
|
|
*/
|
|
Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
|
|
if (!MmIsPagePresent(Process, Address))
|
|
{
|
|
DPRINT1("Trying to page out not-present page at (%p,0x%p).\n",
|
|
Process ? Process->UniqueProcessId : 0, Address);
|
|
KeBugCheck(MEMORY_MANAGEMENT);
|
|
}
|
|
Page = MmGetPfnForProcess(Process, Address);
|
|
SwapEntry = MmGetSavedSwapEntryPage(Page);
|
|
|
|
/*
|
|
* Check for a private (COWed) page.
|
|
*/
|
|
if (IS_SWAP_FROM_SSE(Entry) || PFN_FROM_SSE(Entry) != Page)
|
|
{
|
|
Private = TRUE;
|
|
}
|
|
else
|
|
{
|
|
Private = FALSE;
|
|
}
|
|
|
|
/*
|
|
* Speculatively set all mappings of the page to clean.
|
|
*/
|
|
MmSetCleanAllRmaps(Page);
|
|
|
|
/*
|
|
* If this page was direct mapped from the cache then the cache manager
|
|
* will take care of writing it back to disk.
|
|
*/
|
|
if (DirectMapped && !Private)
|
|
{
|
|
//LARGE_INTEGER SOffset;
|
|
ASSERT(SwapEntry == 0);
|
|
//SOffset.QuadPart = Offset.QuadPart + Segment->Image.FileOffset;
|
|
#ifndef NEWCC
|
|
CcRosMarkDirtyFile(SharedCacheMap, Offset.QuadPart);
|
|
#endif
|
|
MmLockSectionSegment(Segment);
|
|
MmSetPageEntrySectionSegment(Segment, &Offset, PageEntry);
|
|
MmUnlockSectionSegment(Segment);
|
|
MiSetPageEvent(NULL, NULL);
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* If necessary, allocate an entry in the paging file for this page
|
|
*/
|
|
if (SwapEntry == 0)
|
|
{
|
|
SwapEntry = MmAllocSwapPage();
|
|
if (SwapEntry == 0)
|
|
{
|
|
MmSetDirtyAllRmaps(Page);
|
|
MiSetPageEvent(NULL, NULL);
|
|
return(STATUS_PAGEFILE_QUOTA);
|
|
}
|
|
MmSetSavedSwapEntryPage(Page, SwapEntry);
|
|
}
|
|
|
|
/*
|
|
* Write the page to the pagefile
|
|
*/
|
|
Status = MmWriteToSwapPage(SwapEntry, Page);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
|
|
Status);
|
|
MmSetDirtyAllRmaps(Page);
|
|
MiSetPageEvent(NULL, NULL);
|
|
return(STATUS_UNSUCCESSFUL);
|
|
}
|
|
|
|
/*
|
|
* Otherwise we have succeeded.
|
|
*/
|
|
DPRINT("MM: Wrote section page 0x%.8X to swap!\n", Page << PAGE_SHIFT);
|
|
MiSetPageEvent(NULL, NULL);
|
|
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
|
|
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);
|
|
}
|
|
|
|
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)
|
|
{
|
|
ULONG i;
|
|
ULONG NrSegments;
|
|
ULONG RefCount;
|
|
PMM_SECTION_SEGMENT SectionSegments;
|
|
|
|
/*
|
|
* 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;
|
|
|
|
SectionSegments = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment)->Segments;
|
|
NrSegments = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment)->NrSegments;
|
|
|
|
for (i = 0; i < NrSegments; i++)
|
|
{
|
|
if (SectionSegments[i].Image.Characteristics & IMAGE_SCN_MEM_SHARED)
|
|
{
|
|
MmLockSectionSegment(&SectionSegments[i]);
|
|
}
|
|
RefCount = InterlockedDecrementUL(&SectionSegments[i].ReferenceCount);
|
|
if (SectionSegments[i].Image.Characteristics & IMAGE_SCN_MEM_SHARED)
|
|
{
|
|
MmUnlockSectionSegment(&SectionSegments[i]);
|
|
if (RefCount == 0)
|
|
{
|
|
MmpFreePageFileSegment(&SectionSegments[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#ifdef NEWCC
|
|
else if (Section->Segment && Section->Segment->Flags & MM_DATAFILE_SEGMENT)
|
|
{
|
|
ULONG RefCount = 0;
|
|
PMM_SECTION_SEGMENT Segment = Section->Segment;
|
|
|
|
if (Segment &&
|
|
(RefCount = InterlockedDecrementUL(&Segment->ReferenceCount)) == 0)
|
|
{
|
|
DPRINT("Freeing section segment\n");
|
|
Section->Segment = NULL;
|
|
MmFinalizeSegment(Segment);
|
|
}
|
|
else
|
|
{
|
|
DPRINT("RefCount %d\n", RefCount);
|
|
}
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
/*
|
|
* NOTE: Section->Segment can be NULL for short time
|
|
* during the section creating.
|
|
*/
|
|
if (Section->Segment == NULL)
|
|
return;
|
|
|
|
(void)InterlockedDecrementUL(&((PMM_SECTION_SEGMENT)Section->Segment)->ReferenceCount);
|
|
}
|
|
|
|
if (Section->Segment)
|
|
{
|
|
PMM_SECTION_SEGMENT Segment = (PMM_SECTION_SEGMENT)Section->Segment;
|
|
if (Segment->FileObject != NULL)
|
|
{
|
|
#ifndef NEWCC
|
|
CcRosDereferenceCache(Segment->FileObject);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
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 = ~((ULONG_PTR)0);
|
|
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->ReferenceCount = 1;
|
|
ExInitializeFastMutex(&Segment->Lock);
|
|
Segment->Image.FileOffset = 0;
|
|
Segment->Protection = PAGE_EXECUTE_READWRITE;
|
|
Segment->RawLength = SectionSize;
|
|
Segment->Length = SectionSize;
|
|
Segment->Flags = 0;
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MmCreateDataFileSection(PSECTION *SectionObject,
|
|
ACCESS_MASK DesiredAccess,
|
|
POBJECT_ATTRIBUTES ObjectAttributes,
|
|
PLARGE_INTEGER UMaximumSize,
|
|
ULONG SectionPageProtection,
|
|
ULONG AllocationAttributes,
|
|
PFILE_OBJECT FileObject)
|
|
/*
|
|
* Create a section backed by a data file
|
|
*/
|
|
{
|
|
PSECTION Section;
|
|
NTSTATUS Status;
|
|
LARGE_INTEGER MaximumSize;
|
|
PMM_SECTION_SEGMENT Segment;
|
|
FILE_STANDARD_INFORMATION FileInfo;
|
|
ULONG Length;
|
|
|
|
/*
|
|
* Create the section
|
|
*/
|
|
Status = ObCreateObject(ExGetPreviousMode(),
|
|
MmSectionObjectType,
|
|
ObjectAttributes,
|
|
ExGetPreviousMode(),
|
|
NULL,
|
|
sizeof(*Section),
|
|
0,
|
|
0,
|
|
(PVOID*)&Section);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ObDereferenceObject(FileObject);
|
|
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;
|
|
|
|
/*
|
|
* FIXME: This is propably not entirely correct. We can't look into
|
|
* the standard FCB header because it might not be initialized yet
|
|
* (as in case of the EXT2FS driver by Manoj Paul Joseph where the
|
|
* standard file information is filled on first request).
|
|
*/
|
|
Status = IoQueryFileInformation(FileObject,
|
|
FileStandardInformation,
|
|
sizeof(FILE_STANDARD_INFORMATION),
|
|
&FileInfo,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ObDereferenceObject(Section);
|
|
ObDereferenceObject(FileObject);
|
|
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 = FileInfo.EndOfFile;
|
|
/* Mapping zero-sized files isn't allowed. */
|
|
if (MaximumSize.QuadPart == 0)
|
|
{
|
|
ObDereferenceObject(Section);
|
|
ObDereferenceObject(FileObject);
|
|
return STATUS_MAPPED_FILE_SIZE_ZERO;
|
|
}
|
|
}
|
|
|
|
if (MaximumSize.QuadPart > FileInfo.EndOfFile.QuadPart)
|
|
{
|
|
Status = IoSetInformation(FileObject,
|
|
FileEndOfFileInformation,
|
|
sizeof(LARGE_INTEGER),
|
|
&MaximumSize);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ObDereferenceObject(Section);
|
|
ObDereferenceObject(FileObject);
|
|
return(STATUS_SECTION_NOT_EXTENDED);
|
|
}
|
|
}
|
|
|
|
if (FileObject->SectionObjectPointer == NULL ||
|
|
FileObject->SectionObjectPointer->SharedCacheMap == NULL)
|
|
{
|
|
ObDereferenceObject(Section);
|
|
ObDereferenceObject(FileObject);
|
|
return STATUS_INVALID_FILE_FOR_SECTION;
|
|
}
|
|
|
|
/*
|
|
* Lock the file
|
|
*/
|
|
Status = MmspWaitForFileLock(FileObject);
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
ObDereferenceObject(Section);
|
|
ObDereferenceObject(FileObject);
|
|
return(Status);
|
|
}
|
|
|
|
/*
|
|
* If this file hasn't been mapped as a data file before then allocate a
|
|
* section segment to describe the data file mapping
|
|
*/
|
|
if (FileObject->SectionObjectPointer->DataSectionObject == NULL)
|
|
{
|
|
Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
|
|
TAG_MM_SECTION_SEGMENT);
|
|
if (Segment == NULL)
|
|
{
|
|
//KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
|
|
ObDereferenceObject(Section);
|
|
ObDereferenceObject(FileObject);
|
|
return(STATUS_NO_MEMORY);
|
|
}
|
|
Section->Segment = (PSEGMENT)Segment;
|
|
Segment->ReferenceCount = 1;
|
|
ExInitializeFastMutex(&Segment->Lock);
|
|
Segment->FileObject = FileObject;
|
|
/*
|
|
* Set the lock before assigning the segment to the file object
|
|
*/
|
|
ExAcquireFastMutex(&Segment->Lock);
|
|
FileObject->SectionObjectPointer->DataSectionObject = (PVOID)Segment;
|
|
|
|
Segment->Image.FileOffset = 0;
|
|
Segment->Protection = SectionPageProtection;
|
|
Segment->Flags = MM_DATAFILE_SEGMENT;
|
|
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;
|
|
Segment->Locked = TRUE;
|
|
MiInitializeSectionPageTable(Segment);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* If the file is already mapped as a data file then we may need
|
|
* to extend it
|
|
*/
|
|
Segment =
|
|
(PMM_SECTION_SEGMENT)FileObject->SectionObjectPointer->
|
|
DataSectionObject;
|
|
Section->Segment = (PSEGMENT)Segment;
|
|
(void)InterlockedIncrementUL(&Segment->ReferenceCount);
|
|
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);
|
|
}
|
|
|
|
/* We let the segment reference the file object */
|
|
ObDereferenceObject(FileObject);
|
|
FileObject = Segment->FileObject;
|
|
}
|
|
MmUnlockSectionSegment(Segment);
|
|
Section->SizeOfSection = MaximumSize;
|
|
#ifndef NEWCC
|
|
CcRosReferenceCache(FileObject);
|
|
#endif
|
|
//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);
|
|
|
|
/* Flush data since we're about to perform a non-cached read */
|
|
CcFlushCache(FileObject->SectionObjectPointer,
|
|
&FileOffset,
|
|
BufferSize,
|
|
&Iosb);
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
UsedSize = 0;
|
|
|
|
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)
|
|
{
|
|
RtlZeroMemory(ImageSectionObject, sizeof(*ImageSectionObject));
|
|
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);
|
|
|
|
/*
|
|
* 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 = 1;
|
|
MiInitializeSectionPageTable(&ImageSectionObject->Segments[i]);
|
|
ImageSectionObject->Segments[i].FileObject = FileObject;
|
|
}
|
|
|
|
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_SECTION_SEGMENT SectionSegments;
|
|
PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
|
|
ULONG i;
|
|
|
|
if (FileObject == NULL)
|
|
return STATUS_INVALID_FILE_FOR_SECTION;
|
|
|
|
#ifndef NEWCC
|
|
if (!CcIsFileCached(FileObject))
|
|
{
|
|
DPRINT1("Denying section creation due to missing cache initialization\n");
|
|
return STATUS_INVALID_FILE_FOR_SECTION;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Create the section
|
|
*/
|
|
Status = ObCreateObject (ExGetPreviousMode(),
|
|
MmSectionObjectType,
|
|
ObjectAttributes,
|
|
ExGetPreviousMode(),
|
|
NULL,
|
|
sizeof(*Section),
|
|
0,
|
|
0,
|
|
(PVOID*)(PVOID)&Section);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ObDereferenceObject(FileObject);
|
|
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;
|
|
|
|
if (FileObject->SectionObjectPointer->ImageSectionObject == NULL)
|
|
{
|
|
NTSTATUS StatusExeFmt;
|
|
|
|
ImageSectionObject = ExAllocatePoolWithTag(PagedPool, sizeof(MM_IMAGE_SECTION_OBJECT), TAG_MM_SECTION_SEGMENT);
|
|
if (ImageSectionObject == NULL)
|
|
{
|
|
ObDereferenceObject(FileObject);
|
|
ObDereferenceObject(Section);
|
|
return(STATUS_NO_MEMORY);
|
|
}
|
|
|
|
RtlZeroMemory(ImageSectionObject, sizeof(MM_IMAGE_SECTION_OBJECT));
|
|
|
|
StatusExeFmt = ExeFmtpCreateImageSection(FileObject, ImageSectionObject);
|
|
|
|
if (!NT_SUCCESS(StatusExeFmt))
|
|
{
|
|
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);
|
|
ObDereferenceObject(FileObject);
|
|
return(Status);
|
|
}
|
|
|
|
Section->Segment = (PSEGMENT)ImageSectionObject;
|
|
ASSERT(ImageSectionObject->Segments);
|
|
|
|
/*
|
|
* Lock the file
|
|
*/
|
|
Status = MmspWaitForFileLock(FileObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ExFreePool(ImageSectionObject->Segments);
|
|
ExFreePool(ImageSectionObject);
|
|
ObDereferenceObject(Section);
|
|
ObDereferenceObject(FileObject);
|
|
return(Status);
|
|
}
|
|
|
|
if (NULL != InterlockedCompareExchangePointer(&FileObject->SectionObjectPointer->ImageSectionObject,
|
|
ImageSectionObject, NULL))
|
|
{
|
|
/*
|
|
* An other thread has initialized the same image in the background
|
|
*/
|
|
ExFreePool(ImageSectionObject->Segments);
|
|
ExFreePool(ImageSectionObject);
|
|
ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
|
|
Section->Segment = (PSEGMENT)ImageSectionObject;
|
|
SectionSegments = ImageSectionObject->Segments;
|
|
|
|
for (i = 0; i < ImageSectionObject->NrSegments; i++)
|
|
{
|
|
(void)InterlockedIncrementUL(&SectionSegments[i].ReferenceCount);
|
|
}
|
|
|
|
/* We let the Image Section Object hold the reference */
|
|
ObDereferenceObject(FileObject);
|
|
FileObject = ImageSectionObject->FileObject;
|
|
}
|
|
|
|
Status = StatusExeFmt;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Lock the file
|
|
*/
|
|
Status = MmspWaitForFileLock(FileObject);
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
ObDereferenceObject(Section);
|
|
ObDereferenceObject(FileObject);
|
|
return(Status);
|
|
}
|
|
|
|
ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
|
|
Section->Segment = (PSEGMENT)ImageSectionObject;
|
|
SectionSegments = ImageSectionObject->Segments;
|
|
|
|
/*
|
|
* Otherwise just reference all the section segments
|
|
*/
|
|
for (i = 0; i < ImageSectionObject->NrSegments; i++)
|
|
{
|
|
(void)InterlockedIncrementUL(&SectionSegments[i].ReferenceCount);
|
|
}
|
|
|
|
/* We let the Image Section Object hold the reference */
|
|
ObDereferenceObject(FileObject);
|
|
FileObject = ImageSectionObject->FileObject;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
#ifndef NEWCC
|
|
CcRosReferenceCache(FileObject);
|
|
#endif
|
|
//KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
|
|
*SectionObject = Section;
|
|
return(Status);
|
|
}
|
|
|
|
|
|
|
|
static NTSTATUS
|
|
MmMapViewOfSegment(PMMSUPPORT AddressSpace,
|
|
PSECTION Section,
|
|
PMM_SECTION_SEGMENT Segment,
|
|
PVOID* BaseAddress,
|
|
SIZE_T ViewSize,
|
|
ULONG Protect,
|
|
ULONG ViewOffset,
|
|
ULONG AllocationType)
|
|
{
|
|
PMEMORY_AREA MArea;
|
|
NTSTATUS Status;
|
|
ULONG Granularity;
|
|
|
|
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);
|
|
}
|
|
|
|
ObReferenceObject((PVOID)Section);
|
|
|
|
MArea->SectionData.Segment = Segment;
|
|
MArea->SectionData.Section = Section;
|
|
MArea->SectionData.ViewOffset.QuadPart = ViewOffset;
|
|
if (Section->u.Flags.Image)
|
|
{
|
|
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;
|
|
#ifndef NEWCC
|
|
PFILE_OBJECT FileObject;
|
|
PROS_SHARED_CACHE_MAP SharedCacheMap;
|
|
#endif
|
|
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.QuadPart;
|
|
|
|
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 mark it as dirty in the
|
|
* cache manager.
|
|
*/
|
|
if (Segment->Flags & MM_DATAFILE_SEGMENT)
|
|
{
|
|
if (Page == PFN_FROM_SSE(Entry) && Dirty)
|
|
{
|
|
#ifndef NEWCC
|
|
FileObject = MemoryArea->SectionData.Segment->FileObject;
|
|
SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
|
|
CcRosMarkDirtyFile(SharedCacheMap, Offset.QuadPart + Segment->Image.FileOffset);
|
|
#endif
|
|
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))
|
|
{
|
|
/*
|
|
* Just dereference private pages
|
|
*/
|
|
SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
|
|
if (SavedSwapEntry != 0)
|
|
{
|
|
MmFreeSwapPage(SavedSwapEntry);
|
|
MmSetSavedSwapEntryPage(Page, 0);
|
|
}
|
|
MmDeleteRmap(Page, Process, Address);
|
|
MmReleasePageMemoryConsumer(MC_USER, Page);
|
|
}
|
|
else
|
|
{
|
|
MmDeleteRmap(Page, Process, Address);
|
|
MmUnsharePageEntrySectionSegment(MemoryArea, Segment, &Offset, Dirty, FALSE, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
static NTSTATUS
|
|
MmUnmapViewOfSegment(PMMSUPPORT AddressSpace,
|
|
PVOID BaseAddress)
|
|
{
|
|
NTSTATUS Status;
|
|
PMEMORY_AREA MemoryArea;
|
|
PSECTION Section;
|
|
PMM_SECTION_SEGMENT Segment;
|
|
PLIST_ENTRY CurrentEntry;
|
|
PMM_REGION CurrentRegion;
|
|
PLIST_ENTRY RegionListHead;
|
|
|
|
MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace,
|
|
BaseAddress);
|
|
if (MemoryArea == NULL)
|
|
{
|
|
return(STATUS_UNSUCCESSFUL);
|
|
}
|
|
|
|
Section = MemoryArea->SectionData.Section;
|
|
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 (Section->u.Flags.PhysicalMemory)
|
|
{
|
|
Status = MmFreeMemoryArea(AddressSpace,
|
|
MemoryArea,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
Status = MmFreeMemoryArea(AddressSpace,
|
|
MemoryArea,
|
|
MmFreeSectionPage,
|
|
AddressSpace);
|
|
}
|
|
MmUnlockSectionSegment(Segment);
|
|
ObDereferenceObject(Section);
|
|
return(Status);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MiRosUnmapViewOfSection(IN PEPROCESS Process,
|
|
IN PVOID BaseAddress,
|
|
IN BOOLEAN SkipDebuggerNotify)
|
|
{
|
|
NTSTATUS Status;
|
|
PMEMORY_AREA MemoryArea;
|
|
PMMSUPPORT AddressSpace;
|
|
PSECTION Section;
|
|
PVOID ImageBaseAddress = 0;
|
|
|
|
DPRINT("Opening memory area Process %p BaseAddress %p\n",
|
|
Process, BaseAddress);
|
|
|
|
ASSERT(Process);
|
|
|
|
AddressSpace = Process ? &Process->Vm : MmGetKernelAddressSpace();
|
|
|
|
MmLockAddressSpace(AddressSpace);
|
|
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);
|
|
MmUnlockAddressSpace(AddressSpace);
|
|
return STATUS_NOT_MAPPED_VIEW;
|
|
}
|
|
|
|
Section = MemoryArea->SectionData.Section;
|
|
|
|
if ((Section != NULL) && Section->u.Flags.Image)
|
|
{
|
|
ULONG i;
|
|
ULONG NrSegments;
|
|
PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
|
|
PMM_SECTION_SEGMENT SectionSegments;
|
|
PMM_SECTION_SEGMENT Segment;
|
|
|
|
Segment = MemoryArea->SectionData.Segment;
|
|
ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->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));
|
|
}
|
|
}
|
|
|
|
MmUnlockAddressSpace(AddressSpace);
|
|
|
|
/* 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.Commit)
|
|
Sbi.Attributes |= SEC_COMMIT;
|
|
if (Section->u.Flags.Reserve)
|
|
Sbi.Attributes |= SEC_RESERVE;
|
|
if (Section->u.Flags.File)
|
|
Sbi.Attributes |= SEC_FILE;
|
|
if (Section->u.Flags.Image)
|
|
Sbi.Attributes |= SEC_IMAGE;
|
|
|
|
/* FIXME : Complete/test the list of flags passed back from NtCreateSection */
|
|
|
|
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)
|
|
*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;
|
|
ULONG ViewOffset;
|
|
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;
|
|
|
|
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,
|
|
Section,
|
|
&SectionSegments[i],
|
|
&SBaseAddress,
|
|
SectionSegments[i].Length.LowPart,
|
|
SectionSegments[i].Protection,
|
|
0,
|
|
0);
|
|
MmUnlockSectionSegment(&SectionSegments[i]);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
MmUnlockAddressSpace(AddressSpace);
|
|
return(Status);
|
|
}
|
|
}
|
|
|
|
*BaseAddress = (PVOID)ImageBase;
|
|
*ViewSize = ImageSize;
|
|
}
|
|
else
|
|
{
|
|
PMM_SECTION_SEGMENT Segment = (PMM_SECTION_SEGMENT)Section->Segment;
|
|
|
|
/* 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->u.LowPart;
|
|
}
|
|
|
|
if ((ViewOffset % PAGE_SIZE) != 0)
|
|
{
|
|
MmUnlockAddressSpace(AddressSpace);
|
|
return(STATUS_MAPPED_ALIGNMENT);
|
|
}
|
|
|
|
if ((*ViewSize) == 0)
|
|
{
|
|
(*ViewSize) = Section->SizeOfSection.u.LowPart - ViewOffset;
|
|
}
|
|
else if (((*ViewSize)+ViewOffset) > Section->SizeOfSection.u.LowPart)
|
|
{
|
|
(*ViewSize) = Section->SizeOfSection.u.LowPart - ViewOffset;
|
|
}
|
|
|
|
*ViewSize = PAGE_ROUND_UP(*ViewSize);
|
|
|
|
MmLockSectionSegment(Segment);
|
|
Status = MmMapViewOfSegment(AddressSpace,
|
|
Section,
|
|
Segment,
|
|
BaseAddress,
|
|
*ViewSize,
|
|
Protect,
|
|
ViewOffset,
|
|
AllocationType & (MEM_TOP_DOWN|SEC_NO_CHANGE));
|
|
MmUnlockSectionSegment(Segment);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
MmUnlockAddressSpace(AddressSpace);
|
|
return(Status);
|
|
}
|
|
}
|
|
|
|
MmUnlockAddressSpace(AddressSpace);
|
|
ASSERT(*BaseAddress == ALIGN_DOWN_POINTER_BY(*BaseAddress, MM_VIRTMEM_GRANULARITY));
|
|
|
|
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)
|
|
{
|
|
/* 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;
|
|
}
|
|
|
|
if (SectionObjectPointer->DataSectionObject != NULL)
|
|
{
|
|
PMM_SECTION_SEGMENT Segment;
|
|
|
|
Segment = (PMM_SECTION_SEGMENT)SectionObjectPointer->
|
|
DataSectionObject;
|
|
|
|
if (Segment->ReferenceCount != 0)
|
|
{
|
|
#ifdef NEWCC
|
|
CC_FILE_SIZES FileSizes;
|
|
CcpLock();
|
|
if (SectionObjectPointer->SharedCacheMap && (Segment->ReferenceCount > CcpCountCacheSections((PNOCC_CACHE_MAP)SectionObjectPointer->SharedCacheMap)))
|
|
{
|
|
CcpUnlock();
|
|
/* Check size of file */
|
|
if (SectionObjectPointer->SharedCacheMap)
|
|
{
|
|
if (!CcGetFileSizes(Segment->FileObject, &FileSizes))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (NewFileSize->QuadPart <= FileSizes.FileSize.QuadPart)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
CcpUnlock();
|
|
#else
|
|
/* Check size of file */
|
|
if (SectionObjectPointer->SharedCacheMap)
|
|
{
|
|
PROS_SHARED_CACHE_MAP SharedCacheMap = SectionObjectPointer->SharedCacheMap;
|
|
if (NewFileSize->QuadPart <= SharedCacheMap->FileSize.QuadPart)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
/* Something must gone wrong
|
|
* how can we have a Section but no
|
|
* reference? */
|
|
DPRINT("ERROR: DataSectionObject without reference!\n");
|
|
}
|
|
}
|
|
|
|
DPRINT("FIXME: didn't check for outstanding write probes\n");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOLEAN NTAPI
|
|
MmFlushImageSection (IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
|
|
IN MMFLUSH_TYPE FlushType)
|
|
{
|
|
BOOLEAN Result = TRUE;
|
|
#ifdef NEWCC
|
|
PMM_SECTION_SEGMENT Segment;
|
|
#endif
|
|
|
|
switch(FlushType)
|
|
{
|
|
case MmFlushForDelete:
|
|
if (SectionObjectPointer->ImageSectionObject ||
|
|
SectionObjectPointer->DataSectionObject)
|
|
{
|
|
return FALSE;
|
|
}
|
|
#ifndef NEWCC
|
|
CcRosRemoveIfClosed(SectionObjectPointer);
|
|
#endif
|
|
return TRUE;
|
|
case MmFlushForWrite:
|
|
{
|
|
DPRINT("MmFlushImageSection(%d)\n", FlushType);
|
|
#ifdef NEWCC
|
|
Segment = (PMM_SECTION_SEGMENT)SectionObjectPointer->DataSectionObject;
|
|
#endif
|
|
|
|
if (SectionObjectPointer->ImageSectionObject)
|
|
{
|
|
DPRINT1("SectionObject has ImageSection\n");
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef NEWCC
|
|
CcpLock();
|
|
Result = !SectionObjectPointer->SharedCacheMap || (Segment->ReferenceCount == CcpCountCacheSections((PNOCC_CACHE_MAP)SectionObjectPointer->SharedCacheMap));
|
|
CcpUnlock();
|
|
DPRINT("Result %d\n", Result);
|
|
#endif
|
|
return Result;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS NTAPI
|
|
MmMapViewInSystemSpace (IN PVOID SectionObject,
|
|
OUT PVOID * MappedBase,
|
|
IN OUT PSIZE_T ViewSize)
|
|
{
|
|
PSECTION Section;
|
|
PMM_SECTION_SEGMENT Segment;
|
|
PMMSUPPORT AddressSpace;
|
|
NTSTATUS Status;
|
|
PAGED_CODE();
|
|
|
|
if (MiIsRosSectionObject(SectionObject) == FALSE)
|
|
{
|
|
return MiMapViewInSystemSpace(SectionObject,
|
|
&MmSession,
|
|
MappedBase,
|
|
ViewSize);
|
|
}
|
|
|
|
DPRINT("MmMapViewInSystemSpace() called\n");
|
|
|
|
Section = SectionObject;
|
|
Segment = (PMM_SECTION_SEGMENT)Section->Segment;
|
|
|
|
AddressSpace = MmGetKernelAddressSpace();
|
|
|
|
MmLockAddressSpace(AddressSpace);
|
|
|
|
|
|
if ((*ViewSize) == 0)
|
|
{
|
|
(*ViewSize) = Section->SizeOfSection.u.LowPart;
|
|
}
|
|
else if ((*ViewSize) > Section->SizeOfSection.u.LowPart)
|
|
{
|
|
(*ViewSize) = Section->SizeOfSection.u.LowPart;
|
|
}
|
|
|
|
MmLockSectionSegment(Segment);
|
|
|
|
|
|
Status = MmMapViewOfSegment(AddressSpace,
|
|
Section,
|
|
Segment,
|
|
MappedBase,
|
|
*ViewSize,
|
|
PAGE_READWRITE,
|
|
0,
|
|
0);
|
|
|
|
MmUnlockSectionSegment(Segment);
|
|
MmUnlockAddressSpace(AddressSpace);
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MiRosUnmapViewInSystemSpace(IN PVOID MappedBase)
|
|
{
|
|
PMMSUPPORT AddressSpace;
|
|
NTSTATUS Status;
|
|
|
|
DPRINT("MmUnmapViewInSystemSpace() called\n");
|
|
|
|
AddressSpace = MmGetKernelAddressSpace();
|
|
|
|
MmLockAddressSpace(AddressSpace);
|
|
|
|
Status = MmUnmapViewOfSegment(AddressSpace, MappedBase);
|
|
|
|
MmUnlockAddressSpace(AddressSpace);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**********************************************************************
|
|
* 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;
|
|
|
|
/* 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 an object? */
|
|
if (FileObject)
|
|
{
|
|
/* Reference the object directly */
|
|
ObReferenceObject(FileObject);
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
#ifndef NEWCC // A hack for initializing caching.
|
|
// This is needed only in the old case.
|
|
if (FileHandle)
|
|
{
|
|
IO_STATUS_BLOCK Iosb;
|
|
NTSTATUS Status;
|
|
CHAR Buffer;
|
|
LARGE_INTEGER ByteOffset;
|
|
ByteOffset.QuadPart = 0;
|
|
Status = ZwReadFile(FileHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&Iosb,
|
|
&Buffer,
|
|
sizeof(Buffer),
|
|
&ByteOffset,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status) && Status != STATUS_END_OF_FILE)
|
|
{
|
|
DPRINT1("CC failure: %lx\n", Status);
|
|
if (FileObject)
|
|
ObDereferenceObject(FileObject);
|
|
return Status;
|
|
}
|
|
// Caching is initialized...
|
|
|
|
// Hack of the hack: actually, it might not be initialized if FSD init on effective right and if file is null-size
|
|
// In such case, force cache by initiating a write IRP
|
|
if (Status == STATUS_END_OF_FILE && !(AllocationAttributes & SEC_IMAGE) && FileObject != NULL &&
|
|
(FileObject->SectionObjectPointer == NULL || FileObject->SectionObjectPointer->SharedCacheMap == NULL))
|
|
{
|
|
Buffer = 0xdb;
|
|
Status = ZwWriteFile(FileHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&Iosb,
|
|
&Buffer,
|
|
sizeof(Buffer),
|
|
&ByteOffset,
|
|
NULL);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
LARGE_INTEGER Zero;
|
|
Zero.QuadPart = 0LL;
|
|
|
|
Status = IoSetInformation(FileObject,
|
|
FileEndOfFileInformation,
|
|
sizeof(LARGE_INTEGER),
|
|
&Zero);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (AllocationAttributes & SEC_IMAGE)
|
|
{
|
|
Status = MmCreateImageSection(SectionObject,
|
|
DesiredAccess,
|
|
ObjectAttributes,
|
|
MaximumSize,
|
|
SectionPageProtection,
|
|
AllocationAttributes,
|
|
FileObject);
|
|
}
|
|
#ifndef NEWCC
|
|
else if (FileHandle != NULL)
|
|
{
|
|
Status = MmCreateDataFileSection(SectionObject,
|
|
DesiredAccess,
|
|
ObjectAttributes,
|
|
MaximumSize,
|
|
SectionPageProtection,
|
|
AllocationAttributes,
|
|
FileObject);
|
|
}
|
|
#else
|
|
else if (FileHandle != NULL || FileObject != NULL)
|
|
{
|
|
Status = MmCreateCacheSection(SectionObject,
|
|
DesiredAccess,
|
|
ObjectAttributes,
|
|
SizeOfSection,
|
|
InitialPageProtection,
|
|
AllocationAttributes,
|
|
FileObject);
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
/* All cases should be handled above, and the Physical Memorw section was created at initialization phase */
|
|
ASSERT(FALSE);
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
if (FileObject)
|
|
ObDereferenceObject(FileObject);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/* EOF */
|