mirror of
https://github.com/reactos/reactos.git
synced 2025-02-24 01:15:09 +00:00

The main motivation to switch to that newer driver is, that our old one simply can not read all isos. Especially complex ones made trouble and were only shown as empty in explorer. It is still possible to build and use the old driver when needed, only thing that needs to be done for that is to revert 0.4.8-dev-164-gec6b3ecbe4
Porting back the state up to 0.4.8-release-100-g8f947b5 implies: Fixing the following JIRA-IDs (or avoid introducing them): CORE-18029 "Mute noisy DPRINT 'SectionObject has ImageSection'" CORE-17405 "Fix a macro-copy-paste and shrink the binary size" CORE-15659 "Unable to build the gcc Release version in Windows using RosBE 2.1.6 (module cdfs fails)" CORE-14315 "CDFS_NEW assertion during first stage setup due to new CcPerformReadAhead" CORE-14128 "Avast! Free Antivirus 7.0 hangs the system when trying to detect a newly created virus" CORE-14067 "CDFS_NEW assertions and exceptions" CORE-14003 "Shutting down LiveCD asserts since introduction of MS PL CDFS_NEW" CORE-13184 "Restore ability to install from disk-image" by picking the following commits: 0.4.8-release-100-g8f947b5322
[NTOSKRNL] Mute noisy DPRINT 'SectionObject has ImageSection' CORE-18029 0.4.8-release-80-geb1ea19588
[CDFS_NEW] == 0.4.15-dev-1456-g889eab7
CORE-17405 0.4.8-release-62-g8c07aad4a8
[CDFS_NEW/XDK] == 0.4.11-dev-39-ga2f9762
+ 0.4.11-dev-40-g6d7ec8c
CORE-14067 0.4.8-release-3-g5d976d04e8
[CDFS_NEW] == 0.4.12-dev-431-gbccad87f3c
+ 0.4.12-dev-432-g3463b2db9f
CORE-15659 0.4.8-RC-3-g51f9494d48
[CDFS_NEW] superseded later by the proper fix 0.4.8-release-62-g8c07aad4a8
CORE-14067 0.4.8-dev-1069-ga5e89014dc
[CDFS_NEW] CORE-14315 0.4.8-dev-475-ga59d4674de
[NTOSKRNL] io/iomgr/device.c (forgotten assert) CORE-14128 0.4.8-dev-221-g9d67a24799
[CDFS_NEW] 0.4.8-dev-220-g67a7e45e35
[CDFS_NEW/DOC] 0.4.8-dev-219-g6a3bbf24e0
[CDFS_NEW] 0.4.8-dev-218-gec26cde4a1
[CDFS_NEW] 0.4.8-dev-217-gbc2378a356
[CDFS_NEW] 0.4.8-dev-216-g5429771b99
[CDFS_NEW] 0.4.8-dev-215-gfd34548263
[CDFS_NEW] Sync with MS-PL driver 0.4.8-dev-164-gec6b3ecbe4
[FILESYSTEMS] switch from CDFS to CDFS_NEW in CMakeLists.txt 0.4.8-dev-160-g2b217e4ecf
[NTOSKRNL] Mute spam CcSetReadAheadGranularity() 0.4.8-dev-159-g64cb138a67
[NTOSKRNL] Mute spam CcPurgeCacheSection() 0.4.8-dev-150-gf723d230a0
[CDFS_NEW] 0.4.8-dev-133-gfaee3753ea
[CDFS_NEW] CORE-14003 0.4.8-dev-132-g1d777ffab5
[NTOSKRNL] iofunc.c CORE-14003 0.4.8-dev-131-gc3d5a3f2bd
[NTOSKRNL] iofunc.c CORE-14003 0.4.8-dev-130-g3b64f7f8fb
[NTOSKRNL] ob/obref.c & co CORE-14003 0.4.8-dev-129-g7eefe70294
[NTOSKRNL] io/iomgr.c & co CORE-14003 0.4.8-dev-127-g5f255827d3
[CDFS_NEW] 0.4.8-dev-126-g1bef48796e
[NTOSKRNL] just a comment, superseded later 0.4.8-dev-125-gcbf0430b56
[CDFS_NEW] 0.4.8-dev-123-gf88fe43abd
[NTOSKRNL] io/iomgr/device.c (forbidden DPRINT) 0.4.8-dev-122-g6c73385625
[CDFS_NEW] CORE-13184 0.4.8-dev-97-g94298313c0
[CDFS_NEW] 0.4.8-dev-95-ge88eeb21af
[CDFS_NEW/NTOSKRNL] CcWaitForCurrentLazyWriterActivity() stub return Success 0.4.8-dev-94-g03d5be6437
[CDFS_NEW] 0.4.8-dev-93-gfa1c60db50
[CDFS_NEW] 0.4.8-dev-92-g8b2fd60829
[CDFS_NEW] 0.4.8-dev-91-ge4da7ecc50
[CDFS_NEW] 0.4.8-dev-90-g7b19676e2b
[CDFS_NEW] 0.4.8-dev-89-g3d4b8783fd
[CDFS_NEW] 0.4.8-dev-88-g818025ecc8
[CDFS_NEW] 0.4.8-dev-87-g2639dd6736
[CDFS_NEW] 0.4.8-dev-86-g755bdb5d0b
[CDFS_NEW] 0.4.8-dev-85-g3cbcb1bade
[CDFS_NEW] and mute spam in opcode INSTEAD of picking: 0.4.8-dev-165-g2284a457a3
[NTOSKRNL] oplock.c Fixup 0.4.8-dev-163-gd3d5853956
[NTOSKRNL] oplock.c Implement oplock-support 0.4.12-dev-232-gf488102c86
[CDFS] was also left out for now I am aware, that the backport introduces white-space-glitches within CDFS_NEW. I decided to live with them in favor of better sync to master and newer releases.
5172 lines
168 KiB
C
5172 lines
168 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>
|
|
|
|
#if defined (ALLOC_PRAGMA)
|
|
#pragma alloc_text(INIT, MmCreatePhysicalMemorySection)
|
|
#pragma alloc_text(INIT, MmInitSectionImplementation)
|
|
#endif
|
|
|
|
#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;
|
|
|
|
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
|
|
{
|
|
PROS_SECTION_OBJECT Section;
|
|
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;
|
|
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_HDR32_MAGIC:
|
|
#ifdef _WIN64
|
|
case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
|
|
#endif // _WIN64
|
|
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 = IMAGE_SCN_CNT_INITIALIZED_DATA;
|
|
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));
|
|
|
|
/* conversion */
|
|
pssSegments[i].Image.FileOffset = pishSectionHeaders[i].PointerToRawData;
|
|
pssSegments[i].RawLength.QuadPart = pishSectionHeaders[i].SizeOfRawData;
|
|
}
|
|
else
|
|
{
|
|
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 || pishSectionHeaders[i].Misc.VirtualSize < pishSectionHeaders[i].SizeOfRawData)
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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(PROS_SECTION_OBJECT Section,
|
|
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 = Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
|
|
#endif
|
|
|
|
Page = PFN_FROM_SSE(Entry);
|
|
FileObject = Section->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->Flags & MM_PAGEFILE_SEGMENT) ||
|
|
(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->Flags & MM_PAGEFILE_SEGMENT) ||
|
|
(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->Data.SectionData.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
|
|
{
|
|
PROS_SHARED_CACHE_MAP SharedCacheMap;
|
|
PROS_VACB Vacb;
|
|
SharedCacheMap = MemoryArea->Data.SectionData.Section->FileObject->SectionObjectPointer->SharedCacheMap;
|
|
Vacb = CcRosLookupVacb(SharedCacheMap, SegOffset + MemoryArea->Data.SectionData.Segment->Image.FileOffset);
|
|
if (Vacb)
|
|
{
|
|
CcRosReleaseVacb(SharedCacheMap, Vacb, Vacb->Valid, FALSE, TRUE);
|
|
return TRUE;
|
|
}
|
|
}
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MiCopyFromUserPage(PFN_NUMBER DestPage, PFN_NUMBER SrcPage)
|
|
{
|
|
PEPROCESS Process;
|
|
KIRQL Irql, Irql2;
|
|
PVOID DestAddress, SrcAddress;
|
|
|
|
Process = PsGetCurrentProcess();
|
|
DestAddress = MiMapPageInHyperSpace(Process, DestPage, &Irql);
|
|
SrcAddress = MiMapPageInHyperSpace(Process, SrcPage, &Irql2);
|
|
if (DestAddress == NULL || SrcAddress == 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, SrcAddress, Irql2);
|
|
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->Data.SectionData.Section->FileObject;
|
|
SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
|
|
RawLength = MemoryArea->Data.SectionData.Segment->RawLength.QuadPart;
|
|
FileOffset = SegOffset + MemoryArea->Data.SectionData.Segment->Image.FileOffset;
|
|
IsImageSection = MemoryArea->Data.SectionData.Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
|
|
|
|
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->Data.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->Data.SectionData.Section->FileObject;
|
|
Resources.FileOffset.QuadPart = SegOffset +
|
|
MemoryArea->Data.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->Data.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->Data.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 (Segment->Image.Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ||
|
|
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;
|
|
PROS_SECTION_OBJECT 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->Data.SectionData.ViewOffset.QuadPart;
|
|
|
|
Segment = MemoryArea->Data.SectionData.Segment;
|
|
Section = MemoryArea->Data.SectionData.Section;
|
|
Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
|
|
&MemoryArea->Data.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->Data.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) || (Segment->Image.Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA))
|
|
{
|
|
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
|
|
*/
|
|
if (Segment->Flags & MM_PAGEFILE_SEGMENT)
|
|
{
|
|
DPRINT1("Found a swaped out private page in a pagefile section.\n");
|
|
KeBugCheck(MEMORY_MANAGEMENT);
|
|
}
|
|
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->AllocationAttributes & SEC_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 ((Segment->Flags & MM_PAGEFILE_SEGMENT) ||
|
|
((Offset.QuadPart >= (LONGLONG)PAGE_ROUND_UP(Segment->RawLength.QuadPart) &&
|
|
(Section->AllocationAttributes & SEC_IMAGE))))
|
|
{
|
|
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;
|
|
PROS_SECTION_OBJECT Section;
|
|
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->Data.SectionData.ViewOffset.QuadPart;
|
|
|
|
Segment = MemoryArea->Data.SectionData.Segment;
|
|
Section = MemoryArea->Data.SectionData.Section;
|
|
Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
|
|
&MemoryArea->Data.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
|
|
*/
|
|
MiCopyFromUserPage(NewPage, OldPage);
|
|
|
|
/*
|
|
* Unshare the old page.
|
|
*/
|
|
DPRINT("Swapping page (Old %x New %x)\n", OldPage, NewPage);
|
|
MmDeleteVirtualMapping(Process, PAddress, NULL, NULL);
|
|
MmDeleteRmap(OldPage, Process, PAddress);
|
|
MmUnsharePageEntrySectionSegment(Section, 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((PROS_SECTION_OBJECT)PageOutContext->Section,
|
|
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->Data.SectionData.Segment;
|
|
Context.Section = MemoryArea->Data.SectionData.Section;
|
|
Context.SectionEntry = Entry;
|
|
Context.CallingProcess = Process;
|
|
|
|
Context.Offset.QuadPart = (ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea)
|
|
+ MemoryArea->Data.SectionData.ViewOffset.QuadPart;
|
|
|
|
DirectMapped = FALSE;
|
|
|
|
MmLockSectionSegment(Context.Segment);
|
|
|
|
#ifndef NEWCC
|
|
FileOffset = Context.Offset.QuadPart + Context.Segment->Image.FileOffset;
|
|
IsImageSection = Context.Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
|
|
FileObject = Context.Section->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
|
|
|
|
|
|
/*
|
|
* This should never happen since mappings of physical memory are never
|
|
* placed in the rmap lists.
|
|
*/
|
|
if (Context.Section->AllocationAttributes & SEC_PHYSICALMEMORY)
|
|
{
|
|
DPRINT1("Trying to page out from physical memory section address 0x%p "
|
|
"process %p\n", Address,
|
|
Process ? Process->UniqueProcessId : 0);
|
|
KeBugCheck(MEMORY_MANAGEMENT);
|
|
}
|
|
|
|
/*
|
|
* 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 (Context.Segment->Image.Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ||
|
|
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 = KeAcquireQueuedSpinLock(LockQueuePfnLock);
|
|
MmReferencePage(Page);
|
|
KeReleaseQueuedSpinLock(LockQueuePfnLock, 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->Flags & MM_PAGEFILE_SEGMENT) &&
|
|
!(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->Flags & MM_PAGEFILE_SEGMENT)
|
|
{
|
|
if (Context.Private)
|
|
{
|
|
DPRINT1("Found a %s private page (address %p) in a pagefile segment.\n",
|
|
Context.WasDirty ? "dirty" : "clean", Address);
|
|
KeBugCheckEx(MEMORY_MANAGEMENT, SwapEntry, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
|
|
}
|
|
if (!Context.WasDirty && SwapEntry != 0)
|
|
{
|
|
MmSetSavedSwapEntryPage(Page, 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.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,
|
|
MemoryArea->Protect,
|
|
&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,
|
|
MemoryArea->Protect,
|
|
&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,
|
|
MemoryArea->Protect,
|
|
&Page,
|
|
1);
|
|
MmSetDirtyPage(Process, Address);
|
|
MmInsertRmap(Page,
|
|
Process,
|
|
Address);
|
|
}
|
|
else
|
|
{
|
|
MmLockSectionSegment(Context.Segment);
|
|
Status = MmCreateVirtualMapping(Process,
|
|
Address,
|
|
MemoryArea->Protect,
|
|
&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->Flags & MM_PAGEFILE_SEGMENT ||
|
|
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;
|
|
PROS_SECTION_OBJECT Section;
|
|
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->Data.SectionData.ViewOffset.QuadPart;
|
|
|
|
/*
|
|
* Get the segment and section.
|
|
*/
|
|
Segment = MemoryArea->Data.SectionData.Segment;
|
|
Section = MemoryArea->Data.SectionData.Section;
|
|
IsImageSection = Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
|
|
|
|
FileObject = Section->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;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This should never happen since mappings of physical memory are never
|
|
* placed in the rmap lists.
|
|
*/
|
|
if (Section->AllocationAttributes & SEC_PHYSICALMEMORY)
|
|
{
|
|
DPRINT1("Trying to write back page from physical memory mapped at %p "
|
|
"process %p\n", Address,
|
|
Process ? Process->UniqueProcessId : 0);
|
|
KeBugCheck(MEMORY_MANAGEMENT);
|
|
}
|
|
|
|
/*
|
|
* 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 (Segment->Image.Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ||
|
|
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
|
|
CcRosMarkDirtyVacb(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->Data.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->Data.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;
|
|
PROS_SECTION_OBJECT Section;
|
|
PMM_SECTION_SEGMENT Segment;
|
|
|
|
Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
|
|
&MemoryArea->Data.SectionData.RegionListHead,
|
|
Address, &RegionBaseAddress);
|
|
if (Region == NULL)
|
|
{
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
Section = MemoryArea->Data.SectionData.Section;
|
|
if (Section->AllocationAttributes & SEC_IMAGE)
|
|
{
|
|
Segment = MemoryArea->Data.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 = MemoryArea->Protect;
|
|
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)
|
|
{
|
|
PROS_SECTION_OBJECT Section = (PROS_SECTION_OBJECT)ObjectBody;
|
|
|
|
/* Check if it's an ARM3, or ReactOS section */
|
|
if (!MiIsRosSectionObject(Section))
|
|
{
|
|
MiDeleteARM3Section(ObjectBody);
|
|
return;
|
|
}
|
|
|
|
DPRINT("MmpDeleteSection(ObjectBody %p)\n", ObjectBody);
|
|
if (Section->AllocationAttributes & SEC_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->ImageSection == NULL)
|
|
return;
|
|
|
|
SectionSegments = Section->ImageSection->Segments;
|
|
NrSegments = Section->ImageSection->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;
|
|
|
|
if (Section->Segment->Flags & MM_PAGEFILE_SEGMENT)
|
|
{
|
|
MmpFreePageFileSegment(Section->Segment);
|
|
MmFreePageTablesSectionSegment(Section->Segment, NULL);
|
|
ExFreePool(Section->Segment);
|
|
Section->Segment = NULL;
|
|
}
|
|
else
|
|
{
|
|
(void)InterlockedDecrementUL(&Section->Segment->ReferenceCount);
|
|
}
|
|
}
|
|
if (Section->FileObject != NULL)
|
|
{
|
|
#ifndef NEWCC
|
|
CcRosDereferenceCache(Section->FileObject);
|
|
#endif
|
|
ObDereferenceObject(Section->FileObject);
|
|
Section->FileObject = NULL;
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
NTSTATUS
|
|
INIT_FUNCTION
|
|
NTAPI
|
|
MmCreatePhysicalMemorySection(VOID)
|
|
{
|
|
PROS_SECTION_OBJECT PhysSection;
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES Obj;
|
|
UNICODE_STRING Name = RTL_CONSTANT_STRING(L"\\Device\\PhysicalMemory");
|
|
LARGE_INTEGER SectionSize;
|
|
HANDLE Handle;
|
|
|
|
/*
|
|
* Create the section mapping physical memory
|
|
*/
|
|
SectionSize.QuadPart = 0xFFFFFFFF;
|
|
InitializeObjectAttributes(&Obj,
|
|
&Name,
|
|
OBJ_PERMANENT | OBJ_KERNEL_EXCLUSIVE,
|
|
NULL,
|
|
NULL);
|
|
Status = MmCreateSection((PVOID)&PhysSection,
|
|
SECTION_ALL_ACCESS,
|
|
&Obj,
|
|
&SectionSize,
|
|
PAGE_EXECUTE_READWRITE,
|
|
SEC_PHYSICALMEMORY,
|
|
NULL,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to create PhysicalMemory section\n");
|
|
KeBugCheck(MEMORY_MANAGEMENT);
|
|
}
|
|
Status = ObInsertObject(PhysSection,
|
|
NULL,
|
|
SECTION_ALL_ACCESS,
|
|
0,
|
|
NULL,
|
|
&Handle);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ObDereferenceObject(PhysSection);
|
|
}
|
|
ObCloseHandle(Handle, KernelMode);
|
|
PhysSection->AllocationAttributes |= SEC_PHYSICALMEMORY;
|
|
PhysSection->Segment->Flags &= ~MM_PAGEFILE_SEGMENT;
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
NTSTATUS
|
|
INIT_FUNCTION
|
|
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(ROS_SECTION_OBJECT);
|
|
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
|
|
MmCreatePageFileSection(PROS_SECTION_OBJECT *SectionObject,
|
|
ACCESS_MASK DesiredAccess,
|
|
POBJECT_ATTRIBUTES ObjectAttributes,
|
|
PLARGE_INTEGER UMaximumSize,
|
|
ULONG SectionPageProtection,
|
|
ULONG AllocationAttributes)
|
|
/*
|
|
* Create a section which is backed by the pagefile
|
|
*/
|
|
{
|
|
LARGE_INTEGER MaximumSize;
|
|
PROS_SECTION_OBJECT Section;
|
|
PMM_SECTION_SEGMENT Segment;
|
|
NTSTATUS Status;
|
|
|
|
if (UMaximumSize == NULL)
|
|
{
|
|
DPRINT1("MmCreatePageFileSection: (UMaximumSize == NULL)\n");
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
MaximumSize = *UMaximumSize;
|
|
|
|
/*
|
|
* Create the section
|
|
*/
|
|
Status = ObCreateObject(ExGetPreviousMode(),
|
|
MmSectionObjectType,
|
|
ObjectAttributes,
|
|
ExGetPreviousMode(),
|
|
NULL,
|
|
sizeof(ROS_SECTION_OBJECT),
|
|
0,
|
|
0,
|
|
(PVOID*)(PVOID)&Section);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("MmCreatePageFileSection: failed to create object (0x%lx)\n", Status);
|
|
return(Status);
|
|
}
|
|
|
|
/*
|
|
* Initialize it
|
|
*/
|
|
RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT));
|
|
Section->Type = 'SC';
|
|
Section->Size = 'TN';
|
|
Section->SectionPageProtection = SectionPageProtection;
|
|
Section->AllocationAttributes = AllocationAttributes;
|
|
Section->MaximumSize = MaximumSize;
|
|
Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
|
|
TAG_MM_SECTION_SEGMENT);
|
|
if (Segment == NULL)
|
|
{
|
|
ObDereferenceObject(Section);
|
|
return(STATUS_NO_MEMORY);
|
|
}
|
|
RtlZeroMemory(Segment, sizeof(MM_SECTION_SEGMENT));
|
|
Section->Segment = Segment;
|
|
Segment->ReferenceCount = 1;
|
|
ExInitializeFastMutex(&Segment->Lock);
|
|
Segment->Image.FileOffset = 0;
|
|
Segment->Protection = SectionPageProtection;
|
|
Segment->RawLength.QuadPart = MaximumSize.u.LowPart;
|
|
Segment->Length.QuadPart = PAGE_ROUND_UP(MaximumSize.u.LowPart);
|
|
Segment->Flags = MM_PAGEFILE_SEGMENT;
|
|
Segment->WriteCopy = FALSE;
|
|
Segment->Image.VirtualAddress = 0;
|
|
Segment->Image.Characteristics = 0;
|
|
*SectionObject = Section;
|
|
MiInitializeSectionPageTable(Segment);
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MmCreateDataFileSection(PROS_SECTION_OBJECT *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
|
|
*/
|
|
{
|
|
PROS_SECTION_OBJECT 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(ROS_SECTION_OBJECT),
|
|
0,
|
|
0,
|
|
(PVOID*)&Section);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ObDereferenceObject(FileObject);
|
|
return(Status);
|
|
}
|
|
/*
|
|
* Initialize it
|
|
*/
|
|
RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT));
|
|
Section->Type = 'SC';
|
|
Section->Size = 'TN';
|
|
Section->SectionPageProtection = SectionPageProtection;
|
|
Section->AllocationAttributes = AllocationAttributes;
|
|
|
|
/*
|
|
* 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 = Segment;
|
|
Segment->ReferenceCount = 1;
|
|
ExInitializeFastMutex(&Segment->Lock);
|
|
/*
|
|
* 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 = 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);
|
|
}
|
|
}
|
|
MmUnlockSectionSegment(Segment);
|
|
Section->FileObject = FileObject;
|
|
Section->MaximumSize = 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;
|
|
|
|
return
|
|
(Segment1->Image.VirtualAddress - Segment2->Image.VirtualAddress) >>
|
|
((sizeof(ULONG_PTR) - sizeof(int)) * 8);
|
|
}
|
|
|
|
/*
|
|
* 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]);
|
|
}
|
|
|
|
ASSERT(NT_SUCCESS(Status));
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MmCreateImageSection(PROS_SECTION_OBJECT *SectionObject,
|
|
ACCESS_MASK DesiredAccess,
|
|
POBJECT_ATTRIBUTES ObjectAttributes,
|
|
PLARGE_INTEGER UMaximumSize,
|
|
ULONG SectionPageProtection,
|
|
ULONG AllocationAttributes,
|
|
PFILE_OBJECT FileObject)
|
|
{
|
|
PROS_SECTION_OBJECT 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 (FileObject->SectionObjectPointer->SharedCacheMap == NULL)
|
|
{
|
|
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(ROS_SECTION_OBJECT),
|
|
0,
|
|
0,
|
|
(PVOID*)(PVOID)&Section);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ObDereferenceObject(FileObject);
|
|
return(Status);
|
|
}
|
|
|
|
/*
|
|
* Initialize it
|
|
*/
|
|
RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT));
|
|
Section->Type = 'SC';
|
|
Section->Size = 'TN';
|
|
Section->SectionPageProtection = SectionPageProtection;
|
|
Section->AllocationAttributes = AllocationAttributes;
|
|
|
|
#ifndef NEWCC
|
|
/*
|
|
* Initialized caching for this file object if previously caching
|
|
* was initialized for the same on disk file
|
|
*/
|
|
Status = CcTryToInitializeFileCache(FileObject);
|
|
#else
|
|
Status = STATUS_SUCCESS;
|
|
#endif
|
|
|
|
if (!NT_SUCCESS(Status) || 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->ImageSection = 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->ImageSection = ImageSectionObject;
|
|
SectionSegments = ImageSectionObject->Segments;
|
|
|
|
for (i = 0; i < ImageSectionObject->NrSegments; i++)
|
|
{
|
|
(void)InterlockedIncrementUL(&SectionSegments[i].ReferenceCount);
|
|
}
|
|
}
|
|
|
|
Status = StatusExeFmt;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Lock the file
|
|
*/
|
|
Status = MmspWaitForFileLock(FileObject);
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
ObDereferenceObject(Section);
|
|
ObDereferenceObject(FileObject);
|
|
return(Status);
|
|
}
|
|
|
|
ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
|
|
Section->ImageSection = ImageSectionObject;
|
|
SectionSegments = ImageSectionObject->Segments;
|
|
|
|
/*
|
|
* Otherwise just reference all the section segments
|
|
*/
|
|
for (i = 0; i < ImageSectionObject->NrSegments; i++)
|
|
{
|
|
(void)InterlockedIncrementUL(&SectionSegments[i].ReferenceCount);
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
Section->FileObject = FileObject;
|
|
#ifndef NEWCC
|
|
CcRosReferenceCache(FileObject);
|
|
#endif
|
|
//KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
|
|
*SectionObject = Section;
|
|
return(Status);
|
|
}
|
|
|
|
|
|
|
|
static NTSTATUS
|
|
MmMapViewOfSegment(PMMSUPPORT AddressSpace,
|
|
PROS_SECTION_OBJECT 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->Data.SectionData.Segment = Segment;
|
|
MArea->Data.SectionData.Section = Section;
|
|
MArea->Data.SectionData.ViewOffset.QuadPart = ViewOffset;
|
|
if (Section->AllocationAttributes & SEC_IMAGE)
|
|
{
|
|
MArea->VadNode.u.VadFlags.VadType = VadImageMap;
|
|
}
|
|
|
|
MmInitializeRegion(&MArea->Data.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;
|
|
PROS_SECTION_OBJECT Section;
|
|
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->Data.SectionData.ViewOffset.QuadPart;
|
|
|
|
Section = MemoryArea->Data.SectionData.Section;
|
|
Segment = MemoryArea->Data.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->Data.SectionData.Section->FileObject;
|
|
SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
|
|
CcRosMarkDirtyVacb(SharedCacheMap, Offset.QuadPart + Segment->Image.FileOffset);
|
|
#endif
|
|
ASSERT(SwapEntry == 0);
|
|
}
|
|
}
|
|
|
|
if (SwapEntry != 0)
|
|
{
|
|
/*
|
|
* Sanity check
|
|
*/
|
|
if (Segment->Flags & MM_PAGEFILE_SEGMENT)
|
|
{
|
|
DPRINT1("Found a swap entry for a page in a pagefile section.\n");
|
|
KeBugCheck(MEMORY_MANAGEMENT);
|
|
}
|
|
MmFreeSwapPage(SwapEntry);
|
|
}
|
|
else if (Page != 0)
|
|
{
|
|
if (IS_SWAP_FROM_SSE(Entry) ||
|
|
Page != PFN_FROM_SSE(Entry))
|
|
{
|
|
/*
|
|
* Sanity check
|
|
*/
|
|
if (Segment->Flags & MM_PAGEFILE_SEGMENT)
|
|
{
|
|
DPRINT1("Found a private page in a pagefile section.\n");
|
|
KeBugCheck(MEMORY_MANAGEMENT);
|
|
}
|
|
/*
|
|
* 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(Section, Segment, &Offset, Dirty, FALSE, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
static NTSTATUS
|
|
MmUnmapViewOfSegment(PMMSUPPORT AddressSpace,
|
|
PVOID BaseAddress)
|
|
{
|
|
NTSTATUS Status;
|
|
PMEMORY_AREA MemoryArea;
|
|
PROS_SECTION_OBJECT 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->Data.SectionData.Section;
|
|
Segment = MemoryArea->Data.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->Data.SectionData.RegionListHead;
|
|
while (!IsListEmpty(RegionListHead))
|
|
{
|
|
CurrentEntry = RemoveHeadList(RegionListHead);
|
|
CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION, RegionListEntry);
|
|
ExFreePoolWithTag(CurrentRegion, TAG_MM_REGION);
|
|
}
|
|
|
|
if (Section->AllocationAttributes & SEC_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;
|
|
PROS_SECTION_OBJECT 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 ||
|
|
((MemoryArea->Type != MEMORY_AREA_SECTION_VIEW) &&
|
|
(MemoryArea->Type != MEMORY_AREA_CACHE)) ||
|
|
MemoryArea->DeleteInProgress)
|
|
{
|
|
if (MemoryArea) ASSERT(MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3);
|
|
MmUnlockAddressSpace(AddressSpace);
|
|
return STATUS_NOT_MAPPED_VIEW;
|
|
}
|
|
|
|
Section = MemoryArea->Data.SectionData.Section;
|
|
|
|
if ((Section != NULL) && (Section->AllocationAttributes & SEC_IMAGE))
|
|
{
|
|
ULONG i;
|
|
ULONG NrSegments;
|
|
PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
|
|
PMM_SECTION_SEGMENT SectionSegments;
|
|
PMM_SECTION_SEGMENT Segment;
|
|
|
|
Segment = MemoryArea->Data.SectionData.Segment;
|
|
ImageSectionObject = Section->ImageSection;
|
|
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;
|
|
}
|
|
|
|
if (MiIsRosSectionObject(Section))
|
|
{
|
|
PROS_SECTION_OBJECT RosSection = (PROS_SECTION_OBJECT)Section;
|
|
|
|
switch (SectionInformationClass)
|
|
{
|
|
case SectionBasicInformation:
|
|
{
|
|
PSECTION_BASIC_INFORMATION Sbi = (PSECTION_BASIC_INFORMATION)SectionInformation;
|
|
|
|
_SEH2_TRY
|
|
{
|
|
Sbi->Attributes = RosSection->AllocationAttributes;
|
|
if (RosSection->AllocationAttributes & SEC_IMAGE)
|
|
{
|
|
Sbi->BaseAddress = 0;
|
|
Sbi->Size.QuadPart = 0;
|
|
}
|
|
else
|
|
{
|
|
Sbi->BaseAddress = (PVOID)RosSection->Segment->Image.VirtualAddress;
|
|
Sbi->Size.QuadPart = RosSection->Segment->Length.QuadPart;
|
|
}
|
|
|
|
if (ResultLength != NULL)
|
|
{
|
|
*ResultLength = sizeof(SECTION_BASIC_INFORMATION);
|
|
}
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
break;
|
|
}
|
|
|
|
case SectionImageInformation:
|
|
{
|
|
PSECTION_IMAGE_INFORMATION Sii = (PSECTION_IMAGE_INFORMATION)SectionInformation;
|
|
|
|
_SEH2_TRY
|
|
{
|
|
if (RosSection->AllocationAttributes & SEC_IMAGE)
|
|
{
|
|
PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
|
|
ImageSectionObject = RosSection->ImageSection;
|
|
|
|
*Sii = ImageSectionObject->ImageInformation;
|
|
}
|
|
|
|
if (ResultLength != NULL)
|
|
{
|
|
*ResultLength = sizeof(SECTION_IMAGE_INFORMATION);
|
|
}
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
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.Image)
|
|
Sbi.Attributes |= SEC_IMAGE;
|
|
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 */
|
|
|
|
_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
|
|
{
|
|
/* Currently not supported */
|
|
ASSERT(FALSE);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
PROS_SECTION_OBJECT 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 = (PROS_SECTION_OBJECT)SectionObject;
|
|
AddressSpace = &Process->Vm;
|
|
|
|
AllocationType |= (Section->AllocationAttributes & SEC_NO_CHANGE);
|
|
|
|
MmLockAddressSpace(AddressSpace);
|
|
|
|
if (Section->AllocationAttributes & SEC_IMAGE)
|
|
{
|
|
ULONG i;
|
|
ULONG NrSegments;
|
|
ULONG_PTR ImageBase;
|
|
SIZE_T ImageSize;
|
|
PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
|
|
PMM_SECTION_SEGMENT SectionSegments;
|
|
|
|
ImageSectionObject = Section->ImageSection;
|
|
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
|
|
{
|
|
/* check for write access */
|
|
if ((Protect & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE)) &&
|
|
!(Section->SectionPageProtection & (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->SectionPageProtection & (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->SectionPageProtection & (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->MaximumSize.u.LowPart - ViewOffset;
|
|
}
|
|
else if (((*ViewSize)+ViewOffset) > Section->MaximumSize.u.LowPart)
|
|
{
|
|
(*ViewSize) = Section->MaximumSize.u.LowPart - ViewOffset;
|
|
}
|
|
|
|
*ViewSize = PAGE_ROUND_UP(*ViewSize);
|
|
|
|
MmLockSectionSegment(Section->Segment);
|
|
Status = MmMapViewOfSegment(AddressSpace,
|
|
Section,
|
|
Section->Segment,
|
|
BaseAddress,
|
|
*ViewSize,
|
|
Protect,
|
|
ViewOffset,
|
|
AllocationType & (MEM_TOP_DOWN|SEC_NO_CHANGE));
|
|
MmUnlockSectionSegment(Section->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)
|
|
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)
|
|
{
|
|
PROS_SECTION_OBJECT Section;
|
|
PMMSUPPORT AddressSpace;
|
|
NTSTATUS Status;
|
|
PAGED_CODE();
|
|
|
|
if (MiIsRosSectionObject(SectionObject) == FALSE)
|
|
{
|
|
return MiMapViewInSystemSpace(SectionObject,
|
|
&MmSession,
|
|
MappedBase,
|
|
ViewSize);
|
|
}
|
|
|
|
DPRINT("MmMapViewInSystemSpace() called\n");
|
|
|
|
Section = (PROS_SECTION_OBJECT)SectionObject;
|
|
AddressSpace = MmGetKernelAddressSpace();
|
|
|
|
MmLockAddressSpace(AddressSpace);
|
|
|
|
|
|
if ((*ViewSize) == 0)
|
|
{
|
|
(*ViewSize) = Section->MaximumSize.u.LowPart;
|
|
}
|
|
else if ((*ViewSize) > Section->MaximumSize.u.LowPart)
|
|
{
|
|
(*ViewSize) = Section->MaximumSize.u.LowPart;
|
|
}
|
|
|
|
MmLockSectionSegment(Section->Segment);
|
|
|
|
|
|
Status = MmMapViewOfSegment(AddressSpace,
|
|
Section,
|
|
Section->Segment,
|
|
MappedBase,
|
|
*ViewSize,
|
|
PAGE_READWRITE,
|
|
0,
|
|
0);
|
|
|
|
MmUnlockSectionSegment(Section->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;
|
|
PROS_SECTION_OBJECT *SectionObject = (PROS_SECTION_OBJECT *)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,
|
|
MaximumSize,
|
|
SectionPageProtection,
|
|
AllocationAttributes,
|
|
FileObject);
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
if ((AllocationAttributes & SEC_PHYSICALMEMORY) == 0)
|
|
{
|
|
DPRINT1("Invalid path: %lx %p %p\n", AllocationAttributes, FileObject, FileHandle);
|
|
}
|
|
// ASSERT(AllocationAttributes & SEC_PHYSICALMEMORY);
|
|
Status = MmCreatePageFileSection(SectionObject,
|
|
DesiredAccess,
|
|
ObjectAttributes,
|
|
MaximumSize,
|
|
SectionPageProtection,
|
|
AllocationAttributes);
|
|
if (FileObject)
|
|
ObDereferenceObject(FileObject);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/* EOF */
|