mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5528 lines
160 KiB
5528 lines
160 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
creasect.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the routines which implement the
|
|
NtCreateSection and NtOpenSection.
|
|
|
|
Author:
|
|
|
|
Lou Perazzoli (loup) 22-May-1989
|
|
Landy Wang (landyw) 02-Jun-1997
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "mi.h"
|
|
|
|
const ULONG MMCONTROL = 'aCmM';
|
|
const ULONG MMTEMPORARY = 'xxmM';
|
|
|
|
#define MM_SIZE_OF_LARGEST_IMAGE ((ULONG)0x77000000)
|
|
|
|
#define MM_MAXIMUM_IMAGE_HEADER (2 * PAGE_SIZE)
|
|
|
|
extern SIZE_T MmAllocationFragment;
|
|
|
|
//
|
|
// The maximum number of image object (object table entries) is
|
|
// the number which will fit into the MM_MAXIMUM_IMAGE_HEADER with
|
|
// the start of the PE image header in the last word of the first
|
|
//
|
|
|
|
#define MM_MAXIMUM_IMAGE_SECTIONS \
|
|
((MM_MAXIMUM_IMAGE_HEADER - (PAGE_SIZE + sizeof(IMAGE_NT_HEADERS))) / \
|
|
sizeof(IMAGE_SECTION_HEADER))
|
|
|
|
#if DBG
|
|
extern PEPROCESS MmWatchProcess;
|
|
ULONG MiMakeImageFloppy[2];
|
|
#endif
|
|
|
|
extern POBJECT_TYPE IoFileObjectType;
|
|
|
|
CCHAR MmImageProtectionArray[16] = {
|
|
MM_NOACCESS,
|
|
MM_EXECUTE,
|
|
MM_READONLY,
|
|
MM_EXECUTE_READ,
|
|
MM_WRITECOPY,
|
|
MM_EXECUTE_WRITECOPY,
|
|
MM_WRITECOPY,
|
|
MM_EXECUTE_WRITECOPY,
|
|
MM_NOACCESS,
|
|
MM_EXECUTE,
|
|
MM_READONLY,
|
|
MM_EXECUTE_READ,
|
|
MM_READWRITE,
|
|
MM_EXECUTE_READWRITE,
|
|
MM_READWRITE,
|
|
MM_EXECUTE_READWRITE };
|
|
|
|
|
|
CCHAR
|
|
MiGetImageProtection (
|
|
IN ULONG SectionCharacteristics
|
|
);
|
|
|
|
NTSTATUS
|
|
MiVerifyImageHeader (
|
|
IN PIMAGE_NT_HEADERS NtHeader,
|
|
IN PIMAGE_DOS_HEADER DosHeader,
|
|
IN ULONG NtHeaderSize
|
|
);
|
|
|
|
LOGICAL
|
|
MiCheckDosCalls (
|
|
IN PIMAGE_OS2_HEADER Os2Header,
|
|
IN ULONG HeaderSize
|
|
);
|
|
|
|
PCONTROL_AREA
|
|
MiFindImageSectionObject(
|
|
IN PFILE_OBJECT File,
|
|
IN PLOGICAL GlobalNeeded
|
|
);
|
|
|
|
VOID
|
|
MiInsertImageSectionObject(
|
|
IN PFILE_OBJECT File,
|
|
IN PCONTROL_AREA ControlArea
|
|
);
|
|
|
|
LOGICAL
|
|
MiFlushDataSection(
|
|
IN PFILE_OBJECT File
|
|
);
|
|
|
|
PVOID
|
|
MiCopyHeaderIfResident (
|
|
IN PFILE_OBJECT File,
|
|
IN PFN_NUMBER ImagePageFrameNumber
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE,MiCreateImageFileMap)
|
|
#pragma alloc_text(PAGE,NtCreateSection)
|
|
#pragma alloc_text(PAGE,NtOpenSection)
|
|
#pragma alloc_text(PAGE,MiGetImageProtection)
|
|
#pragma alloc_text(PAGE,MiVerifyImageHeader)
|
|
#pragma alloc_text(PAGE,MiCheckDosCalls)
|
|
#pragma alloc_text(PAGE,MiCreatePagingFileMap)
|
|
#pragma alloc_text(PAGE,MiCreateDataFileMap)
|
|
|
|
#pragma alloc_text(PAGE,MiGetWritablePagesInSection)
|
|
#endif
|
|
|
|
#pragma pack (1)
|
|
typedef struct _PHARLAP_CONFIG {
|
|
UCHAR uchCopyRight[0x32];
|
|
USHORT usType;
|
|
USHORT usRsv1;
|
|
USHORT usRsv2;
|
|
USHORT usSign;
|
|
} CONFIGPHARLAP, *PCONFIGPHARLAP;
|
|
#pragma pack ()
|
|
|
|
|
|
NTSTATUS
|
|
NtCreateSection (
|
|
OUT PHANDLE SectionHandle,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
|
|
IN PLARGE_INTEGER MaximumSize OPTIONAL,
|
|
IN ULONG SectionPageProtection,
|
|
IN ULONG AllocationAttributes,
|
|
IN HANDLE FileHandle OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function creates a section object and opens a handle to the object
|
|
with the specified desired access.
|
|
|
|
Arguments:
|
|
|
|
SectionHandle - A pointer to a variable that will
|
|
receive the section object handle value.
|
|
|
|
DesiredAccess - The desired types of access for the section.
|
|
|
|
DesiredAccess Flags
|
|
|
|
EXECUTE - Execute access to the section is
|
|
desired.
|
|
|
|
READ - Read access to the section is desired.
|
|
|
|
WRITE - Write access to the section is desired.
|
|
|
|
|
|
ObjectAttributes - Supplies a pointer to an object attributes structure.
|
|
|
|
MaximumSize - Supplies the maximum size of the section in bytes.
|
|
This value is rounded up to the host page size and
|
|
specifies the size of the section (page file
|
|
backed section) or the maximum size to which a
|
|
file can be extended or mapped (file backed
|
|
section).
|
|
|
|
SectionPageProtection - Supplies the protection to place on each page
|
|
in the section. One of PAGE_READ, PAGE_READWRITE, PAGE_EXECUTE,
|
|
or PAGE_WRITECOPY and, optionally, PAGE_NOCACHE may be specified.
|
|
|
|
AllocationAttributes - Supplies a set of flags that describe the
|
|
allocation attributes of the section.
|
|
|
|
AllocationAttributes Flags
|
|
|
|
SEC_BASED - The section is a based section and will be
|
|
allocated at the same virtual address in each process
|
|
address space that receives the section. This does not
|
|
imply that addresses are reserved for based sections.
|
|
Rather if the section cannot be mapped at the based address
|
|
an error is returned.
|
|
|
|
|
|
SEC_RESERVE - All pages of the section are set to the
|
|
reserved state.
|
|
|
|
SEC_COMMIT - All pages of the section are set to the commit
|
|
state.
|
|
|
|
SEC_IMAGE - The file specified by the file handle is an
|
|
executable image file.
|
|
|
|
SEC_FILE - The file specified by the file handle is a mapped
|
|
file. If a file handle is supplied and neither
|
|
SEC_IMAGE or SEC_FILE is supplied, SEC_FILE is
|
|
assumed.
|
|
|
|
SEC_NO_CHANGE - Once the file is mapped, the protection cannot
|
|
be changed nor can the view be unmapped. The
|
|
view is unmapped when the process is deleted.
|
|
Cannot be used with SEC_IMAGE.
|
|
|
|
FileHandle - Supplies an optional handle of an open file object.
|
|
If the value of this handle is null, then the
|
|
section will be backed by a paging file. Otherwise
|
|
the section is backed by the specified data file.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PVOID Section;
|
|
HANDLE Handle;
|
|
LARGE_INTEGER LargeSize;
|
|
LARGE_INTEGER CapturedSize;
|
|
ULONG RetryCount;
|
|
PCONTROL_AREA ControlArea;
|
|
|
|
if ((AllocationAttributes & ~(SEC_COMMIT | SEC_RESERVE | SEC_BASED |
|
|
SEC_IMAGE | SEC_NOCACHE | SEC_NO_CHANGE)) != 0) {
|
|
return STATUS_INVALID_PARAMETER_6;
|
|
}
|
|
|
|
if ((AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_IMAGE)) == 0) {
|
|
return STATUS_INVALID_PARAMETER_6;
|
|
}
|
|
|
|
if ((AllocationAttributes & SEC_IMAGE) &&
|
|
(AllocationAttributes & (SEC_COMMIT | SEC_RESERVE |
|
|
SEC_NOCACHE | SEC_NO_CHANGE))) {
|
|
|
|
return STATUS_INVALID_PARAMETER_6;
|
|
}
|
|
|
|
if ((AllocationAttributes & SEC_COMMIT) &&
|
|
(AllocationAttributes & SEC_RESERVE)) {
|
|
return STATUS_INVALID_PARAMETER_6;
|
|
}
|
|
|
|
//
|
|
// Check the SectionProtection Flag.
|
|
//
|
|
|
|
if ((SectionPageProtection & PAGE_NOCACHE) ||
|
|
(SectionPageProtection & PAGE_GUARD) ||
|
|
(SectionPageProtection & PAGE_NOACCESS)) {
|
|
|
|
//
|
|
// No cache is only specified through SEC_NOCACHE option in the
|
|
// allocation attributes.
|
|
//
|
|
|
|
return STATUS_INVALID_PAGE_PROTECTION;
|
|
}
|
|
|
|
|
|
if (KeGetPreviousMode() != KernelMode) {
|
|
try {
|
|
ProbeForWriteHandle(SectionHandle);
|
|
|
|
if (ARGUMENT_PRESENT (MaximumSize)) {
|
|
|
|
#if !defined (_WIN64)
|
|
|
|
//
|
|
// Note we only probe for byte alignment because prior to 2195,
|
|
// we never probed at all! We don't want to break user apps
|
|
// that had bad alignment if they worked before.
|
|
//
|
|
|
|
ProbeForReadSmallStructure(MaximumSize, sizeof(LARGE_INTEGER), sizeof(UCHAR));
|
|
#else
|
|
ProbeForReadSmallStructure(MaximumSize, sizeof(LARGE_INTEGER), sizeof(LARGE_INTEGER));
|
|
#endif
|
|
LargeSize = *MaximumSize;
|
|
}
|
|
else {
|
|
ZERO_LARGE (LargeSize);
|
|
}
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
return GetExceptionCode();
|
|
}
|
|
}
|
|
else {
|
|
if (ARGUMENT_PRESENT (MaximumSize)) {
|
|
LargeSize = *MaximumSize;
|
|
}
|
|
else {
|
|
ZERO_LARGE (LargeSize);
|
|
}
|
|
}
|
|
|
|
RetryCount = 0;
|
|
|
|
retry:
|
|
|
|
CapturedSize = LargeSize;
|
|
|
|
ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
|
|
Status = MmCreateSection ( &Section,
|
|
DesiredAccess,
|
|
ObjectAttributes,
|
|
&CapturedSize,
|
|
SectionPageProtection,
|
|
AllocationAttributes,
|
|
FileHandle,
|
|
NULL );
|
|
|
|
|
|
ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
if ((Status == STATUS_FILE_LOCK_CONFLICT) &&
|
|
(RetryCount < 3)) {
|
|
|
|
//
|
|
// The file system may have prevented this from working
|
|
// due to log file flushing. Delay and try again.
|
|
//
|
|
|
|
RetryCount += 1;
|
|
|
|
KeDelayExecutionThread (KernelMode,
|
|
FALSE,
|
|
(PLARGE_INTEGER)&MmHalfSecond);
|
|
|
|
goto retry;
|
|
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
ControlArea = ((PSECTION)Section)->Segment->ControlArea;
|
|
|
|
#if DBG
|
|
if (MmDebug & MM_DBG_SECTIONS) {
|
|
DbgPrint ("inserting section %p control %p\n", Section, ControlArea);
|
|
}
|
|
#endif
|
|
|
|
if ((ControlArea != NULL) && (ControlArea->FilePointer != NULL)) {
|
|
CcZeroEndOfLastPage (ControlArea->FilePointer);
|
|
}
|
|
|
|
//
|
|
// Note if the insertion fails, Ob will dereference the object for us.
|
|
//
|
|
|
|
Status = ObInsertObject (Section,
|
|
NULL,
|
|
DesiredAccess,
|
|
0,
|
|
(PVOID *)NULL,
|
|
&Handle);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
try {
|
|
*SectionHandle = Handle;
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
//
|
|
// If the write attempt fails, then do not report an error.
|
|
// When the caller attempts to access the handle value,
|
|
// an access violation will occur.
|
|
//
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MmCreateSection (
|
|
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
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function creates a section object and opens a handle to the object
|
|
with the specified desired access.
|
|
|
|
Arguments:
|
|
|
|
Section - A pointer to a variable that will
|
|
receive the section object address.
|
|
|
|
DesiredAccess - The desired types of access for the section.
|
|
|
|
DesiredAccess Flags
|
|
|
|
EXECUTE - Execute access to the section is desired.
|
|
|
|
READ - Read access to the section is desired.
|
|
|
|
WRITE - Write access to the section is desired.
|
|
|
|
ObjectAttributes - Supplies a pointer to an object attributes structure.
|
|
|
|
InputMaximumSize - Supplies the maximum size of the section in bytes.
|
|
This value is rounded up to the host page size and
|
|
specifies the size of the section (page file
|
|
backed section) or the maximum size to which a
|
|
file can be extended or mapped (file backed
|
|
section).
|
|
|
|
SectionPageProtection - Supplies the protection to place on each page
|
|
in the section. One of PAGE_READ, PAGE_READWRITE,
|
|
PAGE_EXECUTE, or PAGE_WRITECOPY and, optionally,
|
|
PAGE_NOCACHE may be specified.
|
|
|
|
AllocationAttributes - Supplies a set of flags that describe the
|
|
allocation attributes of the section.
|
|
|
|
AllocationAttributes Flags
|
|
|
|
SEC_BASED - The section is a based section and will be
|
|
allocated at the same virtual address in each process
|
|
address space that receives the section. This does not
|
|
imply that addresses are reserved for based sections.
|
|
Rather if the section cannot be mapped at the based address
|
|
an error is returned.
|
|
|
|
SEC_RESERVE - All pages of the section are set to the
|
|
reserved state.
|
|
|
|
SEC_COMMIT - All pages of the section are set to the commit state.
|
|
|
|
SEC_IMAGE - The file specified by the file handle is an
|
|
executable image file.
|
|
|
|
SEC_FILE - The file specified by the file handle is a mapped
|
|
file. If a file handle is supplied and neither
|
|
SEC_IMAGE or SEC_FILE is supplied, SEC_FILE is
|
|
assumed.
|
|
|
|
FileHandle - Supplies an optional handle of an open file object.
|
|
If the value of this handle is null, then the
|
|
section will be backed by a paging file. Otherwise
|
|
the section is backed by the specified data file.
|
|
|
|
FileObject - Supplies an optional pointer to the file object. If this
|
|
value is NULL and the FileHandle is NULL, then there is
|
|
no file to map (image or mapped file). If this value
|
|
is specified, then the File is to be mapped as a MAPPED FILE
|
|
and NO file size checking will be performed.
|
|
|
|
ONLY THE SYSTEM CACHE SHOULD PROVIDE A FILE OBJECT WITH THE
|
|
CALL!! as this is optimized to not check the size, only do
|
|
data mapping, no protection check, etc.
|
|
|
|
Note - Only one of FileHandle or File should be specified!
|
|
|
|
Return Value:
|
|
|
|
Returns the relevant NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
SECTION Section;
|
|
PSECTION NewSection;
|
|
PSEGMENT Segment;
|
|
PSEGMENT NewSegment;
|
|
KPROCESSOR_MODE PreviousMode;
|
|
KIRQL OldIrql;
|
|
NTSTATUS Status;
|
|
NTSTATUS Status2;
|
|
PCONTROL_AREA ControlArea;
|
|
PCONTROL_AREA NewControlArea;
|
|
PCONTROL_AREA SegmentControlArea;
|
|
ACCESS_MASK FileDesiredAccess;
|
|
PFILE_OBJECT File;
|
|
PEVENT_COUNTER Event;
|
|
ULONG IgnoreFileSizing;
|
|
ULONG ProtectionMask;
|
|
ULONG ProtectMaskForAccess;
|
|
ULONG FileAcquired;
|
|
PEVENT_COUNTER SegmentEvent;
|
|
LOGICAL FileSizeChecked;
|
|
LARGE_INTEGER TempSectionSize;
|
|
UINT64 EndOfFile;
|
|
ULONG IncrementedRefCount;
|
|
SIZE_T ControlAreaSize;
|
|
PUINT64 MaximumSize;
|
|
PMMADDRESS_NODE *SectionBasedRoot;
|
|
LOGICAL GlobalNeeded;
|
|
PFILE_OBJECT ChangeFileReference;
|
|
SIZE_T SizeOfSection;
|
|
#if DBG
|
|
PVOID PreviousSectionPointer;
|
|
|
|
PreviousSectionPointer = (PVOID)-1;
|
|
#endif
|
|
|
|
NewControlArea = (PCONTROL_AREA)-1;
|
|
|
|
UNREFERENCED_PARAMETER (DesiredAccess);
|
|
|
|
IgnoreFileSizing = FALSE;
|
|
FileAcquired = FALSE;
|
|
FileSizeChecked = FALSE;
|
|
IncrementedRefCount = FALSE;
|
|
ChangeFileReference = NULL;
|
|
|
|
MaximumSize = (PUINT64) InputMaximumSize;
|
|
|
|
//
|
|
// Check allocation attributes flags.
|
|
//
|
|
|
|
File = (PFILE_OBJECT)NULL;
|
|
|
|
ASSERT ((AllocationAttributes & ~(SEC_COMMIT | SEC_RESERVE | SEC_BASED |
|
|
SEC_IMAGE | SEC_NOCACHE | SEC_NO_CHANGE)) == 0);
|
|
|
|
ASSERT ((AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_IMAGE)) != 0);
|
|
|
|
ASSERT (!((AllocationAttributes & SEC_IMAGE) &&
|
|
(AllocationAttributes & (SEC_COMMIT | SEC_RESERVE |
|
|
SEC_NOCACHE | SEC_NO_CHANGE))));
|
|
|
|
ASSERT (!((AllocationAttributes & SEC_COMMIT) &&
|
|
(AllocationAttributes & SEC_RESERVE)));
|
|
|
|
ASSERT (!((SectionPageProtection & PAGE_NOCACHE) ||
|
|
(SectionPageProtection & PAGE_GUARD) ||
|
|
(SectionPageProtection & PAGE_NOACCESS)));
|
|
|
|
if (AllocationAttributes & SEC_NOCACHE) {
|
|
SectionPageProtection |= PAGE_NOCACHE;
|
|
}
|
|
|
|
//
|
|
// Check the protection field.
|
|
//
|
|
|
|
ProtectionMask = MiMakeProtectionMask (SectionPageProtection);
|
|
if (ProtectionMask == MM_INVALID_PROTECTION) {
|
|
return STATUS_INVALID_PAGE_PROTECTION;
|
|
}
|
|
|
|
ProtectMaskForAccess = ProtectionMask & 0x7;
|
|
|
|
FileDesiredAccess = MmMakeFileAccess[ProtectMaskForAccess];
|
|
|
|
//
|
|
// Get previous processor mode and probe output arguments if necessary.
|
|
//
|
|
|
|
PreviousMode = KeGetPreviousMode();
|
|
|
|
Section.InitialPageProtection = SectionPageProtection;
|
|
Section.Segment = (PSEGMENT)NULL;
|
|
|
|
//
|
|
// Initializing Segment is not needed for correctness, but
|
|
// without it the compiler cannot compile this code W4 to check
|
|
// for use of uninitialized variables.
|
|
//
|
|
|
|
Segment = (PSEGMENT)-1;
|
|
|
|
if (ARGUMENT_PRESENT(FileHandle) || ARGUMENT_PRESENT(FileObject)) {
|
|
|
|
//
|
|
// Only one of FileHandle or FileObject should be supplied.
|
|
// If a FileObject is supplied, this must be from the
|
|
// file system and therefore the file's size should not
|
|
// be checked.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(FileObject)) {
|
|
IgnoreFileSizing = TRUE;
|
|
File = FileObject;
|
|
|
|
//
|
|
// Quick check to see if a control area already exists.
|
|
//
|
|
|
|
if (File->SectionObjectPointer->DataSectionObject) {
|
|
|
|
LOCK_PFN (OldIrql);
|
|
ControlArea =
|
|
(PCONTROL_AREA)(File->SectionObjectPointer->DataSectionObject);
|
|
|
|
if ((ControlArea != NULL) &&
|
|
(!ControlArea->u.Flags.BeingDeleted) &&
|
|
(!ControlArea->u.Flags.BeingCreated)) {
|
|
|
|
//
|
|
// Control area exists and is not being deleted,
|
|
// reference it.
|
|
//
|
|
|
|
NewSegment = ControlArea->Segment;
|
|
if ((ControlArea->NumberOfSectionReferences == 0) &&
|
|
(ControlArea->NumberOfMappedViews == 0) &&
|
|
(ControlArea->ModifiedWriteCount == 0)) {
|
|
|
|
//
|
|
// Dereference the current file object (after releasing
|
|
// the PFN lock) and reference this one.
|
|
//
|
|
|
|
ASSERT (ControlArea->FilePointer != NULL);
|
|
ChangeFileReference = ControlArea->FilePointer;
|
|
ControlArea->FilePointer = FileObject;
|
|
}
|
|
ControlArea->u.Flags.Accessed = 1;
|
|
ControlArea->NumberOfSectionReferences += 1;
|
|
if (ControlArea->DereferenceList.Flink != NULL) {
|
|
|
|
//
|
|
// Remove this from the list of unused segments.
|
|
//
|
|
|
|
RemoveEntryList (&ControlArea->DereferenceList);
|
|
|
|
MI_UNUSED_SEGMENTS_REMOVE_CHARGE (ControlArea);
|
|
|
|
ControlArea->DereferenceList.Flink = NULL;
|
|
ControlArea->DereferenceList.Blink = NULL;
|
|
}
|
|
UNLOCK_PFN (OldIrql);
|
|
|
|
//
|
|
// Inform the object manager to defer this deletion by
|
|
// queueing it to another thread to eliminate deadlocks
|
|
// with the redirector.
|
|
//
|
|
|
|
if (ChangeFileReference != NULL) {
|
|
ObDereferenceObjectDeferDelete (ChangeFileReference);
|
|
}
|
|
|
|
IncrementedRefCount = TRUE;
|
|
Section.SizeOfSection.QuadPart = (LONGLONG)*MaximumSize;
|
|
|
|
goto ReferenceObject;
|
|
}
|
|
UNLOCK_PFN (OldIrql);
|
|
}
|
|
|
|
ObReferenceObject (FileObject);
|
|
|
|
}
|
|
else {
|
|
|
|
Status = ObReferenceObjectByHandle ( FileHandle,
|
|
FileDesiredAccess,
|
|
IoFileObjectType,
|
|
PreviousMode,
|
|
(PVOID *)&File,
|
|
NULL );
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// If this file doesn't have a section object pointer,
|
|
// return an error.
|
|
//
|
|
|
|
if (File->SectionObjectPointer == NULL) {
|
|
ObDereferenceObject (File);
|
|
return STATUS_INVALID_FILE_FOR_SECTION;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check to see if the specified file already has a section.
|
|
// If not, indicate in the file object's pointer to an FCB that
|
|
// a section is being built. This synchronizes segment creation
|
|
// for the file.
|
|
//
|
|
|
|
if (AllocationAttributes & SEC_IMAGE) {
|
|
|
|
//
|
|
// This control area is always just a place holder - the real one
|
|
// is allocated in MiCreateImageFileMap and will be allocated
|
|
// with the correct size and this one freed in a short while.
|
|
//
|
|
// This place holder must always be allocated as a large control
|
|
// area so that it can be chained for the per-session case.
|
|
//
|
|
|
|
ControlAreaSize = sizeof(LARGE_CONTROL_AREA) + sizeof(SUBSECTION);
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Data files are mapped with larger subsections than images or
|
|
// pagefile-backed shared memory. Factor that in here.
|
|
//
|
|
|
|
ControlAreaSize = sizeof(CONTROL_AREA) + sizeof(MSUBSECTION);
|
|
}
|
|
|
|
NewControlArea = ExAllocatePoolWithTag (NonPagedPool,
|
|
ControlAreaSize,
|
|
MMCONTROL);
|
|
|
|
if (NewControlArea == NULL) {
|
|
ObDereferenceObject (File);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory (NewControlArea, ControlAreaSize);
|
|
|
|
NewSegment = NULL;
|
|
|
|
//
|
|
// We only need the file resource if this was a user request, i.e. not
|
|
// a call from the cache manager or file system.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(FileHandle)) {
|
|
|
|
Status = FsRtlAcquireToCreateMappedSection (File, SectionPageProtection);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ExFreePool (NewControlArea);
|
|
ObDereferenceObject (File);
|
|
return Status;
|
|
}
|
|
|
|
IoSetTopLevelIrp((PIRP)FSRTL_FSP_TOP_LEVEL_IRP);
|
|
FileAcquired = TRUE;
|
|
}
|
|
|
|
//
|
|
// Initializing GlobalNeeded is not needed for correctness, but
|
|
// without it the compiler cannot compile this code W4 to check
|
|
// for use of uninitialized variables.
|
|
//
|
|
|
|
GlobalNeeded = FALSE;
|
|
|
|
//
|
|
// Allocate an event to wait on in case the segment is in the
|
|
// process of being deleted. This event cannot be allocated
|
|
// with the PFN database locked as pool expansion would deadlock.
|
|
//
|
|
|
|
ReallocateandcheckSegment:
|
|
|
|
SegmentEvent = MiGetEventCounter();
|
|
|
|
if (SegmentEvent == NULL) {
|
|
if (FileAcquired) {
|
|
IoSetTopLevelIrp((PIRP)NULL);
|
|
FsRtlReleaseFile (File);
|
|
}
|
|
ExFreePool (NewControlArea);
|
|
ObDereferenceObject (File);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RecheckSegment:
|
|
|
|
LOCK_PFN (OldIrql);
|
|
|
|
if (AllocationAttributes & SEC_IMAGE) {
|
|
|
|
ControlArea = MiFindImageSectionObject (File, &GlobalNeeded);
|
|
|
|
}
|
|
else {
|
|
ControlArea =
|
|
(PCONTROL_AREA)(File->SectionObjectPointer->DataSectionObject);
|
|
}
|
|
|
|
if (ControlArea != NULL) {
|
|
|
|
//
|
|
// A segment already exists for this file. Make sure that it
|
|
// is not in the process of being deleted, or being created.
|
|
//
|
|
|
|
|
|
if ((ControlArea->u.Flags.BeingDeleted) ||
|
|
(ControlArea->u.Flags.BeingCreated)) {
|
|
|
|
//
|
|
// The segment object is in the process of being deleted or
|
|
// created.
|
|
// Check to see if another thread is waiting for the deletion,
|
|
// otherwise create an event object to wait upon.
|
|
//
|
|
|
|
if (ControlArea->WaitingForDeletion == NULL) {
|
|
|
|
//
|
|
// Initialize an event and put its address in the control area.
|
|
//
|
|
|
|
ControlArea->WaitingForDeletion = SegmentEvent;
|
|
Event = SegmentEvent;
|
|
SegmentEvent = NULL;
|
|
}
|
|
else {
|
|
Event = ControlArea->WaitingForDeletion;
|
|
|
|
//
|
|
// No interlock is needed for the RefCount increment as
|
|
// no thread can be decrementing it since it is still
|
|
// pointed to by the control area.
|
|
//
|
|
|
|
Event->RefCount += 1;
|
|
}
|
|
|
|
//
|
|
// Release the PFN lock, the file lock, and wait for the event.
|
|
//
|
|
|
|
UNLOCK_PFN (OldIrql);
|
|
if (FileAcquired) {
|
|
IoSetTopLevelIrp((PIRP)NULL);
|
|
FsRtlReleaseFile (File);
|
|
}
|
|
|
|
KeWaitForSingleObject(&Event->Event,
|
|
WrVirtualMemory,
|
|
KernelMode,
|
|
FALSE,
|
|
(PLARGE_INTEGER)NULL);
|
|
|
|
//
|
|
// Before this event can be set, the control area
|
|
// WaitingForDeletion field must be cleared (and may be
|
|
// reinitialized to something else), but cannot be reset
|
|
// to our local event. This allows us to dereference the
|
|
// event count lock free.
|
|
//
|
|
|
|
#if 0
|
|
//
|
|
// Note that the control area cannot be referenced at this
|
|
// point because it may have been freed.
|
|
//
|
|
|
|
ASSERT (Event != ControlArea->WaitingForDeletion);
|
|
#endif
|
|
|
|
if (FileAcquired) {
|
|
Status = FsRtlAcquireToCreateMappedSection (File, SectionPageProtection);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
IoSetTopLevelIrp((PIRP)FSRTL_FSP_TOP_LEVEL_IRP);
|
|
}
|
|
else {
|
|
ExFreePool (NewControlArea);
|
|
ObDereferenceObject (File);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
MiFreeEventCounter (Event);
|
|
|
|
if (SegmentEvent == NULL) {
|
|
|
|
//
|
|
// The event was freed from pool, allocate another
|
|
// event in case we have to synchronize one more time.
|
|
//
|
|
|
|
goto ReallocateandcheckSegment;
|
|
}
|
|
goto RecheckSegment;
|
|
|
|
}
|
|
|
|
//
|
|
// There is already a segment for this file, have
|
|
// this section refer to that segment.
|
|
// No need to reference the file object any more.
|
|
//
|
|
|
|
NewSegment = ControlArea->Segment;
|
|
ControlArea->u.Flags.Accessed = 1;
|
|
ControlArea->NumberOfSectionReferences += 1;
|
|
if (ControlArea->DereferenceList.Flink != NULL) {
|
|
|
|
//
|
|
// Remove this from the list of unused segments.
|
|
//
|
|
|
|
RemoveEntryList (&ControlArea->DereferenceList);
|
|
|
|
MI_UNUSED_SEGMENTS_REMOVE_CHARGE (ControlArea);
|
|
|
|
ControlArea->DereferenceList.Flink = NULL;
|
|
ControlArea->DereferenceList.Blink = NULL;
|
|
}
|
|
IncrementedRefCount = TRUE;
|
|
|
|
//
|
|
// If this reference was not from the cache manager
|
|
// up the count of user references.
|
|
//
|
|
|
|
if (IgnoreFileSizing == FALSE) {
|
|
ControlArea->NumberOfUserReferences += 1;
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// There is no segment associated with this file object.
|
|
// Set the file object to refer to the new control area.
|
|
//
|
|
|
|
ControlArea = NewControlArea;
|
|
ControlArea->u.Flags.BeingCreated = 1;
|
|
|
|
if (AllocationAttributes & SEC_IMAGE) {
|
|
if (GlobalNeeded == TRUE) {
|
|
ControlArea->u.Flags.GlobalOnlyPerSession = 1;
|
|
}
|
|
|
|
MiInsertImageSectionObject (File, ControlArea);
|
|
}
|
|
else {
|
|
#if DBG
|
|
PreviousSectionPointer = File->SectionObjectPointer;
|
|
#endif
|
|
File->SectionObjectPointer->DataSectionObject = (PVOID) ControlArea;
|
|
}
|
|
}
|
|
|
|
UNLOCK_PFN (OldIrql);
|
|
|
|
if (SegmentEvent != NULL) {
|
|
MiFreeEventCounter (SegmentEvent);
|
|
}
|
|
|
|
if (NewSegment != (PSEGMENT)NULL) {
|
|
|
|
//
|
|
// A segment already exists for this file object.
|
|
// If we're creating an imagemap, flush the data section
|
|
// if there is one.
|
|
//
|
|
|
|
if (AllocationAttributes & SEC_IMAGE) {
|
|
MiFlushDataSection (File);
|
|
}
|
|
|
|
//
|
|
// Deallocate the new control area as it won't be needed.
|
|
// Dereference the file object later when we're done with it.
|
|
//
|
|
|
|
ExFreePool (NewControlArea);
|
|
|
|
//
|
|
// The section is in paged pool, this can't be set until
|
|
// the PFN mutex has been released.
|
|
//
|
|
|
|
if ((!IgnoreFileSizing) && (ControlArea->u.Flags.Image == 0)) {
|
|
|
|
//
|
|
// The file size in the segment may not match the current
|
|
// file size, query the file system and get the file
|
|
// size.
|
|
//
|
|
|
|
Status = FsRtlGetFileSize (File, (PLARGE_INTEGER)&EndOfFile );
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
|
|
if (FileAcquired) {
|
|
IoSetTopLevelIrp((PIRP)NULL);
|
|
FsRtlReleaseFile (File);
|
|
FileAcquired = FALSE;
|
|
}
|
|
|
|
ObDereferenceObject (File);
|
|
goto UnrefAndReturn;
|
|
}
|
|
|
|
if (EndOfFile == 0 && *MaximumSize == 0) {
|
|
|
|
//
|
|
// Can't map a zero length without specifying the maximum
|
|
// size as non-zero.
|
|
//
|
|
|
|
Status = STATUS_MAPPED_FILE_SIZE_ZERO;
|
|
|
|
if (FileAcquired) {
|
|
IoSetTopLevelIrp((PIRP)NULL);
|
|
FsRtlReleaseFile (File);
|
|
FileAcquired = FALSE;
|
|
}
|
|
|
|
ObDereferenceObject (File);
|
|
goto UnrefAndReturn;
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// The size is okay in the segment.
|
|
//
|
|
|
|
EndOfFile = (UINT64) NewSegment->SizeOfSegment;
|
|
}
|
|
|
|
if (FileAcquired) {
|
|
IoSetTopLevelIrp((PIRP)NULL);
|
|
FsRtlReleaseFile (File);
|
|
FileAcquired = FALSE;
|
|
}
|
|
|
|
ObDereferenceObject (File);
|
|
|
|
if (*MaximumSize == 0) {
|
|
|
|
Section.SizeOfSection.QuadPart = (LONGLONG)EndOfFile;
|
|
FileSizeChecked = TRUE;
|
|
}
|
|
else if (EndOfFile >= *MaximumSize) {
|
|
|
|
//
|
|
// EndOfFile is greater than the MaximumSize,
|
|
// use the specified maximum size.
|
|
//
|
|
|
|
Section.SizeOfSection.QuadPart = (LONGLONG)*MaximumSize;
|
|
FileSizeChecked = TRUE;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Need to extend the section, make sure the file was
|
|
// opened for write access.
|
|
//
|
|
|
|
if (((SectionPageProtection & PAGE_READWRITE) |
|
|
(SectionPageProtection & PAGE_EXECUTE_READWRITE)) == 0) {
|
|
|
|
Status = STATUS_SECTION_TOO_BIG;
|
|
goto UnrefAndReturn;
|
|
}
|
|
Section.SizeOfSection.QuadPart = (LONGLONG)*MaximumSize;
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// The file does not have an associated segment, create a segment
|
|
// object.
|
|
//
|
|
|
|
PERFINFO_SECTION_CREATE1(File);
|
|
|
|
if (AllocationAttributes & SEC_IMAGE) {
|
|
|
|
Status = MiCreateImageFileMap (File, &Segment);
|
|
|
|
}
|
|
else {
|
|
|
|
Status = MiCreateDataFileMap (File,
|
|
&Segment,
|
|
MaximumSize,
|
|
SectionPageProtection,
|
|
AllocationAttributes,
|
|
IgnoreFileSizing );
|
|
ASSERT (PreviousSectionPointer == File->SectionObjectPointer);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Lock the PFN database and check to see if another thread has
|
|
// tried to create a segment to the file object at the same
|
|
// time.
|
|
//
|
|
|
|
LOCK_PFN (OldIrql);
|
|
|
|
Event = ControlArea->WaitingForDeletion;
|
|
ControlArea->WaitingForDeletion = NULL;
|
|
ASSERT (ControlArea->u.Flags.FilePointerNull == 0);
|
|
ControlArea->u.Flags.FilePointerNull = 1;
|
|
|
|
if (AllocationAttributes & SEC_IMAGE) {
|
|
MiRemoveImageSectionObject (File, ControlArea);
|
|
}
|
|
else {
|
|
File->SectionObjectPointer->DataSectionObject = NULL;
|
|
}
|
|
ControlArea->u.Flags.BeingCreated = 0;
|
|
|
|
UNLOCK_PFN (OldIrql);
|
|
|
|
if (FileAcquired) {
|
|
IoSetTopLevelIrp((PIRP)NULL);
|
|
FsRtlReleaseFile (File);
|
|
}
|
|
|
|
ExFreePool (NewControlArea);
|
|
|
|
ObDereferenceObject (File);
|
|
|
|
if (Event != NULL) {
|
|
|
|
//
|
|
// Signal any waiters that the segment structure exists.
|
|
//
|
|
|
|
KeSetEvent (&Event->Event, 0, FALSE);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// If the size was specified as zero, set the section size
|
|
// from the created segment size. This solves problems with
|
|
// race conditions when multiple sections
|
|
// are created for the same mapped file with varying sizes.
|
|
//
|
|
|
|
if (*MaximumSize == 0) {
|
|
Section.SizeOfSection.QuadPart = (LONGLONG)Segment->SizeOfSegment;
|
|
}
|
|
else {
|
|
Section.SizeOfSection.QuadPart = (LONGLONG)*MaximumSize;
|
|
}
|
|
}
|
|
|
|
}
|
|
else {
|
|
|
|
//
|
|
// No file handle exists, this is a page file backed section.
|
|
//
|
|
|
|
if (AllocationAttributes & SEC_IMAGE) {
|
|
return STATUS_INVALID_FILE_FOR_SECTION;
|
|
}
|
|
|
|
Status = MiCreatePagingFileMap (&NewSegment,
|
|
MaximumSize,
|
|
ProtectionMask,
|
|
AllocationAttributes);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Set the section size from the created segment size. This
|
|
// solves problems with race conditions when multiple sections
|
|
// are created for the same mapped file with varying sizes.
|
|
//
|
|
|
|
Section.SizeOfSection.QuadPart = (LONGLONG)NewSegment->SizeOfSegment;
|
|
ControlArea = NewSegment->ControlArea;
|
|
|
|
//
|
|
// Set IncrementedRefCount so any failures from this point before the
|
|
// object is created will result in the control area & segment getting
|
|
// torn down - otherwise these could leak. This is because pagefile
|
|
// backed sections are not (and should not be) added to the
|
|
// dereference segment cache.
|
|
//
|
|
|
|
IncrementedRefCount = 1;
|
|
}
|
|
|
|
if (NewSegment == NULL) {
|
|
|
|
//
|
|
// A new segment had to be created. Lock the PFN database and
|
|
// check to see if any other threads also tried to create a new segment
|
|
// for this file object at the same time.
|
|
//
|
|
|
|
NewSegment = Segment;
|
|
|
|
SegmentControlArea = Segment->ControlArea;
|
|
|
|
ASSERT (File != NULL);
|
|
|
|
LOCK_PFN (OldIrql);
|
|
|
|
Event = ControlArea->WaitingForDeletion;
|
|
ControlArea->WaitingForDeletion = NULL;
|
|
|
|
if (AllocationAttributes & SEC_IMAGE) {
|
|
|
|
//
|
|
// Change the control area in the file object pointer.
|
|
//
|
|
|
|
MiRemoveImageSectionObject (File, NewControlArea);
|
|
MiInsertImageSectionObject (File, SegmentControlArea);
|
|
|
|
ControlArea = SegmentControlArea;
|
|
}
|
|
else if (SegmentControlArea->u.Flags.Rom == 1) {
|
|
ASSERT (File->SectionObjectPointer->DataSectionObject == NewControlArea);
|
|
File->SectionObjectPointer->DataSectionObject = SegmentControlArea;
|
|
|
|
ControlArea = SegmentControlArea;
|
|
}
|
|
|
|
ControlArea->u.Flags.BeingCreated = 0;
|
|
|
|
UNLOCK_PFN (OldIrql);
|
|
|
|
if ((AllocationAttributes & SEC_IMAGE) ||
|
|
(SegmentControlArea->u.Flags.Rom == 1)) {
|
|
|
|
//
|
|
// Deallocate the pool used for the original control area.
|
|
//
|
|
|
|
ExFreePool (NewControlArea);
|
|
}
|
|
|
|
if (Event != NULL) {
|
|
|
|
//
|
|
// Signal any waiters that the segment structure exists.
|
|
//
|
|
|
|
KeSetEvent (&Event->Event, 0, FALSE);
|
|
}
|
|
|
|
PERFINFO_SECTION_CREATE(ControlArea);
|
|
}
|
|
|
|
//
|
|
// Being created has now been cleared allowing other threads
|
|
// to reference the segment. Release the resource on the file.
|
|
//
|
|
|
|
if (FileAcquired) {
|
|
IoSetTopLevelIrp((PIRP)NULL);
|
|
FsRtlReleaseFile (File);
|
|
FileAcquired = FALSE;
|
|
}
|
|
|
|
ReferenceObject:
|
|
|
|
if (ChangeFileReference) {
|
|
ObReferenceObject (FileObject);
|
|
}
|
|
|
|
//
|
|
// Now that the segment object is created, make the section object
|
|
// refer to the segment object.
|
|
//
|
|
|
|
Section.Segment = NewSegment;
|
|
Section.u.LongFlags = ControlArea->u.LongFlags;
|
|
|
|
//
|
|
// Update the count of writable user sections so the transaction semantics
|
|
// can be supported. Note that no lock synchronization is needed here as
|
|
// the transaction manager must already check for any open writable handles
|
|
// to the file - and no writable sections can be created without a writable
|
|
// file handle. So all that needs to be provided is a way for the
|
|
// transaction manager to know that there are lingering user views or
|
|
// created sections still open that have write access.
|
|
//
|
|
// This must be done before creating the object so a rogue user program
|
|
// that suspends this thread cannot subvert a transaction.
|
|
//
|
|
|
|
if ((FileObject == NULL) &&
|
|
(SectionPageProtection & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE)) &&
|
|
(ControlArea->u.Flags.Image == 0) &&
|
|
(ControlArea->FilePointer != NULL)) {
|
|
|
|
Section.u.Flags.UserWritable = 1;
|
|
|
|
InterlockedIncrement ((PLONG)&ControlArea->Segment->WritableUserReferences);
|
|
}
|
|
|
|
//
|
|
// Create the section object now. The section object is created
|
|
// now so that the error handling when the section object cannot
|
|
// be created is simplified.
|
|
//
|
|
|
|
Status = ObCreateObject (PreviousMode,
|
|
MmSectionObjectType,
|
|
ObjectAttributes,
|
|
PreviousMode,
|
|
NULL,
|
|
sizeof(SECTION),
|
|
sizeof(SECTION) +
|
|
NewSegment->TotalNumberOfPtes * sizeof(MMPTE),
|
|
sizeof(CONTROL_AREA) +
|
|
NewSegment->ControlArea->NumberOfSubsections *
|
|
sizeof(SUBSECTION),
|
|
(PVOID *)&NewSection);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
if ((FileObject == NULL) &&
|
|
(SectionPageProtection & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE)) &&
|
|
(ControlArea->u.Flags.Image == 0) &&
|
|
(ControlArea->FilePointer != NULL)) {
|
|
|
|
ASSERT (Section.u.Flags.UserWritable == 1);
|
|
|
|
InterlockedDecrement ((PLONG)&ControlArea->Segment->WritableUserReferences);
|
|
}
|
|
|
|
goto UnrefAndReturn;
|
|
}
|
|
|
|
RtlCopyMemory (NewSection, &Section, sizeof(SECTION));
|
|
NewSection->Address.StartingVpn = 0;
|
|
|
|
if (!IgnoreFileSizing) {
|
|
|
|
//
|
|
// Indicate that the cache manager is not the owner of this
|
|
// section.
|
|
//
|
|
|
|
NewSection->u.Flags.UserReference = 1;
|
|
|
|
if (AllocationAttributes & SEC_NO_CHANGE) {
|
|
|
|
//
|
|
// Indicate that once the section is mapped, no protection
|
|
// changes or freeing the mapping is allowed.
|
|
//
|
|
|
|
NewSection->u.Flags.NoChange = 1;
|
|
}
|
|
|
|
if (((SectionPageProtection & PAGE_READWRITE) |
|
|
(SectionPageProtection & PAGE_EXECUTE_READWRITE)) == 0) {
|
|
|
|
//
|
|
// This section does not support WRITE access, indicate
|
|
// that changing the protection to WRITE results in COPY_ON_WRITE.
|
|
//
|
|
|
|
NewSection->u.Flags.CopyOnWrite = 1;
|
|
}
|
|
|
|
if (AllocationAttributes & SEC_BASED) {
|
|
|
|
NewSection->u.Flags.Based = 1;
|
|
|
|
SectionBasedRoot = &MmSectionBasedRoot;
|
|
|
|
//
|
|
// This section is based at a unique address system wide.
|
|
// Ensure it does not wrap the virtual address space as the
|
|
// SECTION structure would have to widen to accomodate this and
|
|
// it's not worth the performance penalty for the very few isolated
|
|
// cases that would want this. Note that sections larger than the
|
|
// address space can easily be created - it's just that beyond a
|
|
// certain point you shouldn't specify SEC_BASED (anything this big
|
|
// couldn't use a SEC_BASED section for anything anyway).
|
|
//
|
|
|
|
if ((UINT64)NewSection->SizeOfSection.QuadPart > (UINT64)MmHighSectionBase) {
|
|
ObDereferenceObject (NewSection);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
#if defined(_WIN64)
|
|
SizeOfSection = NewSection->SizeOfSection.QuadPart;
|
|
#else
|
|
SizeOfSection = NewSection->SizeOfSection.LowPart;
|
|
#endif
|
|
|
|
//
|
|
// Get the allocation base mutex.
|
|
//
|
|
|
|
ExAcquireFastMutex (&MmSectionBasedMutex);
|
|
|
|
Status2 = MiFindEmptyAddressRangeDownTree (
|
|
SizeOfSection,
|
|
MmHighSectionBase,
|
|
X64K,
|
|
MmSectionBasedRoot,
|
|
(PVOID *)&NewSection->Address.StartingVpn);
|
|
if (!NT_SUCCESS(Status2)) {
|
|
ExReleaseFastMutex (&MmSectionBasedMutex);
|
|
ObDereferenceObject (NewSection);
|
|
return Status2;
|
|
}
|
|
|
|
NewSection->Address.EndingVpn = NewSection->Address.StartingVpn +
|
|
SizeOfSection - 1;
|
|
|
|
MiInsertBasedSection (NewSection);
|
|
ExReleaseFastMutex (&MmSectionBasedMutex);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the cache manager is creating the section, set the was
|
|
// purged flag as the file size can change.
|
|
//
|
|
|
|
ControlArea->u.Flags.WasPurged |= IgnoreFileSizing;
|
|
|
|
//
|
|
// Check to see if the section is for a data file and the size
|
|
// of the section is greater than the current size of the
|
|
// segment.
|
|
//
|
|
|
|
if (((ControlArea->u.Flags.WasPurged == 1) && (!IgnoreFileSizing)) &&
|
|
(!FileSizeChecked)
|
|
||
|
|
((UINT64)NewSection->SizeOfSection.QuadPart >
|
|
NewSection->Segment->SizeOfSegment)) {
|
|
|
|
TempSectionSize = NewSection->SizeOfSection;
|
|
|
|
NewSection->SizeOfSection.QuadPart = (LONGLONG)NewSection->Segment->SizeOfSegment;
|
|
|
|
//
|
|
// Even if the caller didn't specify extension rights, we enable it here
|
|
// temporarily to make the section correct. Use a temporary section
|
|
// instead of temporarily editing the real section to avoid opening
|
|
// a security window that other concurrent threads could exploit.
|
|
//
|
|
|
|
if (((NewSection->InitialPageProtection & PAGE_READWRITE) |
|
|
(NewSection->InitialPageProtection & PAGE_EXECUTE_READWRITE)) == 0) {
|
|
SECTION WritableSection;
|
|
|
|
*(PSECTION)&WritableSection = *NewSection;
|
|
|
|
Status = MmExtendSection (&WritableSection,
|
|
&TempSectionSize,
|
|
IgnoreFileSizing);
|
|
|
|
NewSection->SizeOfSection = WritableSection.SizeOfSection;
|
|
}
|
|
else {
|
|
Status = MmExtendSection (NewSection,
|
|
&TempSectionSize,
|
|
IgnoreFileSizing);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ObDereferenceObject (NewSection);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
*SectionObject = (PVOID)NewSection;
|
|
|
|
return Status;
|
|
|
|
UnrefAndReturn:
|
|
|
|
//
|
|
// Unreference the control area, if it was referenced and return
|
|
// the error status.
|
|
//
|
|
|
|
if (FileAcquired) {
|
|
IoSetTopLevelIrp((PIRP)NULL);
|
|
FsRtlReleaseFile (File);
|
|
}
|
|
|
|
if (IncrementedRefCount) {
|
|
LOCK_PFN (OldIrql);
|
|
ControlArea->NumberOfSectionReferences -= 1;
|
|
if (!IgnoreFileSizing) {
|
|
ASSERT ((LONG)ControlArea->NumberOfUserReferences > 0);
|
|
ControlArea->NumberOfUserReferences -= 1;
|
|
}
|
|
MiCheckControlArea (ControlArea, NULL, OldIrql);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
LOGICAL
|
|
MiMakeControlAreaRom (
|
|
IN PFILE_OBJECT File,
|
|
IN PLARGE_CONTROL_AREA ControlArea,
|
|
IN PFN_NUMBER PageFrameNumber
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function marks the control area as ROM-backed. It can fail if the
|
|
parallel control area (image vs data) is currently active as ROM-backed
|
|
as the same PFNs cannot be used for both control areas simultaneously.
|
|
|
|
Arguments:
|
|
|
|
ControlArea - Supplies the relevant control area.
|
|
|
|
PageFrameNumber - Supplies the starting physical page frame number.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the control area was marked as ROM-backed, FALSE if not.
|
|
|
|
--*/
|
|
|
|
{
|
|
LOGICAL ControlAreaMarked;
|
|
PCONTROL_AREA OtherControlArea;
|
|
KIRQL OldIrql;
|
|
|
|
ControlAreaMarked = FALSE;
|
|
|
|
LOCK_PFN (OldIrql);
|
|
|
|
if (ControlArea->u.Flags.Image == 1) {
|
|
OtherControlArea = (PCONTROL_AREA) File->SectionObjectPointer->DataSectionObject;
|
|
}
|
|
else {
|
|
OtherControlArea = (PCONTROL_AREA) File->SectionObjectPointer->ImageSectionObject;
|
|
}
|
|
|
|
//
|
|
// This could be made smarter (ie: throw away the other control area if it's
|
|
// not in use) but for now, keep it simple.
|
|
//
|
|
|
|
if ((OtherControlArea == NULL) || (OtherControlArea->u.Flags.Rom == 0)) {
|
|
ControlArea->u.Flags.Rom = 1;
|
|
ControlArea->StartingFrame = PageFrameNumber;
|
|
ControlAreaMarked = TRUE;
|
|
}
|
|
|
|
UNLOCK_PFN (OldIrql);
|
|
|
|
return ControlAreaMarked;
|
|
}
|
|
|
|
NTSTATUS
|
|
MiCreateImageFileMap (
|
|
IN PFILE_OBJECT File,
|
|
OUT PSEGMENT *Segment
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function creates the necessary structures to allow the mapping
|
|
of an image file.
|
|
|
|
The image file is opened and verified for correctness, a segment
|
|
object is created and initialized based on data in the image
|
|
header.
|
|
|
|
Arguments:
|
|
|
|
File - Supplies the file object for the image file.
|
|
|
|
Segment - Returns the segment object.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG_PTR EndingAddress;
|
|
ULONG NumberOfPtes;
|
|
ULONG SizeOfSegment;
|
|
ULONG SectionVirtualSize;
|
|
ULONG i;
|
|
ULONG j;
|
|
PCONTROL_AREA ControlArea;
|
|
PSUBSECTION Subsection;
|
|
PMMPTE PointerPte;
|
|
MMPTE TempPte;
|
|
MMPTE TempPteDemandZero;
|
|
PVOID Base;
|
|
PIMAGE_DOS_HEADER DosHeader;
|
|
PIMAGE_NT_HEADERS NtHeader;
|
|
PIMAGE_FILE_HEADER FileHeader;
|
|
ULONG SizeOfImage;
|
|
ULONG SizeOfHeaders;
|
|
#if defined (_WIN64)
|
|
PIMAGE_NT_HEADERS32 NtHeader32;
|
|
#endif
|
|
PIMAGE_DATA_DIRECTORY ComPlusDirectoryEntry;
|
|
PIMAGE_SECTION_HEADER SectionTableEntry;
|
|
PSEGMENT NewSegment;
|
|
ULONG SectorOffset;
|
|
ULONG NumberOfSubsections;
|
|
PFN_NUMBER PageFrameNumber;
|
|
PFN_NUMBER XipFrameNumber;
|
|
LOGICAL XipFile;
|
|
LOGICAL GlobalPerSession;
|
|
LARGE_INTEGER StartingOffset;
|
|
PCHAR ExtendedHeader;
|
|
PPFN_NUMBER Page;
|
|
ULONG_PTR PreferredImageBase;
|
|
ULONG_PTR NextVa;
|
|
ULONG_PTR ImageBase;
|
|
PKEVENT InPageEvent;
|
|
PMDL Mdl;
|
|
ULONG ImageFileSize;
|
|
ULONG OffsetToSectionTable;
|
|
ULONG ImageAlignment;
|
|
ULONG RoundingAlignment;
|
|
ULONG FileAlignment;
|
|
LOGICAL ImageCommit;
|
|
LOGICAL SectionCommit;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
LARGE_INTEGER EndOfFile;
|
|
ULONG NtHeaderSize;
|
|
ULONG SubsectionsAllocated;
|
|
PLARGE_CONTROL_AREA LargeControlArea;
|
|
PSUBSECTION NewSubsection;
|
|
ULONG OriginalProtection;
|
|
ULONG LoaderFlags;
|
|
ULONG TempNumberOfSubsections;
|
|
PIMAGE_SECTION_HEADER TempSectionTableEntry;
|
|
ULONG AdditionalSubsections;
|
|
ULONG AdditionalPtes;
|
|
ULONG AdditionalBasePtes;
|
|
ULONG NewSubsectionsAllocated;
|
|
PSEGMENT OldSegment;
|
|
PMMPTE NewPointerPte;
|
|
PMMPTE OldPointerPte;
|
|
ULONG OrigNumberOfPtes;
|
|
PCONTROL_AREA NewControlArea;
|
|
SIZE_T NumberOfCommittedPages;
|
|
PHYSICAL_ADDRESS PhysicalAddress;
|
|
LOGICAL ActiveDataReferences;
|
|
|
|
#if defined (_IA64_)
|
|
LOGICAL InvalidAlignmentAllowed;
|
|
|
|
InvalidAlignmentAllowed = FALSE;
|
|
#else
|
|
#define InvalidAlignmentAllowed FALSE
|
|
#endif
|
|
|
|
PAGED_CODE();
|
|
|
|
ExtendedHeader = NULL;
|
|
|
|
Status = FsRtlGetFileSize (File, &EndOfFile);
|
|
|
|
if (Status == STATUS_FILE_IS_A_DIRECTORY) {
|
|
|
|
//
|
|
// Can't map a directory as a section. Return error.
|
|
//
|
|
|
|
return STATUS_INVALID_FILE_FOR_SECTION;
|
|
}
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if (EndOfFile.HighPart != 0) {
|
|
|
|
//
|
|
// File too big. Return error.
|
|
//
|
|
|
|
return STATUS_INVALID_FILE_FOR_SECTION;
|
|
}
|
|
|
|
//
|
|
// Create a segment which maps an image file.
|
|
// For now map a COFF image file with the subsections
|
|
// containing the based address of the file.
|
|
//
|
|
|
|
//
|
|
// Read in the file header.
|
|
//
|
|
|
|
InPageEvent = ExAllocatePoolWithTag (NonPagedPool,
|
|
sizeof(KEVENT) + MmSizeOfMdl (
|
|
NULL,
|
|
MM_MAXIMUM_IMAGE_HEADER),
|
|
MMTEMPORARY);
|
|
if (InPageEvent == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Initializing IoStatus.Information is not needed for correctness, but
|
|
// without it the compiler cannot compile this code W4 to check
|
|
// for use of uninitialized variables.
|
|
//
|
|
|
|
IoStatus.Information = 0;
|
|
|
|
Mdl = (PMDL)(InPageEvent + 1);
|
|
|
|
//
|
|
// Create an event for the read operation.
|
|
//
|
|
|
|
KeInitializeEvent (InPageEvent, NotificationEvent, FALSE);
|
|
|
|
//
|
|
// Build an MDL for the operation.
|
|
//
|
|
|
|
MmCreateMdl( Mdl, NULL, PAGE_SIZE);
|
|
Mdl->MdlFlags |= MDL_PAGES_LOCKED;
|
|
|
|
PageFrameNumber = MiGetPageForHeader();
|
|
|
|
Page = (PPFN_NUMBER)(Mdl + 1);
|
|
*Page = PageFrameNumber;
|
|
|
|
ZERO_LARGE (StartingOffset);
|
|
|
|
CcZeroEndOfLastPage (File);
|
|
|
|
//
|
|
// Flush the data section if there is one.
|
|
//
|
|
// At the same time, capture whether there are any references to the
|
|
// data control area. If so, flip the pages from the image control
|
|
// area into pagefile backings to prevent anyone else's data writes
|
|
// from changing the file after it is validated (this can happen if the
|
|
// pages from the image control area need to be re-paged in later).
|
|
//
|
|
|
|
ActiveDataReferences = MiFlushDataSection (File);
|
|
|
|
Base = MiCopyHeaderIfResident (File, PageFrameNumber);
|
|
|
|
if (Base == NULL) {
|
|
Mdl->MdlFlags |= MDL_PAGES_LOCKED;
|
|
Status = IoPageRead (File,
|
|
Mdl,
|
|
&StartingOffset,
|
|
InPageEvent,
|
|
&IoStatus);
|
|
|
|
if (Status == STATUS_PENDING) {
|
|
KeWaitForSingleObject( InPageEvent,
|
|
WrPageIn,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
Status = IoStatus.Status;
|
|
}
|
|
|
|
if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) {
|
|
MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
if ((Status != STATUS_FILE_LOCK_CONFLICT) && (Status != STATUS_FILE_IS_OFFLINE)) {
|
|
Status = STATUS_INVALID_FILE_FOR_SECTION;
|
|
}
|
|
goto BadSection;
|
|
}
|
|
|
|
Base = MiMapImageHeaderInHyperSpace (PageFrameNumber);
|
|
|
|
if (IoStatus.Information != PAGE_SIZE) {
|
|
|
|
//
|
|
// A full page was not read from the file, zero any remaining
|
|
// bytes.
|
|
//
|
|
|
|
RtlZeroMemory ((PVOID)((PCHAR)Base + IoStatus.Information),
|
|
PAGE_SIZE - IoStatus.Information);
|
|
}
|
|
}
|
|
|
|
DosHeader = (PIMAGE_DOS_HEADER)Base;
|
|
|
|
//
|
|
// Check to determine if this is an NT image (PE format) or
|
|
// a DOS image, Win-16 image, or OS/2 image. If the image is
|
|
// not NT format, return an error indicating which image it
|
|
// appears to be.
|
|
//
|
|
|
|
if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
|
|
|
|
Status = STATUS_INVALID_IMAGE_NOT_MZ;
|
|
goto NeImage;
|
|
}
|
|
|
|
#ifndef i386
|
|
if (((ULONG)DosHeader->e_lfanew & 3) != 0) {
|
|
|
|
//
|
|
// The image header is not aligned on a longword boundary.
|
|
// Report this as an invalid protect mode image.
|
|
//
|
|
|
|
Status = STATUS_INVALID_IMAGE_PROTECT;
|
|
goto NeImage;
|
|
}
|
|
#endif
|
|
|
|
if ((ULONG)DosHeader->e_lfanew > EndOfFile.LowPart) {
|
|
Status = STATUS_INVALID_IMAGE_PROTECT;
|
|
goto NeImage;
|
|
}
|
|
|
|
if (((ULONG)DosHeader->e_lfanew +
|
|
sizeof(IMAGE_NT_HEADERS) +
|
|
(16 * sizeof(IMAGE_SECTION_HEADER))) <= (ULONG)DosHeader->e_lfanew) {
|
|
Status = STATUS_INVALID_IMAGE_PROTECT;
|
|
goto NeImage;
|
|
}
|
|
|
|
if (((ULONG)DosHeader->e_lfanew +
|
|
sizeof(IMAGE_NT_HEADERS) +
|
|
(16 * sizeof(IMAGE_SECTION_HEADER))) > PAGE_SIZE) {
|
|
|
|
//
|
|
// The PE header is not within the page already read or the
|
|
// objects are in another page.
|
|
// Build another MDL and read an additional 8k.
|
|
//
|
|
|
|
ExtendedHeader = ExAllocatePoolWithTag (NonPagedPool,
|
|
MM_MAXIMUM_IMAGE_HEADER,
|
|
MMTEMPORARY);
|
|
if (ExtendedHeader == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto NeImage;
|
|
}
|
|
|
|
//
|
|
// Build an MDL for the operation.
|
|
//
|
|
|
|
MmCreateMdl( Mdl, ExtendedHeader, MM_MAXIMUM_IMAGE_HEADER);
|
|
|
|
MmBuildMdlForNonPagedPool (Mdl);
|
|
|
|
StartingOffset.LowPart = PtrToUlong(PAGE_ALIGN ((ULONG)DosHeader->e_lfanew));
|
|
|
|
KeClearEvent (InPageEvent);
|
|
|
|
Status = IoPageRead (File,
|
|
Mdl,
|
|
&StartingOffset,
|
|
InPageEvent,
|
|
&IoStatus);
|
|
|
|
if (Status == STATUS_PENDING) {
|
|
|
|
KeWaitForSingleObject (InPageEvent,
|
|
WrPageIn,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
Status = IoStatus.Status;
|
|
}
|
|
|
|
if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) {
|
|
MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
if ((Status != STATUS_FILE_LOCK_CONFLICT) && (Status != STATUS_FILE_IS_OFFLINE)) {
|
|
Status = STATUS_INVALID_FILE_FOR_SECTION;
|
|
}
|
|
goto NeImage;
|
|
}
|
|
|
|
NtHeader = (PIMAGE_NT_HEADERS)((PCHAR)ExtendedHeader +
|
|
BYTE_OFFSET((ULONG)DosHeader->e_lfanew));
|
|
NtHeaderSize = MM_MAXIMUM_IMAGE_HEADER -
|
|
(ULONG)(BYTE_OFFSET((ULONG)DosHeader->e_lfanew));
|
|
}
|
|
else {
|
|
NtHeader = (PIMAGE_NT_HEADERS)((PCHAR)DosHeader +
|
|
(ULONG)DosHeader->e_lfanew);
|
|
NtHeaderSize = PAGE_SIZE - (ULONG)DosHeader->e_lfanew;
|
|
}
|
|
FileHeader = &NtHeader->FileHeader;
|
|
|
|
//
|
|
// Check to see if this is an NT image or a DOS or OS/2 image.
|
|
//
|
|
|
|
Status = MiVerifyImageHeader (NtHeader, DosHeader, NtHeaderSize);
|
|
if (Status != STATUS_SUCCESS) {
|
|
goto NeImage;
|
|
}
|
|
|
|
#if defined(_WIN64)
|
|
|
|
if (NtHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
|
|
|
|
//
|
|
// The image is 32-bit. All code below this point must check
|
|
// if NtHeader is NULL. If it is, then the image is PE32 and
|
|
// NtHeader32 must be used.
|
|
//
|
|
|
|
NtHeader32 = (PIMAGE_NT_HEADERS32)NtHeader;
|
|
NtHeader = NULL;
|
|
}
|
|
else {
|
|
NtHeader32 = NULL;
|
|
}
|
|
|
|
|
|
if (NtHeader) {
|
|
#endif
|
|
ImageAlignment = NtHeader->OptionalHeader.SectionAlignment;
|
|
FileAlignment = NtHeader->OptionalHeader.FileAlignment - 1;
|
|
SizeOfImage = NtHeader->OptionalHeader.SizeOfImage;
|
|
LoaderFlags = NtHeader->OptionalHeader.LoaderFlags;
|
|
ImageBase = NtHeader->OptionalHeader.ImageBase;
|
|
SizeOfHeaders = NtHeader->OptionalHeader.SizeOfHeaders;
|
|
|
|
//
|
|
// Read in the COM+ directory entry.
|
|
//
|
|
|
|
ComPlusDirectoryEntry = &NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR];
|
|
|
|
#if defined (_WIN64)
|
|
}
|
|
else {
|
|
ImageAlignment = NtHeader32->OptionalHeader.SectionAlignment;
|
|
FileAlignment = NtHeader32->OptionalHeader.FileAlignment - 1;
|
|
SizeOfImage = NtHeader32->OptionalHeader.SizeOfImage;
|
|
LoaderFlags = NtHeader32->OptionalHeader.LoaderFlags;
|
|
ImageBase = NtHeader32->OptionalHeader.ImageBase;
|
|
SizeOfHeaders = NtHeader32->OptionalHeader.SizeOfHeaders;
|
|
|
|
//
|
|
// Read in the COM+ directory entry.
|
|
//
|
|
|
|
ComPlusDirectoryEntry = &NtHeader32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR];
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Set the appropriate bit for .NET images inside the image's section loader flags.
|
|
//
|
|
if ((ComPlusDirectoryEntry->VirtualAddress != 0) && (ComPlusDirectoryEntry->Size != 0)) {
|
|
LoaderFlags |= IMAGE_LOADER_FLAGS_COMPLUS;
|
|
}
|
|
|
|
RoundingAlignment = ImageAlignment;
|
|
NumberOfSubsections = FileHeader->NumberOfSections;
|
|
|
|
if (ImageAlignment < PAGE_SIZE) {
|
|
|
|
//
|
|
// The image alignment is less than the page size,
|
|
// map the image with a single subsection.
|
|
//
|
|
|
|
ControlArea = ExAllocatePoolWithTag (NonPagedPool,
|
|
(ULONG)(sizeof(CONTROL_AREA) + (sizeof(SUBSECTION))),
|
|
MMCONTROL);
|
|
SubsectionsAllocated = 1;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Allocate a control area and a subsection for each section
|
|
// header plus one for the image header which has no section.
|
|
//
|
|
|
|
ControlArea = ExAllocatePoolWithTag(NonPagedPool,
|
|
(ULONG)(sizeof(CONTROL_AREA) +
|
|
(sizeof(SUBSECTION) *
|
|
(NumberOfSubsections + 1))),
|
|
'iCmM');
|
|
SubsectionsAllocated = NumberOfSubsections + 1;
|
|
}
|
|
|
|
if (ControlArea == NULL) {
|
|
|
|
//
|
|
// The requested pool could not be allocated.
|
|
//
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto NeImage;
|
|
}
|
|
|
|
//
|
|
// Zero the control area and the FIRST subsection.
|
|
//
|
|
|
|
RtlZeroMemory (ControlArea, sizeof(CONTROL_AREA) + sizeof(SUBSECTION));
|
|
|
|
ASSERT (ControlArea->u.Flags.GlobalOnlyPerSession == 0);
|
|
|
|
Subsection = (PSUBSECTION)(ControlArea + 1);
|
|
|
|
NumberOfPtes = BYTES_TO_PAGES (SizeOfImage);
|
|
|
|
if (NumberOfPtes == 0) {
|
|
ExFreePool (ControlArea);
|
|
Status = STATUS_INVALID_IMAGE_FORMAT;
|
|
goto NeImage;
|
|
}
|
|
|
|
#if defined (_WIN64)
|
|
if (NumberOfPtes >= _4gb) {
|
|
ExFreePool (ControlArea);
|
|
Status = STATUS_INVALID_IMAGE_FORMAT;
|
|
goto NeImage;
|
|
}
|
|
#endif
|
|
|
|
#if defined (_IA64_)
|
|
if ((ImageAlignment < PAGE_SIZE) &&
|
|
(KeGetPreviousMode() != KernelMode) &&
|
|
((FileHeader->Machine < USER_SHARED_DATA->ImageNumberLow) ||
|
|
(FileHeader->Machine > USER_SHARED_DATA->ImageNumberHigh))) {
|
|
|
|
InvalidAlignmentAllowed = TRUE;
|
|
}
|
|
OrigNumberOfPtes = NumberOfPtes;
|
|
#endif
|
|
|
|
SizeOfSegment = sizeof(SEGMENT) + (sizeof(MMPTE) * (NumberOfPtes - 1)) +
|
|
sizeof(SECTION_IMAGE_INFORMATION);
|
|
|
|
NewSegment = ExAllocatePoolWithTag (PagedPool, SizeOfSegment, MMSECT);
|
|
|
|
if (NewSegment == NULL) {
|
|
|
|
//
|
|
// The requested pool could not be allocated.
|
|
//
|
|
|
|
ExFreePool (ControlArea);
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto NeImage;
|
|
}
|
|
*Segment = NewSegment;
|
|
RtlZeroMemory (NewSegment, sizeof(SEGMENT));
|
|
|
|
//
|
|
// Align the prototype PTEs on the proper boundary.
|
|
//
|
|
|
|
PointerPte = &NewSegment->ThePtes[0];
|
|
i = (ULONG) (((ULONG_PTR)PointerPte >> PTE_SHIFT) &
|
|
((MM_PROTO_PTE_ALIGNMENT / PAGE_SIZE) - 1));
|
|
|
|
if (i != 0) {
|
|
i = (MM_PROTO_PTE_ALIGNMENT / PAGE_SIZE) - i;
|
|
}
|
|
|
|
NewSegment->PrototypePte = &NewSegment->ThePtes[i];
|
|
|
|
NewSegment->ControlArea = ControlArea;
|
|
NewSegment->u2.ImageInformation =
|
|
(PSECTION_IMAGE_INFORMATION)((PCHAR)NewSegment + sizeof(SEGMENT) +
|
|
(sizeof(MMPTE) * (NumberOfPtes - 1)));
|
|
NewSegment->TotalNumberOfPtes = NumberOfPtes;
|
|
NewSegment->NonExtendedPtes = NumberOfPtes;
|
|
NewSegment->SizeOfSegment = (ULONG_PTR)NumberOfPtes * PAGE_SIZE;
|
|
|
|
RtlZeroMemory (NewSegment->u2.ImageInformation,
|
|
sizeof (SECTION_IMAGE_INFORMATION));
|
|
|
|
|
|
//
|
|
// This code is built twice on the Win64 build - once for PE32+ and once for
|
|
// PE32 images.
|
|
//
|
|
#define INIT_IMAGE_INFORMATION(OptHdr) { \
|
|
NewSegment->u2.ImageInformation->TransferAddress = \
|
|
(PVOID)((ULONG_PTR)((OptHdr).ImageBase) + \
|
|
(OptHdr).AddressOfEntryPoint); \
|
|
NewSegment->u2.ImageInformation->MaximumStackSize = \
|
|
(OptHdr).SizeOfStackReserve; \
|
|
NewSegment->u2.ImageInformation->CommittedStackSize = \
|
|
(OptHdr).SizeOfStackCommit; \
|
|
NewSegment->u2.ImageInformation->SubSystemType = \
|
|
(OptHdr).Subsystem; \
|
|
NewSegment->u2.ImageInformation->SubSystemMajorVersion = (USHORT)((OptHdr).MajorSubsystemVersion); \
|
|
NewSegment->u2.ImageInformation->SubSystemMinorVersion = (USHORT)((OptHdr).MinorSubsystemVersion); \
|
|
NewSegment->u2.ImageInformation->DllCharacteristics = \
|
|
(OptHdr).DllCharacteristics; \
|
|
NewSegment->u2.ImageInformation->ImageContainsCode = \
|
|
(BOOLEAN)(((OptHdr).SizeOfCode != 0) || \
|
|
((OptHdr).AddressOfEntryPoint != 0)); \
|
|
}
|
|
|
|
#if defined (_WIN64)
|
|
if (NtHeader) {
|
|
#endif
|
|
INIT_IMAGE_INFORMATION(NtHeader->OptionalHeader);
|
|
#if defined (_WIN64)
|
|
}
|
|
else {
|
|
|
|
//
|
|
// The image is 32-bit so use the 32-bit header
|
|
//
|
|
|
|
INIT_IMAGE_INFORMATION(NtHeader32->OptionalHeader);
|
|
}
|
|
#endif
|
|
#undef INIT_IMAGE_INFORMATION
|
|
|
|
NewSegment->u2.ImageInformation->ImageCharacteristics =
|
|
FileHeader->Characteristics;
|
|
NewSegment->u2.ImageInformation->Machine =
|
|
FileHeader->Machine;
|
|
NewSegment->u2.ImageInformation->LoaderFlags =
|
|
LoaderFlags;
|
|
|
|
ControlArea->Segment = NewSegment;
|
|
ControlArea->NumberOfSectionReferences = 1;
|
|
ControlArea->NumberOfUserReferences = 1;
|
|
ControlArea->u.Flags.BeingCreated = 1;
|
|
|
|
if (ImageAlignment < PAGE_SIZE) {
|
|
|
|
//
|
|
// Image alignment is less than a page, the number
|
|
// of subsections is 1.
|
|
//
|
|
|
|
ControlArea->NumberOfSubsections = 1;
|
|
}
|
|
else {
|
|
ControlArea->NumberOfSubsections = (USHORT)NumberOfSubsections;
|
|
}
|
|
|
|
ControlArea->u.Flags.Image = 1;
|
|
ControlArea->u.Flags.File = 1;
|
|
|
|
if ((ActiveDataReferences == TRUE) ||
|
|
(IoIsDeviceEjectable(File->DeviceObject)) ||
|
|
((FileHeader->Characteristics &
|
|
IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP) &&
|
|
(FILE_REMOVABLE_MEDIA & File->DeviceObject->Characteristics)) ||
|
|
((FileHeader->Characteristics &
|
|
IMAGE_FILE_NET_RUN_FROM_SWAP) &&
|
|
(FILE_REMOTE_DEVICE & File->DeviceObject->Characteristics))) {
|
|
|
|
//
|
|
// This file resides on a floppy disk or a removable media or
|
|
// network with flags set indicating it should be copied
|
|
// to the paging file.
|
|
//
|
|
|
|
ControlArea->u.Flags.FloppyMedia = 1;
|
|
}
|
|
|
|
#if DBG
|
|
if (MiMakeImageFloppy[0] & 0x1) {
|
|
MiMakeImageFloppy[1] += 1;
|
|
ControlArea->u.Flags.FloppyMedia = 1;
|
|
}
|
|
#endif
|
|
|
|
if (FILE_REMOTE_DEVICE & File->DeviceObject->Characteristics) {
|
|
|
|
//
|
|
// This file resides on a redirected drive.
|
|
//
|
|
|
|
ControlArea->u.Flags.Networked = 1;
|
|
}
|
|
|
|
ControlArea->FilePointer = File;
|
|
|
|
//
|
|
// Build the subsection and prototype PTEs for the image header.
|
|
//
|
|
|
|
Subsection->ControlArea = ControlArea;
|
|
NextVa = ImageBase;
|
|
|
|
#if defined (_WIN64)
|
|
|
|
//
|
|
// Don't let bogus headers cause system alignment faults.
|
|
//
|
|
|
|
if (FileHeader->SizeOfOptionalHeader & (sizeof (ULONG_PTR) - 1)) {
|
|
goto BadPeImageSegment;
|
|
}
|
|
#endif
|
|
|
|
if ((NextVa & (X64K - 1)) != 0) {
|
|
|
|
//
|
|
// Image header is not aligned on a 64k boundary.
|
|
//
|
|
|
|
goto BadPeImageSegment;
|
|
}
|
|
|
|
NewSegment->BasedAddress = (PVOID)NextVa;
|
|
|
|
if (SizeOfHeaders >= SizeOfImage) {
|
|
goto BadPeImageSegment;
|
|
}
|
|
|
|
Subsection->PtesInSubsection = MI_ROUND_TO_SIZE (
|
|
SizeOfHeaders,
|
|
ImageAlignment
|
|
) >> PAGE_SHIFT;
|
|
|
|
PointerPte = NewSegment->PrototypePte;
|
|
Subsection->SubsectionBase = PointerPte;
|
|
|
|
TempPte.u.Long = MiGetSubsectionAddressForPte (Subsection);
|
|
TempPte.u.Soft.Prototype = 1;
|
|
|
|
NewSegment->SegmentPteTemplate = TempPte;
|
|
SectorOffset = 0;
|
|
|
|
if (ImageAlignment < PAGE_SIZE) {
|
|
|
|
//
|
|
// Aligned on less than a page size boundary.
|
|
// Build a single subsection to refer to the image.
|
|
//
|
|
|
|
PointerPte = NewSegment->PrototypePte;
|
|
|
|
Subsection->PtesInSubsection = NumberOfPtes;
|
|
|
|
#if !defined (_WIN64)
|
|
|
|
//
|
|
// Note this only needs to be checked for 32-bit systems as NT64
|
|
// does a much more extensive reallocation of images that are built
|
|
// with alignment less than PAGE_SIZE.
|
|
//
|
|
|
|
if ((UINT64)SizeOfImage < (UINT64)EndOfFile.QuadPart) {
|
|
|
|
//
|
|
// Images that have a size of image (according to the header) that's
|
|
// smaller than the actual file only get that many prototype PTEs.
|
|
// Initialize the subsection properly so no one can read off the
|
|
// end as that would corrupt the PFN database element's original
|
|
// PTE entry.
|
|
//
|
|
|
|
Subsection->NumberOfFullSectors = (SizeOfImage >> MMSECTOR_SHIFT);
|
|
|
|
Subsection->u.SubsectionFlags.SectorEndOffset =
|
|
SizeOfImage & MMSECTOR_MASK;
|
|
}
|
|
else {
|
|
#endif
|
|
Subsection->NumberOfFullSectors =
|
|
(ULONG)(EndOfFile.QuadPart >> MMSECTOR_SHIFT);
|
|
|
|
ASSERT ((ULONG)(EndOfFile.HighPart & 0xFFFFF000) == 0);
|
|
|
|
Subsection->u.SubsectionFlags.SectorEndOffset =
|
|
EndOfFile.LowPart & MMSECTOR_MASK;
|
|
#if !defined (_WIN64)
|
|
}
|
|
#endif
|
|
|
|
Subsection->u.SubsectionFlags.Protection = MM_EXECUTE_WRITECOPY;
|
|
|
|
//
|
|
// Set all the PTEs to the execute-read-write protection.
|
|
// The section will control access to these and the segment
|
|
// must provide a method to allow other users to map the file
|
|
// for various protections.
|
|
//
|
|
|
|
TempPte.u.Soft.Protection = MM_EXECUTE_WRITECOPY;
|
|
|
|
NewSegment->SegmentPteTemplate = TempPte;
|
|
|
|
//
|
|
// Invalid image alignments are supported for cross platform
|
|
// emulation. Only IA64 requires extra handling because
|
|
// the native page size is larger than x86.
|
|
//
|
|
|
|
if (InvalidAlignmentAllowed == TRUE) {
|
|
|
|
TempPteDemandZero.u.Long = 0;
|
|
TempPteDemandZero.u.Soft.Protection = MM_EXECUTE_WRITECOPY;
|
|
SectorOffset = 0;
|
|
|
|
for (i = 0; i < NumberOfPtes; i += 1) {
|
|
|
|
//
|
|
// Set prototype PTEs.
|
|
//
|
|
|
|
if (SectorOffset < EndOfFile.LowPart) {
|
|
|
|
//
|
|
// Data resides on the disk, refer to the control section.
|
|
//
|
|
|
|
MI_WRITE_INVALID_PTE (PointerPte, TempPte);
|
|
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Data does not reside on the disk, use Demand zero pages.
|
|
//
|
|
|
|
MI_WRITE_INVALID_PTE (PointerPte, TempPteDemandZero);
|
|
}
|
|
|
|
SectorOffset += PAGE_SIZE;
|
|
PointerPte += 1;
|
|
}
|
|
|
|
}
|
|
else {
|
|
|
|
for (i = 0; i < NumberOfPtes; i += 1) {
|
|
|
|
//
|
|
// Set all the prototype PTEs to refer to the control section.
|
|
//
|
|
|
|
MI_WRITE_INVALID_PTE (PointerPte, TempPte);
|
|
PointerPte += 1;
|
|
}
|
|
}
|
|
|
|
NewSegment->u1.ImageCommitment = NumberOfPtes;
|
|
|
|
|
|
//
|
|
// Indicate alignment is less than a page.
|
|
//
|
|
|
|
TempPte.u.Long = 0;
|
|
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Alignment is PAGE_SIZE or greater.
|
|
//
|
|
|
|
if (Subsection->PtesInSubsection > NumberOfPtes) {
|
|
|
|
//
|
|
// Inconsistent image, size does not agree with header.
|
|
//
|
|
|
|
goto BadPeImageSegment;
|
|
}
|
|
NumberOfPtes -= Subsection->PtesInSubsection;
|
|
|
|
Subsection->NumberOfFullSectors =
|
|
SizeOfHeaders >> MMSECTOR_SHIFT;
|
|
|
|
Subsection->u.SubsectionFlags.SectorEndOffset =
|
|
SizeOfHeaders & MMSECTOR_MASK;
|
|
|
|
Subsection->u.SubsectionFlags.ReadOnly = 1;
|
|
Subsection->u.SubsectionFlags.Protection = MM_READONLY;
|
|
|
|
TempPte.u.Soft.Protection = MM_READONLY;
|
|
NewSegment->SegmentPteTemplate = TempPte;
|
|
|
|
for (i = 0; i < Subsection->PtesInSubsection; i += 1) {
|
|
|
|
//
|
|
// Set all the prototype PTEs to refer to the control section.
|
|
//
|
|
|
|
if (SectorOffset < SizeOfHeaders) {
|
|
MI_WRITE_INVALID_PTE (PointerPte, TempPte);
|
|
}
|
|
else {
|
|
MI_WRITE_INVALID_PTE (PointerPte, ZeroPte);
|
|
}
|
|
SectorOffset += PAGE_SIZE;
|
|
PointerPte += 1;
|
|
NextVa += PAGE_SIZE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Build the additional subsections.
|
|
//
|
|
|
|
PreferredImageBase = ImageBase;
|
|
|
|
//
|
|
// At this point the object table is read in (if it was not
|
|
// already read in) and may displace the image header.
|
|
//
|
|
|
|
SectionTableEntry = NULL;
|
|
OffsetToSectionTable = sizeof(ULONG) +
|
|
sizeof(IMAGE_FILE_HEADER) +
|
|
FileHeader->SizeOfOptionalHeader;
|
|
|
|
if ((BYTE_OFFSET(NtHeader) + OffsetToSectionTable +
|
|
#if defined (_WIN64)
|
|
BYTE_OFFSET(NtHeader32) +
|
|
#endif
|
|
((NumberOfSubsections + 1) *
|
|
sizeof (IMAGE_SECTION_HEADER))) <= PAGE_SIZE) {
|
|
|
|
//
|
|
// Section tables are within the header which was read.
|
|
//
|
|
|
|
#if defined(_WIN64)
|
|
if (NtHeader32) {
|
|
SectionTableEntry = (PIMAGE_SECTION_HEADER)((PCHAR)NtHeader32 +
|
|
OffsetToSectionTable);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
SectionTableEntry = (PIMAGE_SECTION_HEADER)((PCHAR)NtHeader +
|
|
OffsetToSectionTable);
|
|
}
|
|
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Has an extended header been read in and are the object
|
|
// tables resident?
|
|
//
|
|
|
|
if (ExtendedHeader != NULL) {
|
|
|
|
#if defined(_WIN64)
|
|
if (NtHeader32) {
|
|
SectionTableEntry = (PIMAGE_SECTION_HEADER)((PCHAR)NtHeader32 +
|
|
OffsetToSectionTable);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
SectionTableEntry = (PIMAGE_SECTION_HEADER)((PCHAR)NtHeader +
|
|
OffsetToSectionTable);
|
|
}
|
|
|
|
//
|
|
// Is the whole range of object tables mapped by the
|
|
// extended header?
|
|
//
|
|
|
|
if ((((PCHAR)SectionTableEntry +
|
|
((NumberOfSubsections + 1) *
|
|
sizeof (IMAGE_SECTION_HEADER))) -
|
|
(PCHAR)ExtendedHeader) >
|
|
MM_MAXIMUM_IMAGE_HEADER) {
|
|
SectionTableEntry = NULL;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if (SectionTableEntry == NULL) {
|
|
|
|
//
|
|
// The section table entries are not in the same
|
|
// pages as the other data already read in. Read in
|
|
// the object table entries.
|
|
//
|
|
|
|
if (ExtendedHeader == NULL) {
|
|
ExtendedHeader = ExAllocatePoolWithTag (NonPagedPool,
|
|
MM_MAXIMUM_IMAGE_HEADER,
|
|
MMTEMPORARY);
|
|
if (ExtendedHeader == NULL) {
|
|
ExFreePool (NewSegment);
|
|
ExFreePool (ControlArea);
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto NeImage;
|
|
}
|
|
|
|
//
|
|
// Build an MDL for the operation.
|
|
//
|
|
|
|
MmCreateMdl( Mdl, ExtendedHeader, MM_MAXIMUM_IMAGE_HEADER);
|
|
|
|
MmBuildMdlForNonPagedPool (Mdl);
|
|
}
|
|
|
|
StartingOffset.LowPart = PtrToUlong(PAGE_ALIGN (
|
|
(ULONG)DosHeader->e_lfanew +
|
|
OffsetToSectionTable));
|
|
|
|
SectionTableEntry = (PIMAGE_SECTION_HEADER)((PCHAR)ExtendedHeader +
|
|
BYTE_OFFSET((ULONG)DosHeader->e_lfanew +
|
|
OffsetToSectionTable));
|
|
|
|
KeClearEvent (InPageEvent);
|
|
Status = IoPageRead (File,
|
|
Mdl,
|
|
&StartingOffset,
|
|
InPageEvent,
|
|
&IoStatus);
|
|
|
|
if (Status == STATUS_PENDING) {
|
|
KeWaitForSingleObject( InPageEvent,
|
|
WrPageIn,
|
|
KernelMode,
|
|
FALSE,
|
|
(PLARGE_INTEGER)NULL);
|
|
Status = IoStatus.Status;
|
|
}
|
|
|
|
if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) {
|
|
MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
if ((Status != STATUS_FILE_LOCK_CONFLICT) && (Status != STATUS_FILE_IS_OFFLINE)) {
|
|
Status = STATUS_INVALID_FILE_FOR_SECTION;
|
|
}
|
|
ExFreePool (NewSegment);
|
|
ExFreePool (ControlArea);
|
|
goto NeImage;
|
|
}
|
|
|
|
//
|
|
// From this point on NtHeader is only valid if it
|
|
// was in the first page of the image, otherwise reading in
|
|
// the object tables wiped it out.
|
|
//
|
|
}
|
|
|
|
if ((TempPte.u.Long == 0) && (InvalidAlignmentAllowed == FALSE)) {
|
|
|
|
//
|
|
// The image header is no longer valid, TempPte is
|
|
// used to indicate that this image alignment is
|
|
// less than a PAGE_SIZE.
|
|
//
|
|
// Loop through all sections and make sure there is no
|
|
// uninitialized data.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
while (NumberOfSubsections > 0) {
|
|
if (SectionTableEntry->Misc.VirtualSize == 0) {
|
|
SectionVirtualSize = SectionTableEntry->SizeOfRawData;
|
|
}
|
|
else {
|
|
SectionVirtualSize = SectionTableEntry->Misc.VirtualSize;
|
|
}
|
|
|
|
//
|
|
// If the raw pointer + raw size overflows a long word,
|
|
// return an error.
|
|
//
|
|
|
|
if (SectionTableEntry->PointerToRawData +
|
|
SectionTableEntry->SizeOfRawData <
|
|
SectionTableEntry->PointerToRawData) {
|
|
|
|
KdPrint(("MMCREASECT: invalid section/file size %Z\n",
|
|
&File->FileName));
|
|
|
|
Status = STATUS_INVALID_IMAGE_FORMAT;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If the virtual size and address does not match the rawdata
|
|
// and invalid alignments not allowed return an error.
|
|
//
|
|
|
|
if (((SectionTableEntry->PointerToRawData !=
|
|
SectionTableEntry->VirtualAddress))
|
|
||
|
|
(SectionVirtualSize > SectionTableEntry->SizeOfRawData)) {
|
|
|
|
KdPrint(("MMCREASECT: invalid BSS/Trailingzero %Z\n",
|
|
&File->FileName));
|
|
|
|
Status = STATUS_INVALID_IMAGE_FORMAT;
|
|
break;
|
|
}
|
|
|
|
SectionTableEntry += 1;
|
|
NumberOfSubsections -= 1;
|
|
}
|
|
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ExFreePool (NewSegment);
|
|
ExFreePool (ControlArea);
|
|
goto NeImage;
|
|
}
|
|
|
|
goto PeReturnSuccess;
|
|
|
|
}
|
|
else if ((TempPte.u.Long == 0) && (InvalidAlignmentAllowed == TRUE)) {
|
|
|
|
TempNumberOfSubsections = NumberOfSubsections;
|
|
TempSectionTableEntry = SectionTableEntry;
|
|
|
|
//
|
|
// The image header is no longer valid, TempPte is
|
|
// used to indicate that this image alignment is
|
|
// less than a PAGE_SIZE.
|
|
//
|
|
|
|
//
|
|
// Loop through all sections and make sure there is no
|
|
// uninitialized data.
|
|
//
|
|
// Also determine if there are shared data sections and
|
|
// the number of extra ptes they require.
|
|
//
|
|
|
|
AdditionalSubsections = 0;
|
|
AdditionalPtes = 0;
|
|
AdditionalBasePtes = 0;
|
|
RoundingAlignment = PAGE_SIZE;
|
|
|
|
while (TempNumberOfSubsections > 0) {
|
|
ULONG EndOfSection;
|
|
ULONG ExtraPages;
|
|
|
|
if (TempSectionTableEntry->Misc.VirtualSize == 0) {
|
|
SectionVirtualSize = TempSectionTableEntry->SizeOfRawData;
|
|
}
|
|
else {
|
|
SectionVirtualSize = TempSectionTableEntry->Misc.VirtualSize;
|
|
}
|
|
|
|
EndOfSection = TempSectionTableEntry->PointerToRawData +
|
|
TempSectionTableEntry->SizeOfRawData;
|
|
|
|
//
|
|
// If the raw pointer + raw size overflows a long word, return an error.
|
|
//
|
|
|
|
if (EndOfSection < TempSectionTableEntry->PointerToRawData) {
|
|
|
|
KdPrint(("MMCREASECT: invalid section/file size %Z\n",
|
|
&File->FileName));
|
|
|
|
Status = STATUS_INVALID_IMAGE_FORMAT;
|
|
|
|
ExFreePool (NewSegment);
|
|
ExFreePool (ControlArea);
|
|
goto NeImage;
|
|
}
|
|
|
|
//
|
|
// If the section goes past SizeOfImage then allocate
|
|
// additional PTEs. On x86 this is handled by the subsection
|
|
// mapping. Note the additional data must be in memory so
|
|
// it can be shuffled around later.
|
|
//
|
|
|
|
if ((EndOfSection <= EndOfFile.LowPart) &&
|
|
(EndOfSection > SizeOfImage)) {
|
|
|
|
//
|
|
// Allocate enough PTEs to cover the end of this section.
|
|
// Maximize with any other sections that extend beyond SizeOfImage
|
|
//
|
|
|
|
ExtraPages = MI_ROUND_TO_SIZE (EndOfSection, RoundingAlignment) >> PAGE_SHIFT;
|
|
if ((ExtraPages > OrigNumberOfPtes) &&
|
|
(ExtraPages - OrigNumberOfPtes > AdditionalBasePtes)) {
|
|
|
|
AdditionalBasePtes = ExtraPages - (ULONG) OrigNumberOfPtes;
|
|
}
|
|
}
|
|
|
|
// Count number of shared data sections and additional ptes needed
|
|
|
|
if ((TempSectionTableEntry->Characteristics & IMAGE_SCN_MEM_SHARED) &&
|
|
(!(TempSectionTableEntry->Characteristics & IMAGE_SCN_MEM_EXECUTE) ||
|
|
(TempSectionTableEntry->Characteristics & IMAGE_SCN_MEM_WRITE))) {
|
|
AdditionalPtes +=
|
|
MI_ROUND_TO_SIZE (SectionVirtualSize, RoundingAlignment) >>
|
|
PAGE_SHIFT;
|
|
AdditionalSubsections += 1;
|
|
}
|
|
|
|
TempSectionTableEntry += 1;
|
|
TempNumberOfSubsections -= 1;
|
|
}
|
|
|
|
if (AdditionalBasePtes == 0 && (AdditionalSubsections == 0 || AdditionalPtes == 0)) {
|
|
// no shared data sections
|
|
goto PeReturnSuccess;
|
|
}
|
|
|
|
//
|
|
// There are additional Base PTEs or shared data sections.
|
|
// For shared sections, allocate new PTEs for these sections
|
|
// at the end of the image. The WX86 loader will change
|
|
// fixups to point to the new pages.
|
|
//
|
|
// First reallocate the control area.
|
|
//
|
|
|
|
NewSubsectionsAllocated = SubsectionsAllocated +
|
|
AdditionalSubsections;
|
|
|
|
NewControlArea = ExAllocatePoolWithTag(NonPagedPool,
|
|
(ULONG) (sizeof(CONTROL_AREA) +
|
|
(sizeof(SUBSECTION) *
|
|
NewSubsectionsAllocated)),
|
|
'iCmM');
|
|
if (NewControlArea == NULL) {
|
|
ExFreePool (NewSegment);
|
|
ExFreePool (ControlArea);
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto NeImage;
|
|
}
|
|
|
|
//
|
|
// Copy the old control area to the new one, modify some fields.
|
|
//
|
|
|
|
RtlCopyMemory (NewControlArea, ControlArea,
|
|
sizeof(CONTROL_AREA) +
|
|
sizeof(SUBSECTION) * SubsectionsAllocated);
|
|
|
|
NewControlArea->NumberOfSubsections = (USHORT) NewSubsectionsAllocated;
|
|
|
|
//
|
|
// Now allocate a new segment that has the newly calculated number
|
|
// of PTEs, initialize it from the previously allocated new segment,
|
|
// and overwrite the fields that should be changed.
|
|
//
|
|
|
|
OldSegment = NewSegment;
|
|
|
|
|
|
OrigNumberOfPtes += AdditionalBasePtes;
|
|
PointerPte += AdditionalBasePtes;
|
|
|
|
SizeOfSegment = sizeof(SEGMENT) +
|
|
(sizeof(MMPTE) * (OrigNumberOfPtes + AdditionalPtes - 1)) +
|
|
sizeof(SECTION_IMAGE_INFORMATION);
|
|
|
|
NewSegment = ExAllocatePoolWithTag (PagedPool,
|
|
SizeOfSegment,
|
|
MMSECT);
|
|
|
|
if (NewSegment == NULL) {
|
|
|
|
//
|
|
// The requested pool could not be allocated.
|
|
//
|
|
|
|
ExFreePool (ControlArea);
|
|
ExFreePool (NewControlArea);
|
|
ExFreePool (OldSegment);
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto NeImage;
|
|
}
|
|
|
|
*Segment = NewSegment;
|
|
RtlCopyMemory (NewSegment, OldSegment, sizeof(SEGMENT));
|
|
|
|
//
|
|
// Align the prototype PTEs on the proper boundary.
|
|
//
|
|
|
|
NewPointerPte = &NewSegment->ThePtes[0];
|
|
i = (ULONG) (((ULONG_PTR)NewPointerPte >> PTE_SHIFT) &
|
|
((MM_PROTO_PTE_ALIGNMENT / PAGE_SIZE) - 1));
|
|
|
|
if (i != 0) {
|
|
i = (MM_PROTO_PTE_ALIGNMENT / PAGE_SIZE) - i;
|
|
}
|
|
|
|
NewSegment->PrototypePte = &NewSegment->ThePtes[i];
|
|
if (i != 0) {
|
|
RtlZeroMemory (&NewSegment->ThePtes[0], sizeof(MMPTE) * i);
|
|
}
|
|
PointerPte = NewSegment->PrototypePte +
|
|
(PointerPte - OldSegment->PrototypePte);
|
|
|
|
NewSegment->ControlArea = NewControlArea;
|
|
NewSegment->u2.ImageInformation =
|
|
(PSECTION_IMAGE_INFORMATION)((PCHAR)NewSegment + sizeof(SEGMENT) +
|
|
(sizeof(MMPTE) * (OrigNumberOfPtes + AdditionalPtes - 1)));
|
|
NewSegment->TotalNumberOfPtes = OrigNumberOfPtes + AdditionalPtes;
|
|
NewSegment->NonExtendedPtes = OrigNumberOfPtes + AdditionalPtes;
|
|
NewSegment->SizeOfSegment = (ULONG_PTR)(OrigNumberOfPtes + AdditionalPtes) * PAGE_SIZE;
|
|
|
|
RtlCopyMemory (NewSegment->u2.ImageInformation,
|
|
OldSegment->u2.ImageInformation,
|
|
sizeof (SECTION_IMAGE_INFORMATION));
|
|
|
|
//
|
|
// Now change the fields in the subsections to account for the new
|
|
// control area and the new segment. Also change the PTEs in the
|
|
// newly allocated segment to point to the new subsections.
|
|
//
|
|
|
|
NewControlArea->Segment = NewSegment;
|
|
|
|
Subsection = (PSUBSECTION)(ControlArea + 1);
|
|
NewSubsection = (PSUBSECTION)(NewControlArea + 1);
|
|
NewSubsection->PtesInSubsection += AdditionalBasePtes;
|
|
|
|
for (i = 0; i < SubsectionsAllocated; i += 1) {
|
|
|
|
//
|
|
// Note: SubsectionsAllocated is always 1 (for wx86), so this loop
|
|
// is executed only once.
|
|
//
|
|
|
|
NewSubsection->ControlArea = (PCONTROL_AREA) NewControlArea;
|
|
|
|
NewSubsection->SubsectionBase = NewSegment->PrototypePte +
|
|
(Subsection->SubsectionBase - OldSegment->PrototypePte);
|
|
|
|
NewPointerPte = NewSegment->PrototypePte;
|
|
OldPointerPte = OldSegment->PrototypePte;
|
|
|
|
TempPte.u.Long = MiGetSubsectionAddressForPte (NewSubsection);
|
|
TempPte.u.Soft.Prototype = 1;
|
|
|
|
for (j = 0; j < OldSegment->TotalNumberOfPtes+AdditionalBasePtes; j += 1) {
|
|
|
|
if ((OldPointerPte->u.Soft.Prototype == 1) &&
|
|
(MiGetSubsectionAddress (OldPointerPte) == Subsection)) {
|
|
OriginalProtection = MI_GET_PROTECTION_FROM_SOFT_PTE (OldPointerPte);
|
|
TempPte.u.Soft.Protection = OriginalProtection;
|
|
MI_WRITE_INVALID_PTE (NewPointerPte, TempPte);
|
|
}
|
|
else if (i == 0) {
|
|
|
|
//
|
|
// Since the outer for loop is executed only once, there
|
|
// is no need for the i == 0 above, but it is safer to
|
|
// have it. If the code changes later and other sections
|
|
// are added, their PTEs will get initialized here as
|
|
// DemandZero and if they are not DemandZero, they will be
|
|
// overwritten in a later iteration of the outer loop.
|
|
// For now, this else if clause will be executed only
|
|
// for DemandZero PTEs.
|
|
//
|
|
|
|
OriginalProtection = MI_GET_PROTECTION_FROM_SOFT_PTE (OldPointerPte);
|
|
TempPteDemandZero.u.Long = 0;
|
|
TempPteDemandZero.u.Soft.Protection = OriginalProtection;
|
|
MI_WRITE_INVALID_PTE (NewPointerPte, TempPteDemandZero);
|
|
}
|
|
|
|
NewPointerPte += 1;
|
|
// Stop incrementing the OldPointerPte at the last entry and use it
|
|
// for the additional Base PTEs
|
|
if (j < OldSegment->TotalNumberOfPtes-1) {
|
|
OldPointerPte += 1;
|
|
}
|
|
}
|
|
|
|
Subsection += 1;
|
|
NewSubsection += 1;
|
|
}
|
|
|
|
|
|
RtlZeroMemory (NewSubsection,
|
|
sizeof(SUBSECTION) * AdditionalSubsections);
|
|
|
|
ExFreePool (OldSegment);
|
|
ExFreePool (ControlArea);
|
|
ControlArea = (PCONTROL_AREA) NewControlArea;
|
|
|
|
//
|
|
// Adjust some variables that are used below.
|
|
// PointerPte has already been set above before OldSegment was freed.
|
|
//
|
|
|
|
SubsectionsAllocated = NewSubsectionsAllocated;
|
|
Subsection = NewSubsection - 1; // Points to last used subsection.
|
|
NumberOfPtes = AdditionalPtes; // # PTEs that haven't yet been used in
|
|
// previous subsections.
|
|
|
|
//
|
|
// Additional Base PTEs have been added. Only continue if there are
|
|
// additional subsections to process.
|
|
//
|
|
|
|
if (AdditionalSubsections == 0 || AdditionalPtes == 0) {
|
|
// no shared data sections
|
|
goto PeReturnSuccess;
|
|
}
|
|
}
|
|
|
|
ImageFileSize = EndOfFile.LowPart + 1;
|
|
|
|
while (NumberOfSubsections > 0) {
|
|
|
|
if ((InvalidAlignmentAllowed == FALSE) ||
|
|
((SectionTableEntry->Characteristics & IMAGE_SCN_MEM_SHARED) &&
|
|
(!(SectionTableEntry->Characteristics & IMAGE_SCN_MEM_EXECUTE) ||
|
|
(SectionTableEntry->Characteristics & IMAGE_SCN_MEM_WRITE)))) {
|
|
|
|
//
|
|
// Handle case where virtual size is 0.
|
|
//
|
|
|
|
if (SectionTableEntry->Misc.VirtualSize == 0) {
|
|
SectionVirtualSize = SectionTableEntry->SizeOfRawData;
|
|
}
|
|
else {
|
|
SectionVirtualSize = SectionTableEntry->Misc.VirtualSize;
|
|
}
|
|
|
|
//
|
|
// Fix for Borland linker problem. The SizeOfRawData can
|
|
// be a zero, but the PointerToRawData is not zero.
|
|
// Set it to zero.
|
|
//
|
|
|
|
if (SectionTableEntry->SizeOfRawData == 0) {
|
|
SectionTableEntry->PointerToRawData = 0;
|
|
}
|
|
|
|
//
|
|
// If the section information wraps return an error.
|
|
//
|
|
|
|
if (SectionTableEntry->PointerToRawData +
|
|
SectionTableEntry->SizeOfRawData <
|
|
SectionTableEntry->PointerToRawData) {
|
|
|
|
goto BadPeImageSegment;
|
|
}
|
|
|
|
Subsection->NextSubsection = (Subsection + 1);
|
|
|
|
Subsection += 1;
|
|
Subsection->ControlArea = ControlArea;
|
|
Subsection->NextSubsection = NULL;
|
|
Subsection->UnusedPtes = 0;
|
|
|
|
if (((NextVa != (PreferredImageBase + SectionTableEntry->VirtualAddress)) && (InvalidAlignmentAllowed == FALSE)) ||
|
|
(SectionVirtualSize == 0)) {
|
|
|
|
//
|
|
// The specified virtual address does not align
|
|
// with the next prototype PTE.
|
|
//
|
|
|
|
goto BadPeImageSegment;
|
|
}
|
|
|
|
Subsection->PtesInSubsection =
|
|
MI_ROUND_TO_SIZE (SectionVirtualSize, RoundingAlignment)
|
|
>> PAGE_SHIFT;
|
|
|
|
if (Subsection->PtesInSubsection > NumberOfPtes) {
|
|
|
|
//
|
|
// Inconsistent image, size does not agree with object tables.
|
|
//
|
|
|
|
goto BadPeImageSegment;
|
|
}
|
|
NumberOfPtes -= Subsection->PtesInSubsection;
|
|
|
|
Subsection->u.LongFlags = 0;
|
|
Subsection->StartingSector =
|
|
SectionTableEntry->PointerToRawData >> MMSECTOR_SHIFT;
|
|
|
|
//
|
|
// Align ending sector on file align boundary.
|
|
//
|
|
|
|
EndingAddress = (SectionTableEntry->PointerToRawData +
|
|
SectionTableEntry->SizeOfRawData +
|
|
FileAlignment) & ~FileAlignment;
|
|
|
|
Subsection->NumberOfFullSectors = (ULONG)
|
|
((EndingAddress >> MMSECTOR_SHIFT) -
|
|
Subsection->StartingSector);
|
|
|
|
Subsection->u.SubsectionFlags.SectorEndOffset =
|
|
(ULONG) EndingAddress & MMSECTOR_MASK;
|
|
|
|
Subsection->SubsectionBase = PointerPte;
|
|
|
|
//
|
|
// Build both a demand zero PTE and a PTE pointing to the
|
|
// subsection.
|
|
//
|
|
|
|
TempPte.u.Long = 0;
|
|
TempPteDemandZero.u.Long = 0;
|
|
|
|
TempPte.u.Long = MiGetSubsectionAddressForPte (Subsection);
|
|
TempPte.u.Soft.Prototype = 1;
|
|
ImageFileSize = SectionTableEntry->PointerToRawData +
|
|
SectionTableEntry->SizeOfRawData;
|
|
TempPte.u.Soft.Protection =
|
|
MiGetImageProtection (SectionTableEntry->Characteristics);
|
|
TempPteDemandZero.u.Soft.Protection = TempPte.u.Soft.Protection;
|
|
|
|
if (SectionTableEntry->PointerToRawData == 0) {
|
|
TempPte = TempPteDemandZero;
|
|
}
|
|
|
|
Subsection->u.SubsectionFlags.ReadOnly = 1;
|
|
Subsection->u.SubsectionFlags.Protection = MI_GET_PROTECTION_FROM_SOFT_PTE (&TempPte);
|
|
|
|
//
|
|
// Assume the subsection will be unwritable and therefore
|
|
// won't be charged for any commitment.
|
|
//
|
|
|
|
SectionCommit = FALSE;
|
|
ImageCommit = FALSE;
|
|
|
|
if (TempPte.u.Soft.Protection & MM_PROTECTION_WRITE_MASK) {
|
|
if ((TempPte.u.Soft.Protection & MM_COPY_ON_WRITE_MASK)
|
|
== MM_COPY_ON_WRITE_MASK) {
|
|
|
|
//
|
|
// This page is copy on write, charge ImageCommitment
|
|
// for all pages in this subsection.
|
|
//
|
|
|
|
ImageCommit = TRUE;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// This page is write shared, charge commitment when
|
|
// the mapping completes.
|
|
//
|
|
|
|
SectionCommit = TRUE;
|
|
Subsection->u.SubsectionFlags.GlobalMemory = 1;
|
|
ControlArea->u.Flags.GlobalMemory = 1;
|
|
}
|
|
}
|
|
|
|
NewSegment->SegmentPteTemplate = TempPte;
|
|
SectorOffset = 0;
|
|
|
|
for (i = 0; i < Subsection->PtesInSubsection; i += 1) {
|
|
|
|
//
|
|
// Set all the prototype PTEs to refer to the control section.
|
|
//
|
|
|
|
if (SectorOffset < SectionVirtualSize) {
|
|
|
|
//
|
|
// Make PTE accessible.
|
|
//
|
|
|
|
if (SectionCommit) {
|
|
NewSegment->NumberOfCommittedPages += 1;
|
|
}
|
|
if (ImageCommit) {
|
|
NewSegment->u1.ImageCommitment += 1;
|
|
}
|
|
|
|
if (SectorOffset < SectionTableEntry->SizeOfRawData) {
|
|
|
|
//
|
|
// Data resides on the disk, use the subsection format PTE.
|
|
//
|
|
MI_WRITE_INVALID_PTE (PointerPte, TempPte);
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Demand zero pages.
|
|
//
|
|
MI_WRITE_INVALID_PTE (PointerPte, TempPteDemandZero);
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// No access pages.
|
|
//
|
|
|
|
MI_WRITE_INVALID_PTE (PointerPte, ZeroPte);
|
|
}
|
|
SectorOffset += PAGE_SIZE;
|
|
PointerPte += 1;
|
|
NextVa += PAGE_SIZE;
|
|
}
|
|
}
|
|
|
|
SectionTableEntry += 1;
|
|
NumberOfSubsections -= 1;
|
|
}
|
|
|
|
if (InvalidAlignmentAllowed == FALSE) {
|
|
|
|
//
|
|
// Account for the number of subsections that really are mapped.
|
|
//
|
|
|
|
ASSERT (ImageAlignment >= PAGE_SIZE);
|
|
ControlArea->NumberOfSubsections += 1;
|
|
|
|
//
|
|
// If the file size is not as big as the image claimed to be,
|
|
// return an error.
|
|
//
|
|
|
|
if (ImageFileSize > EndOfFile.LowPart) {
|
|
|
|
//
|
|
// Invalid image size.
|
|
//
|
|
|
|
KdPrint(("MMCREASECT: invalid image size - file size %lx - image size %lx\n %Z\n",
|
|
EndOfFile.LowPart, ImageFileSize, &File->FileName));
|
|
goto BadPeImageSegment;
|
|
}
|
|
|
|
//
|
|
// The total number of PTEs was decremented as sections were built,
|
|
// make sure that there are less than 64k's worth at this point.
|
|
//
|
|
|
|
if (NumberOfPtes >= (ImageAlignment >> PAGE_SHIFT)) {
|
|
|
|
//
|
|
// Inconsistent image, size does not agree with object tables.
|
|
//
|
|
|
|
KdPrint(("MMCREASECT: invalid image - PTE left %lx\n image name %Z\n",
|
|
NumberOfPtes, &File->FileName));
|
|
|
|
goto BadPeImageSegment;
|
|
}
|
|
|
|
//
|
|
// Set any remaining PTEs to no access.
|
|
//
|
|
|
|
while (NumberOfPtes != 0) {
|
|
MI_WRITE_INVALID_PTE (PointerPte, ZeroPte);
|
|
PointerPte += 1;
|
|
NumberOfPtes -= 1;
|
|
}
|
|
|
|
//
|
|
// Turn the image header page into a transition page within the
|
|
// prototype PTEs.
|
|
//
|
|
|
|
if ((ExtendedHeader == NULL) && (SizeOfHeaders < PAGE_SIZE)) {
|
|
|
|
//
|
|
// Zero remaining portion of header.
|
|
//
|
|
|
|
RtlZeroMemory ((PVOID)((PCHAR)Base +
|
|
SizeOfHeaders),
|
|
PAGE_SIZE - SizeOfHeaders);
|
|
}
|
|
}
|
|
|
|
NumberOfCommittedPages = NewSegment->NumberOfCommittedPages;
|
|
|
|
if (NumberOfCommittedPages != 0) {
|
|
|
|
//
|
|
// Commit the pages for the image section.
|
|
//
|
|
|
|
if (MiChargeCommitment (NumberOfCommittedPages, NULL) == FALSE) {
|
|
Status = STATUS_COMMITMENT_LIMIT;
|
|
ExFreePool (NewSegment);
|
|
ExFreePool (ControlArea);
|
|
goto NeImage;
|
|
}
|
|
|
|
MM_TRACK_COMMIT (MM_DBG_COMMIT_IMAGE, NumberOfCommittedPages);
|
|
Status = STATUS_SUCCESS;
|
|
|
|
InterlockedExchangeAddSizeT (&MmSharedCommit, NumberOfCommittedPages);
|
|
}
|
|
|
|
PeReturnSuccess:
|
|
|
|
//
|
|
// Only images that are linked with subsections aligned to the native
|
|
// page size can be directly executed from ROM.
|
|
//
|
|
|
|
XipFile = FALSE;
|
|
XipFrameNumber = 0;
|
|
|
|
if ((FileAlignment == PAGE_SIZE - 1) && (XIPConfigured == TRUE)) {
|
|
|
|
Status = XIPLocatePages (File, &PhysicalAddress);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
XipFrameNumber = (PFN_NUMBER) (PhysicalAddress.QuadPart >> PAGE_SHIFT);
|
|
//
|
|
// The small control area will need to be reallocated as a large
|
|
// one so the starting frame number can be inserted. Set XipFile
|
|
// to denote this.
|
|
//
|
|
|
|
XipFile = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this image is global per session (or is going to be executed directly
|
|
// from ROM), then allocate a large control area. Note this doesn't need
|
|
// to be done for systemwide global control areas or non-global control
|
|
// areas.
|
|
//
|
|
|
|
GlobalPerSession = FALSE;
|
|
if ((ControlArea->u.Flags.GlobalMemory) &&
|
|
((LoaderFlags & IMAGE_LOADER_FLAGS_SYSTEM_GLOBAL) == 0)) {
|
|
GlobalPerSession = TRUE;
|
|
}
|
|
|
|
if ((XipFile == TRUE) || (GlobalPerSession == TRUE)) {
|
|
|
|
LargeControlArea = ExAllocatePoolWithTag(NonPagedPool,
|
|
(ULONG)(sizeof(LARGE_CONTROL_AREA) +
|
|
(sizeof(SUBSECTION) *
|
|
SubsectionsAllocated)),
|
|
'iCmM');
|
|
if (LargeControlArea == NULL) {
|
|
|
|
//
|
|
// The requested pool could not be allocated. If the image is
|
|
// execute-in-place only (ie: not global per session), then just
|
|
// execute it normally instead of inplace (to avoid not executing
|
|
// it at all).
|
|
//
|
|
|
|
if ((XipFile == TRUE) && (GlobalPerSession == FALSE)) {
|
|
goto SkipLargeControlArea;
|
|
}
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
goto ReturnCommitChargeOnError;
|
|
}
|
|
|
|
//
|
|
// Copy the normal control area into our larger one, fix the linkages,
|
|
// Fill in the additional fields in the new one and free the old one.
|
|
//
|
|
|
|
RtlCopyMemory (LargeControlArea, ControlArea, sizeof(CONTROL_AREA));
|
|
|
|
ASSERT (ControlArea->u.Flags.GlobalOnlyPerSession == 0);
|
|
|
|
if (XipFile == TRUE) {
|
|
|
|
//
|
|
// Mark the large control area accordingly. If we can't, then
|
|
// throw it away and use the small control area and execute from
|
|
// RAM instead.
|
|
//
|
|
|
|
if (MiMakeControlAreaRom (File, LargeControlArea, XipFrameNumber) == FALSE) {
|
|
if (GlobalPerSession == FALSE) {
|
|
ExFreePool (LargeControlArea);
|
|
goto SkipLargeControlArea;
|
|
}
|
|
}
|
|
}
|
|
|
|
Subsection = (PSUBSECTION)(ControlArea + 1);
|
|
NewSubsection = (PSUBSECTION)(LargeControlArea + 1);
|
|
|
|
for (i = 0; i < SubsectionsAllocated; i += 1) {
|
|
RtlCopyMemory (NewSubsection, Subsection, sizeof(SUBSECTION));
|
|
NewSubsection->ControlArea = (PCONTROL_AREA) LargeControlArea;
|
|
NewSubsection->NextSubsection = (NewSubsection + 1);
|
|
|
|
PointerPte = NewSegment->PrototypePte;
|
|
|
|
TempPte.u.Long = MiGetSubsectionAddressForPte (NewSubsection);
|
|
TempPte.u.Soft.Prototype = 1;
|
|
|
|
for (j = 0; j < NewSegment->TotalNumberOfPtes; j += 1) {
|
|
|
|
if ((PointerPte->u.Soft.Prototype == 1) &&
|
|
(MiGetSubsectionAddress (PointerPte) == Subsection)) {
|
|
OriginalProtection = MI_GET_PROTECTION_FROM_SOFT_PTE (PointerPte);
|
|
TempPte.u.Soft.Protection = OriginalProtection;
|
|
MI_WRITE_INVALID_PTE (PointerPte, TempPte);
|
|
}
|
|
PointerPte += 1;
|
|
}
|
|
|
|
Subsection += 1;
|
|
NewSubsection += 1;
|
|
}
|
|
|
|
(NewSubsection - 1)->NextSubsection = NULL;
|
|
|
|
NewSegment->ControlArea = (PCONTROL_AREA) LargeControlArea;
|
|
|
|
if (GlobalPerSession == TRUE) {
|
|
LargeControlArea->u.Flags.GlobalOnlyPerSession = 1;
|
|
|
|
LargeControlArea->SessionId = 0;
|
|
InitializeListHead (&LargeControlArea->UserGlobalList);
|
|
}
|
|
|
|
ExFreePool (ControlArea);
|
|
|
|
ControlArea = (PCONTROL_AREA) LargeControlArea;
|
|
}
|
|
|
|
SkipLargeControlArea:
|
|
|
|
MiUnmapImageHeaderInHyperSpace ();
|
|
|
|
//
|
|
// Set the PFN database entry for this page to look like a transition
|
|
// page.
|
|
//
|
|
|
|
PointerPte = NewSegment->PrototypePte;
|
|
|
|
MiUpdateImageHeaderPage (PointerPte, PageFrameNumber, ControlArea);
|
|
if (ExtendedHeader != NULL) {
|
|
ExFreePool (ExtendedHeader);
|
|
}
|
|
ExFreePool (InPageEvent);
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
|
|
//
|
|
// Error returns from image verification.
|
|
//
|
|
|
|
ReturnCommitChargeOnError:
|
|
|
|
NumberOfCommittedPages = NewSegment->NumberOfCommittedPages;
|
|
|
|
if (NumberOfCommittedPages != 0) {
|
|
MiReturnCommitment (NumberOfCommittedPages);
|
|
MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_IMAGE_NO_LARGE_CA, NumberOfCommittedPages);
|
|
InterlockedExchangeAddSizeT (&MmSharedCommit, 0-NumberOfCommittedPages);
|
|
}
|
|
|
|
ExFreePool (NewSegment);
|
|
ExFreePool (ControlArea);
|
|
goto NeImage;
|
|
|
|
BadPeImageSegment:
|
|
|
|
ExFreePool (NewSegment);
|
|
ExFreePool (ControlArea);
|
|
|
|
//BadPeImage:
|
|
Status = STATUS_INVALID_IMAGE_FORMAT;
|
|
|
|
NeImage:
|
|
MiUnmapImageHeaderInHyperSpace ();
|
|
|
|
BadSection:
|
|
MiRemoveImageHeaderPage(PageFrameNumber);
|
|
if (ExtendedHeader != NULL) {
|
|
ExFreePool (ExtendedHeader);
|
|
}
|
|
ExFreePool (InPageEvent);
|
|
return Status;
|
|
}
|
|
|
|
|
|
LOGICAL
|
|
MiCheckDosCalls (
|
|
IN PIMAGE_OS2_HEADER Os2Header,
|
|
IN ULONG HeaderSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Returns the status value.
|
|
|
|
--*/
|
|
|
|
{
|
|
PUCHAR ImportTable;
|
|
UCHAR EntrySize;
|
|
USHORT ModuleCount;
|
|
USHORT ModuleSize;
|
|
USHORT i;
|
|
PUSHORT ModuleTable;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// If there are no modules to check return immediately.
|
|
//
|
|
|
|
ModuleCount = Os2Header->ne_cmod;
|
|
|
|
if (ModuleCount == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// exe headers are notorious for having junk values for offsets
|
|
// in the import table and module table. We guard against this
|
|
// via careful checking plus our exception handler.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// Find out where the Module ref table is. Mod table has two byte
|
|
// for each entry in import table. These two bytes tell the offset
|
|
// in the import table for that entry.
|
|
//
|
|
|
|
ModuleTable = (PUSHORT)((PCHAR)Os2Header + (ULONG)Os2Header->ne_modtab);
|
|
|
|
//
|
|
// Make sure that the module table fits within the passed-in header.
|
|
// Note that each module table entry is 2 bytes long.
|
|
//
|
|
|
|
if (((ULONG)Os2Header->ne_modtab + (ModuleCount * 2)) > HeaderSize) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Now search individual entries for DOSCALLs.
|
|
//
|
|
|
|
for (i = 0; i < ModuleCount; i += 1) {
|
|
|
|
ModuleSize = *((UNALIGNED USHORT *)ModuleTable);
|
|
|
|
//
|
|
// Import table has count byte followed by the string where count
|
|
// is the string length.
|
|
//
|
|
|
|
ImportTable = (PUCHAR)((PCHAR)Os2Header +
|
|
(ULONG)Os2Header->ne_imptab + (ULONG)ModuleSize);
|
|
|
|
//
|
|
// Make sure the offset is within the valid range.
|
|
//
|
|
|
|
if (((ULONG)Os2Header->ne_imptab + (ULONG)ModuleSize)
|
|
>= HeaderSize) {
|
|
return FALSE;
|
|
}
|
|
|
|
EntrySize = *ImportTable++;
|
|
|
|
//
|
|
// 0 is a bad size, bail out.
|
|
//
|
|
|
|
if (EntrySize == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Make sure the offset is within the valid range.
|
|
// The sizeof(UCHAR) is included in the check because ImportTable
|
|
// was incremented above and is used in the RtlEqualMemory
|
|
// comparison below.
|
|
//
|
|
|
|
if (((ULONG)Os2Header->ne_imptab + (ULONG)ModuleSize +
|
|
(ULONG)EntrySize + sizeof(UCHAR)) > HeaderSize) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// If size matches compare DOSCALLS.
|
|
//
|
|
|
|
if (EntrySize == 8) {
|
|
if (RtlEqualMemory (ImportTable, "DOSCALLS", 8) ) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Move on to the next module table entry. Each entry is 2 bytes.
|
|
//
|
|
|
|
ModuleTable = (PUSHORT)((PCHAR)ModuleTable + 2);
|
|
}
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
return FALSE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
MiVerifyImageHeader (
|
|
IN PIMAGE_NT_HEADERS NtHeader,
|
|
IN PIMAGE_DOS_HEADER DosHeader,
|
|
IN ULONG NtHeaderSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function checks for various inconsistencies in the image header.
|
|
|
|
Arguments:
|
|
|
|
NtHeader - Supplies a pointer to the NT header of the image.
|
|
|
|
DosHeader - Supplies a pointer to the DOS header of the image.
|
|
|
|
NtHeaderSize - Supplies the size in bytes of the NT header.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCONFIGPHARLAP PharLapConfigured;
|
|
PUCHAR pb;
|
|
LONG pResTableAddress;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (NtHeader->Signature != IMAGE_NT_SIGNATURE) {
|
|
if ((USHORT)NtHeader->Signature == (USHORT)IMAGE_OS2_SIGNATURE) {
|
|
|
|
//
|
|
// Check to see if this is a win-16 image.
|
|
//
|
|
|
|
if ((!MiCheckDosCalls ((PIMAGE_OS2_HEADER)NtHeader, NtHeaderSize)) &&
|
|
((((PIMAGE_OS2_HEADER)NtHeader)->ne_exetyp == 2)
|
|
||
|
|
((((PIMAGE_OS2_HEADER)NtHeader)->ne_exetyp == 0) &&
|
|
(((((PIMAGE_OS2_HEADER)NtHeader)->ne_expver & 0xff00) ==
|
|
0x200) ||
|
|
((((PIMAGE_OS2_HEADER)NtHeader)->ne_expver & 0xff00) ==
|
|
0x300))))) {
|
|
|
|
//
|
|
// This is a win-16 image.
|
|
//
|
|
|
|
return STATUS_INVALID_IMAGE_WIN_16;
|
|
}
|
|
|
|
// The following OS/2 headers types go to NTDOS
|
|
//
|
|
// - exetype == 5 means binary is for Dos 4.0.
|
|
// e.g Borland Dos extender type
|
|
//
|
|
// - OS/2 apps which have no import table entries
|
|
// cannot be meant for the OS/2 ss.
|
|
// e.g. QuickC for dos binaries
|
|
//
|
|
// - "old" Borland Dosx BC++ 3.x, Paradox 4.x
|
|
// exe type == 1
|
|
// DosHeader->e_cs * 16 + DosHeader->e_ip + 0x200 - 10
|
|
// contains the string " mode EXE$"
|
|
// but import table is empty, so we don't make special check
|
|
//
|
|
|
|
if (((PIMAGE_OS2_HEADER)NtHeader)->ne_exetyp == 5 ||
|
|
((PIMAGE_OS2_HEADER)NtHeader)->ne_enttab ==
|
|
((PIMAGE_OS2_HEADER)NtHeader)->ne_imptab )
|
|
{
|
|
return STATUS_INVALID_IMAGE_PROTECT;
|
|
}
|
|
|
|
|
|
//
|
|
// Borland Dosx types: exe type 1
|
|
//
|
|
// - "new" Borland Dosx BP7.0
|
|
// exe type == 1
|
|
// DosHeader + 0x200 contains the string "16STUB"
|
|
// 0x200 happens to be e_parhdr*16
|
|
//
|
|
|
|
if (((PIMAGE_OS2_HEADER)NtHeader)->ne_exetyp == 1 &&
|
|
RtlEqualMemory((PUCHAR)DosHeader + 0x200, "16STUB", 6) )
|
|
{
|
|
return STATUS_INVALID_IMAGE_PROTECT;
|
|
}
|
|
|
|
//
|
|
// Check for PharLap extended header which we run as a dos app.
|
|
// The PharLap config block is pointed to by the SizeofHeader
|
|
// field in the DosHdr.
|
|
// The following algorithm for detecting a pharlap exe
|
|
// was recommended by PharLap Software Inc.
|
|
//
|
|
|
|
PharLapConfigured =(PCONFIGPHARLAP) ((PCHAR)DosHeader +
|
|
((ULONG)DosHeader->e_cparhdr << 4));
|
|
|
|
if ((PCHAR)PharLapConfigured <
|
|
(PCHAR)DosHeader + PAGE_SIZE - sizeof(CONFIGPHARLAP)) {
|
|
if (RtlEqualMemory(&PharLapConfigured->uchCopyRight[0x18],
|
|
"Phar Lap Software, Inc.", 24) &&
|
|
(PharLapConfigured->usSign == 0x4b50 || // stub loader type 2
|
|
PharLapConfigured->usSign == 0x4f50 || // bindable 286|DosExtender
|
|
PharLapConfigured->usSign == 0x5650 )) // bindable 286|DosExtender (Adv)
|
|
{
|
|
return STATUS_INVALID_IMAGE_PROTECT;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Check for Rational extended header which we run as a dos app.
|
|
// We look for the rational copyright at:
|
|
// wCopyRight = *(DosHeader->e_cparhdr*16 + 30h)
|
|
// pCopyRight = wCopyRight + DosHeader->e_cparhdr*16
|
|
// "Copyright (C) Rational Systems, Inc."
|
|
//
|
|
|
|
pb = ((PUCHAR)DosHeader + ((ULONG)DosHeader->e_cparhdr << 4));
|
|
|
|
if ((ULONG_PTR)pb < (ULONG_PTR)DosHeader + PAGE_SIZE - 0x30 - sizeof(USHORT)) {
|
|
pb += *(PUSHORT)(pb + 0x30);
|
|
if ( (ULONG_PTR)pb < (ULONG_PTR)DosHeader + PAGE_SIZE - 36 &&
|
|
RtlEqualMemory(pb,
|
|
"Copyright (C) Rational Systems, Inc.",
|
|
36) )
|
|
{
|
|
return STATUS_INVALID_IMAGE_PROTECT;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for lotus 123 family of applications. Starting
|
|
// with 123 3.0 (till recently shipped 123 3.4), every
|
|
// exe header is bound but is meant for DOS. This can
|
|
// be checked via, a string signature in the extended
|
|
// header. <len byte>"1-2-3 Preloader" is the string
|
|
// at ne_nrestab offset.
|
|
//
|
|
|
|
pResTableAddress = ((PIMAGE_OS2_HEADER)NtHeader)->ne_nrestab;
|
|
if (pResTableAddress > DosHeader->e_lfanew &&
|
|
((ULONG)((pResTableAddress+16) - DosHeader->e_lfanew) <
|
|
NtHeaderSize) &&
|
|
RtlEqualMemory(
|
|
((PUCHAR)NtHeader + 1 +
|
|
(ULONG)(pResTableAddress - DosHeader->e_lfanew)),
|
|
"1-2-3 Preloader",
|
|
15) ) {
|
|
return STATUS_INVALID_IMAGE_PROTECT;
|
|
}
|
|
|
|
return STATUS_INVALID_IMAGE_NE_FORMAT;
|
|
}
|
|
|
|
if ((USHORT)NtHeader->Signature == (USHORT)IMAGE_OS2_SIGNATURE_LE) {
|
|
|
|
//
|
|
// This is a LE (OS/2) image. We don't support it, so give it to
|
|
// DOS subsystem. There are cases (Rbase.exe) which have a LE
|
|
// header but actually it is suppose to run under DOS. When we
|
|
// do support LE format, some work needs to be done here to
|
|
// decide whether to give it to VDM or OS/2.
|
|
//
|
|
|
|
return STATUS_INVALID_IMAGE_PROTECT;
|
|
}
|
|
return STATUS_INVALID_IMAGE_PROTECT;
|
|
}
|
|
|
|
if ((NtHeader->FileHeader.Machine == 0) &&
|
|
(NtHeader->FileHeader.SizeOfOptionalHeader == 0)) {
|
|
|
|
//
|
|
// This is a bogus DOS app which has a 32-bit portion
|
|
// masquerading as a PE image.
|
|
//
|
|
|
|
return STATUS_INVALID_IMAGE_PROTECT;
|
|
}
|
|
|
|
if (!(NtHeader->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE)) {
|
|
return STATUS_INVALID_IMAGE_FORMAT;
|
|
}
|
|
|
|
#ifdef i386
|
|
|
|
//
|
|
// Make sure the image header is aligned on a Long word boundary.
|
|
//
|
|
|
|
if (((ULONG_PTR)NtHeader & 3) != 0) {
|
|
return STATUS_INVALID_IMAGE_FORMAT;
|
|
}
|
|
#endif
|
|
|
|
#define VALIDATE_NTHEADER(Hdr) { \
|
|
/* File alignment must be multiple of 512 and power of 2. */ \
|
|
if (((((Hdr)->OptionalHeader).FileAlignment & 511) != 0) && \
|
|
(((Hdr)->OptionalHeader).FileAlignment != \
|
|
((Hdr)->OptionalHeader).SectionAlignment)) { \
|
|
return STATUS_INVALID_IMAGE_FORMAT; \
|
|
} \
|
|
\
|
|
if (((Hdr)->OptionalHeader).FileAlignment == 0) { \
|
|
return STATUS_INVALID_IMAGE_FORMAT; \
|
|
} \
|
|
\
|
|
if (((((Hdr)->OptionalHeader).FileAlignment - 1) & \
|
|
((Hdr)->OptionalHeader).FileAlignment) != 0) { \
|
|
return STATUS_INVALID_IMAGE_FORMAT; \
|
|
} \
|
|
\
|
|
if (((Hdr)->OptionalHeader).SectionAlignment < ((Hdr)->OptionalHeader).FileAlignment) { \
|
|
return STATUS_INVALID_IMAGE_FORMAT; \
|
|
} \
|
|
\
|
|
if (((Hdr)->OptionalHeader).SizeOfImage > MM_SIZE_OF_LARGEST_IMAGE) { \
|
|
return STATUS_INVALID_IMAGE_FORMAT; \
|
|
} \
|
|
\
|
|
if ((Hdr)->FileHeader.NumberOfSections > MM_MAXIMUM_IMAGE_SECTIONS) { \
|
|
return STATUS_INVALID_IMAGE_FORMAT; \
|
|
} \
|
|
\
|
|
if (((Hdr)->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) && \
|
|
!((Hdr)->FileHeader.Machine == IMAGE_FILE_MACHINE_I386) ) { \
|
|
return STATUS_INVALID_IMAGE_FORMAT; \
|
|
} \
|
|
\
|
|
if (((Hdr)->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) && \
|
|
!(((Hdr)->FileHeader.Machine == IMAGE_FILE_MACHINE_IA64) || \
|
|
((Hdr)->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64))) { \
|
|
return STATUS_INVALID_IMAGE_FORMAT; \
|
|
} \
|
|
}
|
|
|
|
if (NtHeader->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) {
|
|
|
|
//
|
|
// Image doesn't have the right magic value in its optional header.
|
|
//
|
|
|
|
#if defined (_WIN64)
|
|
if (NtHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
|
|
|
|
//
|
|
// PE32 image. Validate it as such.
|
|
//
|
|
|
|
PIMAGE_NT_HEADERS32 NtHeader32 = (PIMAGE_NT_HEADERS32)NtHeader;
|
|
|
|
VALIDATE_NTHEADER(NtHeader32);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
#else /* !defined(_WIN64) */
|
|
if (NtHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
|
|
|
|
//
|
|
// 64bit image on a 32bit machine.
|
|
//
|
|
return STATUS_INVALID_IMAGE_WIN_64;
|
|
}
|
|
#endif
|
|
return STATUS_INVALID_IMAGE_FORMAT;
|
|
}
|
|
|
|
VALIDATE_NTHEADER(NtHeader);
|
|
#undef VALIDATE_NTHEADER
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
MiCreateDataFileMap (
|
|
IN PFILE_OBJECT File,
|
|
OUT PSEGMENT *Segment,
|
|
IN PUINT64 MaximumSize,
|
|
IN ULONG SectionPageProtection,
|
|
IN ULONG AllocationAttributes,
|
|
IN ULONG IgnoreFileSizing
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function creates the necessary structures to allow the mapping
|
|
of a data file.
|
|
|
|
The data file is accessed to verify desired access, a segment
|
|
object is created and initialized.
|
|
|
|
Arguments:
|
|
|
|
File - Supplies the file object for the image file.
|
|
|
|
Segment - Returns the segment object.
|
|
|
|
MaximumSize - Supplies the maximum size for the mapping.
|
|
|
|
SectionPageProtection - Supplies the initial page protection.
|
|
|
|
AllocationAttributes - Supplies the allocation attributes for the mapping.
|
|
|
|
IgnoreFileSizing - Supplies TRUE if the cache manager is specifying the
|
|
file size and so it does not need to be validated.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG NumberOfPtes;
|
|
ULONG j;
|
|
ULONG Size;
|
|
ULONG PartialSize;
|
|
PCONTROL_AREA ControlArea;
|
|
PLARGE_CONTROL_AREA LargeControlArea;
|
|
PMAPPED_FILE_SEGMENT NewSegment;
|
|
PMSUBSECTION Subsection;
|
|
PMSUBSECTION ExtendedSubsection;
|
|
PMSUBSECTION LargeExtendedSubsection;
|
|
MMPTE TempPte;
|
|
UINT64 EndOfFile;
|
|
UINT64 LastFileChunk;
|
|
UINT64 FileOffset;
|
|
UINT64 NumberOfPtesForEntireFile;
|
|
ULONG ExtendedSubsections;
|
|
PMSUBSECTION Last;
|
|
ULONG NumberOfNewSubsections;
|
|
SIZE_T AllocationFragment;
|
|
PHYSICAL_ADDRESS PhysicalAddress;
|
|
PFN_NUMBER PageFrameNumber;
|
|
|
|
PAGED_CODE();
|
|
|
|
ExtendedSubsections = 0;
|
|
|
|
// *************************************************************
|
|
// Create mapped file section.
|
|
// *************************************************************
|
|
|
|
if (!IgnoreFileSizing) {
|
|
|
|
Status = FsRtlGetFileSize (File, (PLARGE_INTEGER)&EndOfFile);
|
|
|
|
if (Status == STATUS_FILE_IS_A_DIRECTORY) {
|
|
|
|
//
|
|
// Can't map a directory as a section. Return error.
|
|
//
|
|
|
|
return STATUS_INVALID_FILE_FOR_SECTION;
|
|
}
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if (EndOfFile == 0 && *MaximumSize == 0) {
|
|
|
|
//
|
|
// Can't map a zero length without specifying the maximum
|
|
// size as non-zero.
|
|
//
|
|
|
|
return STATUS_MAPPED_FILE_SIZE_ZERO;
|
|
}
|
|
|
|
//
|
|
// Make sure this file is big enough for the section.
|
|
//
|
|
|
|
if (*MaximumSize > EndOfFile) {
|
|
|
|
//
|
|
// If the maximum size is greater than the end-of-file,
|
|
// and the user did not request page_write or page_execute_readwrite
|
|
// to the section, reject the request.
|
|
//
|
|
|
|
if (((SectionPageProtection & PAGE_READWRITE) |
|
|
(SectionPageProtection & PAGE_EXECUTE_READWRITE)) == 0) {
|
|
|
|
return STATUS_SECTION_TOO_BIG;
|
|
}
|
|
|
|
//
|
|
// Check to make sure that the allocation size large enough
|
|
// to contain all the data, if not set a new allocation size.
|
|
//
|
|
|
|
EndOfFile = *MaximumSize;
|
|
|
|
Status = FsRtlSetFileSize (File, (PLARGE_INTEGER)&EndOfFile);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
return Status;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Ignore the file size, this call is from the cache manager.
|
|
//
|
|
|
|
EndOfFile = *MaximumSize;
|
|
}
|
|
|
|
//
|
|
// Calculate the number of prototype PTE chunks to build for this section.
|
|
// A subsection is also allocated for each chunk as all the prototype PTEs
|
|
// in any given chunk are initially encoded to point at the same subsection.
|
|
//
|
|
// The maximum total section size is 16PB (2^54). This is because the
|
|
// StartingSector4132 field in each subsection, ie: 2^42-1 bits of file
|
|
// offset where the offset is in 4K (not pagesize) units. Thus, a
|
|
// subsection may describe a *BYTE* file start offset of maximum
|
|
// 2^54 - 4K.
|
|
//
|
|
// Each subsection can span at most 16TB - 64K.
|
|
//
|
|
// This is because the NumberOfFullSectors and various other fields
|
|
// in the subsection are ULONGs.
|
|
//
|
|
// The last item to notice is that the NumberOfSubsections is currently
|
|
// a USHORT in the ControlArea. Note this does not limit us to 64K-1
|
|
// subsections because this field is only relied on for images not data.
|
|
//
|
|
|
|
NumberOfPtesForEntireFile = (EndOfFile + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
|
|
|
NumberOfPtes = (ULONG)NumberOfPtesForEntireFile;
|
|
|
|
if (EndOfFile > MI_MAXIMUM_SECTION_SIZE) {
|
|
return STATUS_SECTION_TOO_BIG;
|
|
}
|
|
|
|
if (NumberOfPtesForEntireFile > (UINT64)((MAXULONG_PTR / sizeof(MMPTE)) - sizeof (SEGMENT))) {
|
|
return STATUS_SECTION_TOO_BIG;
|
|
}
|
|
|
|
if (NumberOfPtesForEntireFile > EndOfFile) {
|
|
return STATUS_SECTION_TOO_BIG;
|
|
}
|
|
|
|
NewSegment = ExAllocatePoolWithTag (PagedPool,
|
|
sizeof(MAPPED_FILE_SEGMENT),
|
|
'mSmM');
|
|
|
|
if (NewSegment == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Allocate the subsection memory in smaller sizes so the corresponding
|
|
// prototype PTEs can be trimmed later if paged pool virtual address
|
|
// space becomes scarce. Note the size is snapped locally so it can
|
|
// be changed dynamically without locking.
|
|
//
|
|
|
|
AllocationFragment = MmAllocationFragment;
|
|
|
|
ASSERT (MiGetByteOffset (AllocationFragment) == 0);
|
|
ASSERT (AllocationFragment >= PAGE_SIZE);
|
|
ASSERT64 (AllocationFragment < _4gb);
|
|
|
|
Size = (ULONG) AllocationFragment;
|
|
PartialSize = NumberOfPtes * sizeof(MMPTE);
|
|
|
|
NumberOfNewSubsections = 0;
|
|
ExtendedSubsection = NULL;
|
|
|
|
//
|
|
// Initializing Last is not needed for correctness, but without it the
|
|
// compiler cannot compile this code W4 to check for use of uninitialized
|
|
// variables.
|
|
//
|
|
|
|
Last = NULL;
|
|
|
|
ControlArea = (PCONTROL_AREA)File->SectionObjectPointer->DataSectionObject;
|
|
|
|
do {
|
|
|
|
if (PartialSize < (ULONG) AllocationFragment) {
|
|
PartialSize = (ULONG) ROUND_TO_PAGES (PartialSize);
|
|
Size = PartialSize;
|
|
}
|
|
|
|
if (ExtendedSubsection == NULL) {
|
|
ExtendedSubsection = (PMSUBSECTION)(ControlArea + 1);
|
|
|
|
//
|
|
// Control area and first subsection were zeroed at allocation time.
|
|
//
|
|
}
|
|
else {
|
|
|
|
ExtendedSubsection = ExAllocatePoolWithTag (NonPagedPool,
|
|
sizeof(MSUBSECTION),
|
|
'cSmM');
|
|
|
|
if (ExtendedSubsection == NULL) {
|
|
ExFreePool (NewSegment);
|
|
|
|
//
|
|
// Free all the previous allocations and return an error.
|
|
//
|
|
|
|
ExtendedSubsection = (PMSUBSECTION)(ControlArea + 1);
|
|
ExtendedSubsection = (PMSUBSECTION) ExtendedSubsection->NextSubsection;
|
|
while (ExtendedSubsection != NULL) {
|
|
Last = (PMSUBSECTION) ExtendedSubsection->NextSubsection;
|
|
ExFreePool (ExtendedSubsection);
|
|
ExtendedSubsection = Last;
|
|
}
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory (ExtendedSubsection, sizeof(MSUBSECTION));
|
|
Last->NextSubsection = (PSUBSECTION) ExtendedSubsection;
|
|
}
|
|
|
|
NumberOfNewSubsections += 1;
|
|
|
|
ExtendedSubsection->PtesInSubsection = Size / sizeof(MMPTE);
|
|
|
|
Last = ExtendedSubsection;
|
|
PartialSize -= Size;
|
|
} while (PartialSize != 0);
|
|
|
|
*Segment = (PSEGMENT) NewSegment;
|
|
RtlZeroMemory (NewSegment, sizeof(MAPPED_FILE_SEGMENT));
|
|
|
|
NewSegment->LastSubsectionHint = ExtendedSubsection;
|
|
|
|
//
|
|
// Control area and first subsection were zeroed at allocation time.
|
|
//
|
|
|
|
ControlArea->Segment = (PSEGMENT) NewSegment;
|
|
ControlArea->NumberOfSectionReferences = 1;
|
|
|
|
if (IgnoreFileSizing == FALSE) {
|
|
|
|
//
|
|
// This reference is not from the cache manager.
|
|
//
|
|
|
|
ControlArea->NumberOfUserReferences = 1;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Set the was purged flag to indicate that the
|
|
// file size was not explicitly set.
|
|
//
|
|
|
|
ControlArea->u.Flags.WasPurged = 1;
|
|
}
|
|
|
|
ControlArea->u.Flags.BeingCreated = 1;
|
|
ControlArea->u.Flags.File = 1;
|
|
|
|
if (FILE_REMOTE_DEVICE & File->DeviceObject->Characteristics) {
|
|
|
|
//
|
|
// This file resides on a redirected drive.
|
|
//
|
|
|
|
ControlArea->u.Flags.Networked = 1;
|
|
}
|
|
|
|
if (AllocationAttributes & SEC_NOCACHE) {
|
|
ControlArea->u.Flags.NoCache = 1;
|
|
}
|
|
|
|
//
|
|
// Note this count is not correct if we have 65535 or more subsections
|
|
// for this file, but that's ok because the count is only relied on
|
|
// for images, not data anyway.
|
|
//
|
|
|
|
ControlArea->NumberOfSubsections = (USHORT)NumberOfNewSubsections;
|
|
ControlArea->FilePointer = File;
|
|
|
|
ASSERT (ControlArea->u.Flags.GlobalOnlyPerSession == 0);
|
|
|
|
Subsection = (PMSUBSECTION)(ControlArea + 1);
|
|
|
|
//
|
|
// Loop through all the subsections and fill in the PTEs.
|
|
//
|
|
|
|
TempPte.u.Long = MiGetSubsectionAddressForPte (Subsection);
|
|
TempPte.u.Soft.Prototype = 1;
|
|
|
|
//
|
|
// Set all the PTEs to the execute-read-write protection.
|
|
// The section will control access to these and the segment
|
|
// must provide a method to allow other users to map the file
|
|
// for various protections.
|
|
//
|
|
|
|
TempPte.u.Soft.Protection = MM_EXECUTE_READWRITE;
|
|
|
|
NewSegment->ControlArea = ControlArea;
|
|
NewSegment->SizeOfSegment = EndOfFile;
|
|
NewSegment->TotalNumberOfPtes = NumberOfPtes;
|
|
|
|
NewSegment->SegmentPteTemplate = TempPte;
|
|
|
|
if (Subsection->NextSubsection != NULL) {
|
|
|
|
//
|
|
// Multiple segments and subsections.
|
|
// Align first so it is a multiple of the allocation size.
|
|
//
|
|
|
|
NewSegment->NonExtendedPtes =
|
|
(Subsection->PtesInSubsection & ~(((ULONG)AllocationFragment >> PAGE_SHIFT) - 1));
|
|
}
|
|
else {
|
|
NewSegment->NonExtendedPtes = NumberOfPtes;
|
|
}
|
|
|
|
Subsection->PtesInSubsection = NewSegment->NonExtendedPtes;
|
|
|
|
FileOffset = 0;
|
|
|
|
do {
|
|
|
|
//
|
|
// Loop through all the subsections to initialize them.
|
|
//
|
|
|
|
Subsection->ControlArea = ControlArea;
|
|
|
|
Mi4KStartForSubsection(&FileOffset, Subsection);
|
|
|
|
Subsection->u.SubsectionFlags.Protection = MM_EXECUTE_READWRITE;
|
|
|
|
if (Subsection->NextSubsection == NULL) {
|
|
|
|
LastFileChunk = (EndOfFile >> MM4K_SHIFT) - FileOffset;
|
|
|
|
//
|
|
// Note this next line restricts the number of bytes mapped by
|
|
// a single subsection to 16TB-4K. Multiple subsections can always
|
|
// be chained together to support an overall file of size 16K TB.
|
|
//
|
|
|
|
Subsection->NumberOfFullSectors = (ULONG)LastFileChunk;
|
|
|
|
Subsection->u.SubsectionFlags.SectorEndOffset =
|
|
(ULONG) EndOfFile & MM4K_MASK;
|
|
|
|
j = Subsection->PtesInSubsection;
|
|
|
|
Subsection->PtesInSubsection = (ULONG)(
|
|
NumberOfPtesForEntireFile -
|
|
(FileOffset >> (PAGE_SHIFT - MM4K_SHIFT)));
|
|
|
|
MI_CHECK_SUBSECTION (Subsection);
|
|
|
|
Subsection->UnusedPtes = j - Subsection->PtesInSubsection;
|
|
}
|
|
else {
|
|
Subsection->NumberOfFullSectors =
|
|
Subsection->PtesInSubsection << (PAGE_SHIFT - MM4K_SHIFT);
|
|
|
|
MI_CHECK_SUBSECTION (Subsection);
|
|
}
|
|
|
|
FileOffset += Subsection->PtesInSubsection <<
|
|
(PAGE_SHIFT - MM4K_SHIFT);
|
|
Subsection = (PMSUBSECTION) Subsection->NextSubsection;
|
|
} while (Subsection != NULL);
|
|
|
|
if (XIPConfigured == TRUE) {
|
|
|
|
Status = XIPLocatePages (File, &PhysicalAddress);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
PageFrameNumber = (PFN_NUMBER) (PhysicalAddress.QuadPart >> PAGE_SHIFT);
|
|
//
|
|
// Allocate a large control area (so the starting frame number
|
|
// can be saved) and repoint all the created subsections to it.
|
|
//
|
|
|
|
LargeControlArea = ExAllocatePoolWithTag (NonPagedPool,
|
|
(ULONG)(sizeof(LARGE_CONTROL_AREA) +
|
|
sizeof(MSUBSECTION)),
|
|
MMCONTROL);
|
|
|
|
if (LargeControlArea != NULL) {
|
|
|
|
*(PCONTROL_AREA) LargeControlArea = *ControlArea;
|
|
|
|
if (MiMakeControlAreaRom (File, LargeControlArea, PageFrameNumber) == TRUE) {
|
|
|
|
LargeExtendedSubsection = (PMSUBSECTION)(LargeControlArea + 1);
|
|
ExtendedSubsection = (PMSUBSECTION)(ControlArea + 1);
|
|
|
|
*LargeExtendedSubsection = *ExtendedSubsection;
|
|
LargeExtendedSubsection->ControlArea = (PCONTROL_AREA) LargeControlArea;
|
|
|
|
//
|
|
// Only the first subsection needed to be directly modified
|
|
// as above because it is allocated in a single chunk with
|
|
// the control area. Any additional subsections below
|
|
// just need their control area pointers updated.
|
|
//
|
|
|
|
ASSERT (NumberOfNewSubsections >= 1);
|
|
j = NumberOfNewSubsections - 1;
|
|
|
|
while (j != 0) {
|
|
|
|
ExtendedSubsection = (PMSUBSECTION) ExtendedSubsection->NextSubsection;
|
|
ExtendedSubsection->ControlArea = (PCONTROL_AREA) LargeControlArea;
|
|
j -= 1;
|
|
}
|
|
|
|
NewSegment->ControlArea = (PCONTROL_AREA) LargeControlArea;
|
|
}
|
|
else {
|
|
ExFreePool (LargeControlArea);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
MiCreatePagingFileMap (
|
|
OUT PSEGMENT *Segment,
|
|
IN PUINT64 MaximumSize,
|
|
IN ULONG ProtectionMask,
|
|
IN ULONG AllocationAttributes
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function creates the necessary structures to allow the mapping
|
|
of a paging file.
|
|
|
|
Arguments:
|
|
|
|
Segment - Returns the segment object.
|
|
|
|
MaximumSize - Supplies the maximum size for the mapping.
|
|
|
|
ProtectionMask - Supplies the initial page protection.
|
|
|
|
AllocationAttributes - Supplies the allocation attributes for the
|
|
mapping.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
PFN_NUMBER NumberOfPtes;
|
|
ULONG SizeOfSegment;
|
|
ULONG i;
|
|
PCONTROL_AREA ControlArea;
|
|
PSEGMENT NewSegment;
|
|
PMMPTE PointerPte;
|
|
PSUBSECTION Subsection;
|
|
MMPTE TempPte;
|
|
|
|
PAGED_CODE();
|
|
|
|
//*******************************************************************
|
|
// Create a section backed by the paging file.
|
|
//*******************************************************************
|
|
|
|
if (*MaximumSize == 0) {
|
|
return STATUS_INVALID_PARAMETER_4;
|
|
}
|
|
|
|
//
|
|
// Limit page file backed sections to the pagefile maximum size.
|
|
//
|
|
|
|
if (*MaximumSize > (MI_MAXIMUM_PAGEFILE_SIZE - (1024 * 1024))) {
|
|
return STATUS_SECTION_TOO_BIG;
|
|
}
|
|
|
|
//
|
|
// Create the segment object.
|
|
//
|
|
|
|
//
|
|
// Calculate the number of prototype PTEs to build for this segment.
|
|
//
|
|
|
|
NumberOfPtes = BYTES_TO_PAGES (*MaximumSize);
|
|
|
|
if (AllocationAttributes & SEC_COMMIT) {
|
|
|
|
//
|
|
// Commit the pages for the section.
|
|
//
|
|
|
|
ASSERT (ProtectionMask != 0);
|
|
|
|
if (MiChargeCommitment (NumberOfPtes, NULL) == FALSE) {
|
|
return STATUS_COMMITMENT_LIMIT;
|
|
}
|
|
}
|
|
|
|
SizeOfSegment = sizeof(SEGMENT) + sizeof(MMPTE) * ((ULONG)NumberOfPtes - 1);
|
|
|
|
NewSegment = ExAllocatePoolWithTag (PagedPool, SizeOfSegment, MMSECT);
|
|
|
|
if (NewSegment == NULL) {
|
|
|
|
//
|
|
// The requested pool could not be allocated.
|
|
//
|
|
|
|
if (AllocationAttributes & SEC_COMMIT) {
|
|
MiReturnCommitment (NumberOfPtes);
|
|
}
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
*Segment = NewSegment;
|
|
|
|
ControlArea = ExAllocatePoolWithTag (NonPagedPool,
|
|
(ULONG)sizeof(CONTROL_AREA) +
|
|
(ULONG)sizeof(SUBSECTION),
|
|
MMCONTROL);
|
|
|
|
if (ControlArea == NULL) {
|
|
|
|
//
|
|
// The requested pool could not be allocated.
|
|
//
|
|
|
|
ExFreePool (NewSegment);
|
|
|
|
if (AllocationAttributes & SEC_COMMIT) {
|
|
MiReturnCommitment (NumberOfPtes);
|
|
}
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Zero control area and first subsection.
|
|
//
|
|
|
|
RtlZeroMemory (ControlArea, sizeof(CONTROL_AREA) + sizeof(SUBSECTION));
|
|
|
|
ControlArea->Segment = NewSegment;
|
|
ControlArea->NumberOfSectionReferences = 1;
|
|
ControlArea->NumberOfUserReferences = 1;
|
|
ControlArea->NumberOfSubsections = 1;
|
|
|
|
if (AllocationAttributes & SEC_BASED) {
|
|
ControlArea->u.Flags.Based = 1;
|
|
}
|
|
|
|
if (AllocationAttributes & SEC_RESERVE) {
|
|
ControlArea->u.Flags.Reserve = 1;
|
|
}
|
|
|
|
if (AllocationAttributes & SEC_COMMIT) {
|
|
ControlArea->u.Flags.Commit = 1;
|
|
}
|
|
|
|
Subsection = (PSUBSECTION)(ControlArea + 1);
|
|
|
|
Subsection->ControlArea = ControlArea;
|
|
Subsection->PtesInSubsection = (ULONG)NumberOfPtes;
|
|
Subsection->u.SubsectionFlags.Protection = ProtectionMask;
|
|
|
|
//
|
|
// Align the prototype PTEs on the proper boundary.
|
|
//
|
|
|
|
PointerPte = &NewSegment->ThePtes[0];
|
|
i = (ULONG) (((ULONG_PTR)PointerPte >> PTE_SHIFT) &
|
|
((MM_PROTO_PTE_ALIGNMENT / PAGE_SIZE) - 1));
|
|
|
|
if (i != 0) {
|
|
i = (MM_PROTO_PTE_ALIGNMENT / PAGE_SIZE) - i;
|
|
}
|
|
|
|
//
|
|
// Zero the segment header.
|
|
//
|
|
|
|
RtlZeroMemory (NewSegment, sizeof(SEGMENT));
|
|
|
|
NewSegment->PrototypePte = &NewSegment->ThePtes[i];
|
|
|
|
NewSegment->ControlArea = ControlArea;
|
|
|
|
//
|
|
// Record the process that created this segment for the performance
|
|
// analysis tools.
|
|
//
|
|
|
|
NewSegment->u1.CreatingProcess = PsGetCurrentProcess ();
|
|
|
|
NewSegment->SizeOfSegment = (UINT64)NumberOfPtes * PAGE_SIZE;
|
|
NewSegment->TotalNumberOfPtes = (ULONG)NumberOfPtes;
|
|
NewSegment->NonExtendedPtes = (ULONG)NumberOfPtes;
|
|
|
|
PointerPte = NewSegment->PrototypePte;
|
|
Subsection->SubsectionBase = PointerPte;
|
|
TempPte = ZeroPte;
|
|
|
|
if (AllocationAttributes & SEC_COMMIT) {
|
|
TempPte.u.Soft.Protection = ProtectionMask;
|
|
|
|
//
|
|
// Record commitment charging.
|
|
//
|
|
|
|
MM_TRACK_COMMIT (MM_DBG_COMMIT_PAGEFILE_BACKED_SHMEM, NumberOfPtes);
|
|
|
|
NewSegment->NumberOfCommittedPages = NumberOfPtes;
|
|
|
|
InterlockedExchangeAddSizeT (&MmSharedCommit, NumberOfPtes);
|
|
}
|
|
|
|
NewSegment->SegmentPteTemplate.u.Soft.Protection = ProtectionMask;
|
|
|
|
//
|
|
// Set all the prototype PTEs to either no access or demand zero
|
|
// depending on the commit flag.
|
|
//
|
|
|
|
MiFillMemoryPte (PointerPte, NumberOfPtes * sizeof(MMPTE), TempPte.u.Long);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NtOpenSection (
|
|
OUT PHANDLE SectionHandle,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN POBJECT_ATTRIBUTES ObjectAttributes
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function opens a handle to a section object with the specified
|
|
desired access.
|
|
|
|
Arguments:
|
|
|
|
|
|
Sectionhandle - Supplies a pointer to a variable that will
|
|
receive the section object handle value.
|
|
|
|
DesiredAccess - Supplies the desired types of access for the
|
|
section.
|
|
|
|
DesiredAccess Flags
|
|
|
|
EXECUTE - Execute access to the section is desired.
|
|
|
|
READ - Read access to the section is desired.
|
|
|
|
WRITE - Write access to the section is desired.
|
|
|
|
ObjectAttributes - Supplies a pointer to an object attributes structure.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE Handle;
|
|
KPROCESSOR_MODE PreviousMode;
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
//
|
|
// Get previous processor mode and probe output arguments if necessary.
|
|
//
|
|
|
|
PreviousMode = KeGetPreviousMode();
|
|
if (PreviousMode != KernelMode) {
|
|
try {
|
|
ProbeForWriteHandle(SectionHandle);
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
return GetExceptionCode();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Open handle to the section object with the specified desired
|
|
// access.
|
|
//
|
|
|
|
Status = ObOpenObjectByName (ObjectAttributes,
|
|
MmSectionObjectType,
|
|
PreviousMode,
|
|
NULL,
|
|
DesiredAccess,
|
|
NULL,
|
|
&Handle
|
|
);
|
|
|
|
try {
|
|
*SectionHandle = Handle;
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
return Status;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
CCHAR
|
|
MiGetImageProtection (
|
|
IN ULONG SectionCharacteristics
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function takes a section characteristic mask from the
|
|
image and converts it to an PTE protection mask.
|
|
|
|
Arguments:
|
|
|
|
SectionCharacteristics - Supplies the characteristics mask from the
|
|
image.
|
|
|
|
Return Value:
|
|
|
|
Returns the protection mask for the PTE.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Index;
|
|
PAGED_CODE();
|
|
|
|
Index = 0;
|
|
if (SectionCharacteristics & IMAGE_SCN_MEM_EXECUTE) {
|
|
Index |= 1;
|
|
}
|
|
if (SectionCharacteristics & IMAGE_SCN_MEM_READ) {
|
|
Index |= 2;
|
|
}
|
|
if (SectionCharacteristics & IMAGE_SCN_MEM_WRITE) {
|
|
Index |= 4;
|
|
}
|
|
if (SectionCharacteristics & IMAGE_SCN_MEM_SHARED) {
|
|
Index |= 8;
|
|
}
|
|
|
|
return MmImageProtectionArray[Index];
|
|
}
|
|
|
|
PFN_NUMBER
|
|
MiGetPageForHeader (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This non-pagable function acquires the PFN lock, removes
|
|
a page and updates the PFN database as though the page was
|
|
ready to be deleted if the reference count is decremented.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Returns the physical page frame number.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL OldIrql;
|
|
PFN_NUMBER PageFrameNumber;
|
|
PMMPFN Pfn1;
|
|
PEPROCESS Process;
|
|
ULONG PageColor;
|
|
|
|
Process = PsGetCurrentProcess();
|
|
PageColor = MI_PAGE_COLOR_VA_PROCESS ((PVOID)X64K,
|
|
&Process->NextPageColor);
|
|
|
|
//
|
|
// Lock the PFN database and get a page.
|
|
//
|
|
|
|
LOCK_PFN (OldIrql);
|
|
|
|
MiEnsureAvailablePageOrWait (NULL, NULL);
|
|
|
|
//
|
|
// Remove page for 64k alignment.
|
|
//
|
|
|
|
PageFrameNumber = MiRemoveAnyPage (PageColor);
|
|
|
|
//
|
|
// Increment the reference count for the page so the
|
|
// paging I/O will work, and so this page cannot be stolen from us.
|
|
//
|
|
|
|
Pfn1 = MI_PFN_ELEMENT (PageFrameNumber);
|
|
Pfn1->u3.e2.ReferenceCount += 1;
|
|
|
|
//
|
|
// Don't need the PFN lock for the fields below...
|
|
//
|
|
|
|
UNLOCK_PFN (OldIrql);
|
|
|
|
Pfn1->OriginalPte = ZeroPte;
|
|
Pfn1->PteAddress = (PVOID) (ULONG_PTR)X64K;
|
|
MI_SET_PFN_DELETED (Pfn1);
|
|
|
|
return PageFrameNumber;
|
|
}
|
|
|
|
VOID
|
|
MiUpdateImageHeaderPage (
|
|
IN PMMPTE PointerPte,
|
|
IN PFN_NUMBER PageFrameNumber,
|
|
IN PCONTROL_AREA ControlArea
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This non-pagable function acquires the PFN lock, and
|
|
turns the specified prototype PTE into a transition PTE
|
|
referring to the specified physical page. It then
|
|
decrements the reference count causing the page to
|
|
be placed on the standby or modified list.
|
|
|
|
Arguments:
|
|
|
|
PointerPte - Supplies the PTE to set into the transition state.
|
|
|
|
PageFrameNumber - Supplies the physical page.
|
|
|
|
ControlArea - Supplies the control area for the prototype PTEs.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL OldIrql;
|
|
|
|
LOCK_PFN (OldIrql);
|
|
|
|
MiMakeSystemAddressValidPfn (PointerPte);
|
|
|
|
MiInitializeTransitionPfn (PageFrameNumber, PointerPte);
|
|
ControlArea->NumberOfPfnReferences += 1;
|
|
|
|
//
|
|
// Add the page to the standby list.
|
|
//
|
|
|
|
MiDecrementReferenceCount (PageFrameNumber);
|
|
|
|
UNLOCK_PFN (OldIrql);
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
MiRemoveImageHeaderPage (
|
|
IN PFN_NUMBER PageFrameNumber
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This non-pagable function acquires the PFN lock, and decrements
|
|
the reference count thereby causing the physical page to
|
|
be deleted.
|
|
|
|
Arguments:
|
|
|
|
PageFrameNumber - Supplies the PFN to decrement.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
KIRQL OldIrql;
|
|
|
|
LOCK_PFN (OldIrql);
|
|
MiDecrementReferenceCount (PageFrameNumber);
|
|
UNLOCK_PFN (OldIrql);
|
|
return;
|
|
}
|
|
|
|
PCONTROL_AREA
|
|
MiFindImageSectionObject(
|
|
IN PFILE_OBJECT File,
|
|
OUT PLOGICAL GlobalNeeded
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function searches the control area chains (if any) for an existing
|
|
cache of the specified image file. For non-global control areas, there is
|
|
no chain and the control area is shared for all callers and sessions.
|
|
Likewise for systemwide global control areas.
|
|
|
|
However, for global PER-SESSION control areas, we must do the walk.
|
|
|
|
Arguments:
|
|
|
|
File - Supplies the file object for the image file.
|
|
|
|
GlobalNeeded - Supplies a pointer to store whether a global control area is
|
|
required as a placeholder. This can only be set when there
|
|
is already some global control area in the list - ie: our
|
|
caller should only rely on this when this function returns
|
|
NULL so the caller knows what kind of control area to
|
|
insert.
|
|
|
|
Return Value:
|
|
|
|
Returns the matching control area or NULL if one does not exist.
|
|
|
|
Environment:
|
|
|
|
Must be holding the PFN lock.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCONTROL_AREA ControlArea;
|
|
PLARGE_CONTROL_AREA LargeControlArea;
|
|
PLIST_ENTRY Head, Next;
|
|
ULONG SessionId;
|
|
|
|
MM_PFN_LOCK_ASSERT();
|
|
|
|
*GlobalNeeded = FALSE;
|
|
|
|
//
|
|
// Get first (if any) control area pointer.
|
|
//
|
|
|
|
ControlArea = (PCONTROL_AREA)(File->SectionObjectPointer->ImageSectionObject);
|
|
|
|
//
|
|
// If no control area, or the control area is not session global,
|
|
// then our job is easy. Note, however, that they each require different
|
|
// return values as they represent different states.
|
|
//
|
|
|
|
if (ControlArea == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (ControlArea->u.Flags.GlobalOnlyPerSession == 0) {
|
|
return ControlArea;
|
|
}
|
|
|
|
LargeControlArea = (PLARGE_CONTROL_AREA) ControlArea;
|
|
|
|
//
|
|
// Get the current session ID and search for a matching control area.
|
|
//
|
|
|
|
SessionId = MmGetSessionId (PsGetCurrentProcess());
|
|
|
|
if (LargeControlArea->SessionId == SessionId) {
|
|
return (PCONTROL_AREA) LargeControlArea;
|
|
}
|
|
|
|
//
|
|
// Must search the control area list for a matching session ID.
|
|
//
|
|
|
|
Head = &LargeControlArea->UserGlobalList;
|
|
|
|
for (Next = Head->Flink; Next != Head; Next = Next->Flink) {
|
|
|
|
LargeControlArea = CONTAINING_RECORD (Next, LARGE_CONTROL_AREA, UserGlobalList);
|
|
|
|
ASSERT (LargeControlArea->u.Flags.GlobalOnlyPerSession == 1);
|
|
|
|
if (LargeControlArea->SessionId == SessionId) {
|
|
return (PCONTROL_AREA) LargeControlArea;
|
|
}
|
|
}
|
|
|
|
//
|
|
// No match, so tell our caller to create a new global control area.
|
|
//
|
|
|
|
*GlobalNeeded = TRUE;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
VOID
|
|
MiInsertImageSectionObject(
|
|
IN PFILE_OBJECT File,
|
|
IN PCONTROL_AREA InputControlArea
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function inserts the control area into the file's section object
|
|
pointers. For non-global control areas, there is no chain and the
|
|
control area is shared for all callers and sessions.
|
|
Likewise for systemwide global control areas.
|
|
|
|
However, for global PER-SESSION control areas, we must do a list insertion.
|
|
|
|
Arguments:
|
|
|
|
File - Supplies the file object for the image file.
|
|
|
|
InputControlArea - Supplies the control area to insert.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Environment:
|
|
|
|
Must be holding the PFN lock.
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY Head;
|
|
PLARGE_CONTROL_AREA ControlArea;
|
|
PLARGE_CONTROL_AREA FirstControlArea;
|
|
#if DBG
|
|
PLIST_ENTRY Next;
|
|
PLARGE_CONTROL_AREA NextControlArea;
|
|
#endif
|
|
|
|
MM_PFN_LOCK_ASSERT();
|
|
|
|
ControlArea = (PLARGE_CONTROL_AREA) InputControlArea;
|
|
|
|
//
|
|
// If this is not a global-per-session control area or just a placeholder
|
|
// control area (with no chain already in place) then just put it in.
|
|
//
|
|
|
|
FirstControlArea = (PLARGE_CONTROL_AREA)(File->SectionObjectPointer->ImageSectionObject);
|
|
|
|
if (FirstControlArea == NULL) {
|
|
if (ControlArea->u.Flags.GlobalOnlyPerSession == 0) {
|
|
File->SectionObjectPointer->ImageSectionObject = (PVOID)ControlArea;
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// A per-session control area needs to be inserted...
|
|
//
|
|
|
|
ASSERT (ControlArea->u.Flags.GlobalOnlyPerSession == 1);
|
|
|
|
ControlArea->SessionId = MmGetSessionId (PsGetCurrentProcess());
|
|
|
|
//
|
|
// If the control area list is empty, just initialize links for this entry.
|
|
//
|
|
|
|
if (File->SectionObjectPointer->ImageSectionObject == NULL) {
|
|
InitializeListHead (&ControlArea->UserGlobalList);
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Insert new entry before the current first entry. The control area
|
|
// must be in the midst of creation/deletion or have a valid session
|
|
// ID to be inserted.
|
|
//
|
|
|
|
ASSERT (ControlArea->u.Flags.BeingDeleted ||
|
|
ControlArea->u.Flags.BeingCreated ||
|
|
ControlArea->SessionId != (ULONG)-1);
|
|
|
|
FirstControlArea = (PLARGE_CONTROL_AREA)(File->SectionObjectPointer->ImageSectionObject);
|
|
|
|
Head = &FirstControlArea->UserGlobalList;
|
|
|
|
#if DBG
|
|
//
|
|
// Ensure no duplicate session IDs exist in the list.
|
|
//
|
|
|
|
for (Next = Head->Flink; Next != Head; Next = Next->Flink) {
|
|
NextControlArea = CONTAINING_RECORD (Next, LARGE_CONTROL_AREA, UserGlobalList);
|
|
ASSERT (NextControlArea->SessionId != (ULONG)-1 &&
|
|
NextControlArea->SessionId != ControlArea->SessionId);
|
|
}
|
|
#endif
|
|
|
|
InsertTailList (Head, &ControlArea->UserGlobalList);
|
|
}
|
|
|
|
//
|
|
// Update first control area pointer.
|
|
//
|
|
|
|
File->SectionObjectPointer->ImageSectionObject = (PVOID) ControlArea;
|
|
}
|
|
|
|
VOID
|
|
MiRemoveImageSectionObject(
|
|
IN PFILE_OBJECT File,
|
|
IN PCONTROL_AREA InputControlArea
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function searches the control area chains (if any) for an existing
|
|
cache of the specified image file. For non-global control areas, there is
|
|
no chain and the control area is shared for all callers and sessions.
|
|
Likewise for systemwide global control areas.
|
|
|
|
However, for global PER-SESSION control areas, we must do the walk.
|
|
|
|
Upon finding the specified control area, we unlink it.
|
|
|
|
Arguments:
|
|
|
|
File - Supplies the file object for the image file.
|
|
|
|
InputControlArea - Supplies the control area to remove.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Environment:
|
|
|
|
Must be holding the PFN lock.
|
|
|
|
--*/
|
|
|
|
{
|
|
#if DBG
|
|
PLIST_ENTRY Head;
|
|
#endif
|
|
PLIST_ENTRY Next;
|
|
PLARGE_CONTROL_AREA ControlArea;
|
|
PLARGE_CONTROL_AREA FirstControlArea;
|
|
PLARGE_CONTROL_AREA NextControlArea;
|
|
|
|
MM_PFN_LOCK_ASSERT();
|
|
|
|
ControlArea = (PLARGE_CONTROL_AREA) InputControlArea;
|
|
|
|
FirstControlArea = (PLARGE_CONTROL_AREA)(File->SectionObjectPointer->ImageSectionObject);
|
|
|
|
//
|
|
// Get a pointer to the first control area. If this is not a
|
|
// global-per-session control area, then there is no list, so we're done.
|
|
//
|
|
|
|
if (FirstControlArea->u.Flags.GlobalOnlyPerSession == 0) {
|
|
ASSERT (ControlArea->u.Flags.GlobalOnlyPerSession == 0);
|
|
|
|
File->SectionObjectPointer->ImageSectionObject = NULL;
|
|
return;
|
|
}
|
|
|
|
//
|
|
// A list may exist. Walk it as necessary and delete the requested entry.
|
|
//
|
|
|
|
if (FirstControlArea == ControlArea) {
|
|
|
|
//
|
|
// The first entry is the one to remove. If it is the only entry
|
|
// in the list, then the new first entry pointer will be NULL.
|
|
// Otherwise, get a pointer to the next entry and unlink the current.
|
|
//
|
|
|
|
if (IsListEmpty (&FirstControlArea->UserGlobalList)) {
|
|
NextControlArea = NULL;
|
|
}
|
|
else {
|
|
Next = FirstControlArea->UserGlobalList.Flink;
|
|
RemoveEntryList (&FirstControlArea->UserGlobalList);
|
|
NextControlArea = CONTAINING_RECORD (Next,
|
|
LARGE_CONTROL_AREA,
|
|
UserGlobalList);
|
|
|
|
ASSERT (NextControlArea->u.Flags.GlobalOnlyPerSession == 1);
|
|
}
|
|
|
|
File->SectionObjectPointer->ImageSectionObject = (PVOID)NextControlArea;
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Remove the entry, note that the ImageSectionObject need not be updated
|
|
// as the entry is not at the head.
|
|
//
|
|
|
|
#if DBG
|
|
Head = &FirstControlArea->UserGlobalList;
|
|
|
|
for (Next = Head->Flink; Next != Head; Next = Next->Flink) {
|
|
|
|
NextControlArea = CONTAINING_RECORD (Next,
|
|
LARGE_CONTROL_AREA,
|
|
UserGlobalList);
|
|
|
|
ASSERT (NextControlArea->u.Flags.GlobalOnlyPerSession == 1);
|
|
|
|
if (NextControlArea == ControlArea) {
|
|
break;
|
|
}
|
|
}
|
|
ASSERT (Next != Head);
|
|
#endif
|
|
|
|
RemoveEntryList (&ControlArea->UserGlobalList);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
MiGetWritablePagesInSection (
|
|
IN PSECTION Section,
|
|
OUT PULONG WritablePages
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine calculates the number of writable pages in the image.
|
|
|
|
Arguments:
|
|
|
|
Section - Supplies the section for the image.
|
|
|
|
WritablePages - Supplies a pointer to fill with the
|
|
number of writable pages.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if all went well, otherwise various NTSTATUS error codes.
|
|
|
|
Environment:
|
|
|
|
Kernel mode, APC_LEVEL and below, MmSystemLoadLock held.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PVOID ViewBase;
|
|
SIZE_T ViewSize;
|
|
KAPC_STATE ApcState;
|
|
ULONG PagesInSubsection;
|
|
ULONG Protection;
|
|
ULONG NumberOfSubsections;
|
|
ULONG OffsetToSectionTable;
|
|
ULONG SectionVirtualSize;
|
|
PEPROCESS Process;
|
|
LARGE_INTEGER SectionOffset;
|
|
PIMAGE_DOS_HEADER DosHeader;
|
|
PIMAGE_NT_HEADERS NtHeader;
|
|
PIMAGE_SECTION_HEADER SectionTableEntry;
|
|
|
|
PAGED_CODE();
|
|
|
|
ViewBase = NULL;
|
|
ViewSize = 0;
|
|
SectionOffset.QuadPart = 0;
|
|
|
|
*WritablePages = 0;
|
|
|
|
KeStackAttachProcess (&PsInitialSystemProcess->Pcb, &ApcState);
|
|
|
|
Process = PsGetCurrentProcess();
|
|
|
|
Status = MmMapViewOfSection (Section,
|
|
Process,
|
|
&ViewBase,
|
|
0,
|
|
0,
|
|
&SectionOffset,
|
|
&ViewSize,
|
|
ViewUnmap,
|
|
0,
|
|
PAGE_EXECUTE);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KeUnstackDetachProcess (&ApcState);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// The DLL is mapped as a data file not as an image. Pull apart the
|
|
// executable header. The security checks have already been
|
|
// done as part of creating the section in the first place.
|
|
//
|
|
|
|
DosHeader = (PIMAGE_DOS_HEADER) ViewBase;
|
|
|
|
ASSERT (DosHeader->e_magic == IMAGE_DOS_SIGNATURE);
|
|
|
|
#ifndef i386
|
|
ASSERT (((ULONG)DosHeader->e_lfanew & 3) == 0);
|
|
#endif
|
|
|
|
ASSERT ((ULONG)DosHeader->e_lfanew <= ViewSize);
|
|
|
|
NtHeader = (PIMAGE_NT_HEADERS)((PCHAR)DosHeader +
|
|
(ULONG)DosHeader->e_lfanew);
|
|
|
|
OffsetToSectionTable = sizeof(ULONG) +
|
|
sizeof(IMAGE_FILE_HEADER) +
|
|
NtHeader->FileHeader.SizeOfOptionalHeader;
|
|
|
|
SectionTableEntry = (PIMAGE_SECTION_HEADER)((PCHAR)NtHeader +
|
|
OffsetToSectionTable);
|
|
|
|
NumberOfSubsections = NtHeader->FileHeader.NumberOfSections;
|
|
|
|
while (NumberOfSubsections > 0) {
|
|
|
|
Protection = MiGetImageProtection (SectionTableEntry->Characteristics);
|
|
|
|
if (Protection & MM_PROTECTION_WRITE_MASK) {
|
|
|
|
//
|
|
// Handle the case where the virtual size is 0.
|
|
//
|
|
|
|
if (SectionTableEntry->Misc.VirtualSize == 0) {
|
|
SectionVirtualSize = SectionTableEntry->SizeOfRawData;
|
|
}
|
|
else {
|
|
SectionVirtualSize = SectionTableEntry->Misc.VirtualSize;
|
|
}
|
|
|
|
PagesInSubsection =
|
|
MI_ROUND_TO_SIZE (SectionVirtualSize, PAGE_SIZE) >> PAGE_SHIFT;
|
|
|
|
*WritablePages += PagesInSubsection;
|
|
}
|
|
|
|
SectionTableEntry += 1;
|
|
NumberOfSubsections -= 1;
|
|
}
|
|
|
|
Status = MiUnmapViewOfSection (Process, ViewBase, FALSE);
|
|
|
|
KeUnstackDetachProcess (&ApcState);
|
|
|
|
ASSERT (NT_SUCCESS(Status));
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
LOGICAL
|
|
MiFlushDataSection(
|
|
IN PFILE_OBJECT File
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine flushes the data section if there is one.
|
|
|
|
Arguments:
|
|
|
|
File - Supplies the file object.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Environment:
|
|
|
|
Kernel mode, APC_LEVEL and below.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL OldIrql;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
PCONTROL_AREA ControlArea;
|
|
LOGICAL DataInUse;
|
|
|
|
DataInUse = FALSE;
|
|
|
|
LOCK_PFN (OldIrql);
|
|
|
|
ControlArea = (PCONTROL_AREA) File->SectionObjectPointer->DataSectionObject;
|
|
|
|
if (ControlArea) {
|
|
if (ControlArea->NumberOfSystemCacheViews) {
|
|
UNLOCK_PFN (OldIrql);
|
|
CcFlushCache (File->SectionObjectPointer,
|
|
NULL,
|
|
0,
|
|
&IoStatus);
|
|
|
|
}
|
|
else {
|
|
UNLOCK_PFN (OldIrql);
|
|
MmFlushSection (File->SectionObjectPointer,
|
|
NULL,
|
|
0,
|
|
&IoStatus,
|
|
TRUE);
|
|
}
|
|
|
|
if ((ControlArea->NumberOfSectionReferences != 0) ||
|
|
(ControlArea->NumberOfMappedViews != 0)) {
|
|
|
|
DataInUse = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
UNLOCK_PFN (OldIrql);
|
|
}
|
|
|
|
return DataInUse;
|
|
}
|
|
|
|
|
|
PVOID
|
|
MiCopyHeaderIfResident (
|
|
IN PFILE_OBJECT File,
|
|
IN PFN_NUMBER ImagePageFrameNumber
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine copies the image header from the data section if there is
|
|
one and the page is already resident or in transition.
|
|
|
|
Arguments:
|
|
|
|
File - Supplies the file object.
|
|
|
|
ImagePageFrameNumber - Supplies the image frame to copy the data into.
|
|
|
|
Return Value:
|
|
|
|
Virtual address of the image page frame number if successful, NULL if not.
|
|
|
|
Environment:
|
|
|
|
Kernel mode, APC_LEVEL and below.
|
|
|
|
--*/
|
|
|
|
{
|
|
PMMPFN Pfn1;
|
|
PVOID DataPage;
|
|
PVOID ImagePage;
|
|
KIRQL OldIrql;
|
|
PCONTROL_AREA ControlArea;
|
|
PMMPTE PointerPte;
|
|
MMPTE PteContents;
|
|
PFN_NUMBER PageFrameIndex;
|
|
PEPROCESS Process;
|
|
PSUBSECTION Subsection;
|
|
PSECTION_OBJECT_POINTERS SectionObjectPointer;
|
|
|
|
//
|
|
// Take a quick (safely unsynchronized) look to see whether to bother
|
|
// mapping the image header page at all - if there's no data section
|
|
// object, then skip it and just return.
|
|
//
|
|
|
|
SectionObjectPointer = File->SectionObjectPointer;
|
|
if (SectionObjectPointer == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
ControlArea = (PCONTROL_AREA) SectionObjectPointer->DataSectionObject;
|
|
|
|
if (ControlArea == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// There's a data section, so map the target page.
|
|
//
|
|
|
|
ImagePage = MiMapImageHeaderInHyperSpace (ImagePageFrameNumber);
|
|
|
|
LOCK_PFN (OldIrql);
|
|
|
|
//
|
|
// Now that we are synchronized via the PFN lock, take a safe look.
|
|
//
|
|
|
|
SectionObjectPointer = File->SectionObjectPointer;
|
|
if (SectionObjectPointer == NULL) {
|
|
UNLOCK_PFN (OldIrql);
|
|
MiUnmapImageHeaderInHyperSpace ();
|
|
return NULL;
|
|
}
|
|
|
|
ControlArea = (PCONTROL_AREA) SectionObjectPointer->DataSectionObject;
|
|
|
|
if (ControlArea == NULL) {
|
|
UNLOCK_PFN (OldIrql);
|
|
MiUnmapImageHeaderInHyperSpace ();
|
|
return NULL;
|
|
}
|
|
|
|
if ((ControlArea->u.Flags.BeingCreated) ||
|
|
(ControlArea->u.Flags.BeingDeleted)) {
|
|
|
|
UNLOCK_PFN (OldIrql);
|
|
MiUnmapImageHeaderInHyperSpace ();
|
|
return NULL;
|
|
}
|
|
|
|
if (ControlArea->u.Flags.Rom == 0) {
|
|
Subsection = (PSUBSECTION) (ControlArea + 1);
|
|
}
|
|
else {
|
|
Subsection = (PSUBSECTION)((PLARGE_CONTROL_AREA)ControlArea + 1);
|
|
}
|
|
|
|
//
|
|
// If the prototype PTEs have been tossed (or never created) then we
|
|
// don't have any data to copy.
|
|
//
|
|
|
|
PointerPte = Subsection->SubsectionBase;
|
|
|
|
if (PointerPte == NULL) {
|
|
UNLOCK_PFN (OldIrql);
|
|
MiUnmapImageHeaderInHyperSpace ();
|
|
return NULL;
|
|
}
|
|
|
|
if (MiGetPteAddress (PointerPte)->u.Hard.Valid == 0) {
|
|
|
|
//
|
|
// We have no reference to the data section so if we can't do this
|
|
// without relinquishing the PFN lock, then don't bother.
|
|
// ie: the entire control area and everything can be freed
|
|
// while a call to MiMakeSystemAddressValidPfn releases the lock.
|
|
//
|
|
|
|
UNLOCK_PFN (OldIrql);
|
|
MiUnmapImageHeaderInHyperSpace ();
|
|
return NULL;
|
|
}
|
|
|
|
PteContents = *PointerPte;
|
|
|
|
if ((PteContents.u.Hard.Valid == 1) ||
|
|
((PteContents.u.Soft.Prototype == 0) &&
|
|
(PteContents.u.Soft.Transition == 1))) {
|
|
|
|
if (PteContents.u.Hard.Valid == 1) {
|
|
PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (&PteContents);
|
|
}
|
|
else {
|
|
PageFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&PteContents);
|
|
Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
|
|
|
|
if (Pfn1->u3.e1.ReadInProgress != 0) {
|
|
UNLOCK_PFN (OldIrql);
|
|
MiUnmapImageHeaderInHyperSpace ();
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
Process = PsGetCurrentProcess ();
|
|
|
|
DataPage = MiMapPageInHyperSpaceAtDpc (Process, PageFrameIndex);
|
|
|
|
RtlCopyMemory (ImagePage, DataPage, PAGE_SIZE);
|
|
|
|
MiUnmapPageInHyperSpaceFromDpc (Process, DataPage);
|
|
|
|
UNLOCK_PFN (OldIrql);
|
|
|
|
return ImagePage;
|
|
}
|
|
|
|
//
|
|
// The data page is not resident, so return NULL and the caller will take
|
|
// the long way.
|
|
//
|
|
|
|
UNLOCK_PFN (OldIrql);
|
|
MiUnmapImageHeaderInHyperSpace ();
|
|
return NULL;
|
|
}
|