From a4274ad54837287bca58ca698b5a0ce237d0fd9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herm=C3=A8s=20B=C3=A9lusca-Ma=C3=AFto?= Date: Thu, 3 Nov 2022 00:12:09 +0100 Subject: [PATCH] [SMSS][NTOS:MM] Implement the architecture-specific pagefile size limits + code review. (#4843) What we have: - Maximum number of pagefiles: 16 - Minimum pagefile size: 256 pages (1 MB when page size = 4096 bytes) - Maximum pagefile size: * 32-bit platforms: (1024 * 1024 - 1) pages (~ 4095 MB) * x86 with PAE support: same size as for AMD x64 * x64 platform: (4 * 1024 * 1024 * 1024 - 1) pages (~ 16 TB) * IA64 platform: (8 * 1024 * 1024 * 1024 - 1) pages (~ 32 TB) Those are the values as supported and verified by the NT kernel. Now, user-mode programs (including SMSS.EXE) have different opinions on these, namely, they consider estimates directly in MB, respectively: 4095 MB, (16 * 1024 * 1024) MB, and (32 * 1024 * 1024) MB (verified on Win2k3 and Win7 32 and 64 bits). Also here, the minimum pagefile size is set to 2 MB. Starting Windows 8+ (and 10), those values change slightly, and are still not fully synchronized between NTOS:MM and SMSS. Finally, while (x86 PAE and) AMD64 and ARM64 seem to share the maximum pagefile size limit, 32-bit ARMv7 appears to use different limits than regular x86 (2 GB instead of 4). Please keep those values as they are for NT compatibility! See the following references: https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/mm/modwrite/create.htm https://techcommunity.microsoft.com/t5/ask-the-performance-team/what-is-the-page-file-for-anyway/ba-p/372608 + Manual extraction of the values from different NT 6.2,6.3,10 builds. [SMSS] Fill out in particular the x86-specific case for PAE. [NTOS:MM] Some cleanup in the NtCreatePagingFile() code, namely: - Clarify some comments; - Validate the lower and upper bounds of the Minimum and Maximum sizes (based on Windows behaviour as explained by Geoff + manual tests). - Open the pagefile in case-insensitive; - Simplify the loop that finds an existing matching pagefile; - Simplify some failure exit paths; - Add a "Missing validation steps TODO" comment block explaining the existing code-hole. --- base/system/smss/pagefile.c | 106 ++++++++++++----- ntoskrnl/mm/pagefile.c | 220 ++++++++++++++++++------------------ 2 files changed, 191 insertions(+), 135 deletions(-) diff --git a/base/system/smss/pagefile.c b/base/system/smss/pagefile.c index 2eed8a7236d..c8115955c4f 100644 --- a/base/system/smss/pagefile.c +++ b/base/system/smss/pagefile.c @@ -20,8 +20,63 @@ // #define STANDARD_PAGING_FILE_NAME L"\\??\\?:\\pagefile.sys" #define STANDARD_DRIVE_LETTER_OFFSET 4 -#define MEGABYTE 0x100000UL -#define MAXIMUM_PAGEFILE_SIZE (4095 * MEGABYTE) +#define MAX_PAGING_FILES 16 // See also ntoskrnl/include/internal/mm.h +#define MEGABYTE (1024 * 1024) + +/* Minimum pagefile size is 256 pages (1 MB) */ +// #define MINIMUM_PAGEFILE_SIZE (256ULL * PAGE_SIZE) + +/* Maximum pagefile sizes for different architectures */ +#define GIGABYTE (1024ULL * MEGABYTE) +#define TERABYTE (1024ULL * GIGABYTE) + +// NOTE: No changes for NTDDI_WIN10 +#if (NTDDI_VERSION >= NTDDI_WINBLUE) // NTDDI_WIN81 +#define MAXIMUM_PAGEFILE_SIZE32 ((1ULL * 1024 * 1024 - 1) * PAGE_SIZE) + // PAGE_ROUND_DOWN(4ULL * GIGABYTE - 1) +#else +/* 4095 MB */ +#define MAXIMUM_PAGEFILE_SIZE32 (4095ULL * MEGABYTE) +#endif + +// NOTE: No changes for NTDDI_WIN10 +#if (NTDDI_VERSION >= NTDDI_WINBLUE) // NTDDI_WIN81 +#define MAXIMUM_PAGEFILE_SIZE64 ((4ULL * 1024 * 1024 * 1024 - 1) * PAGE_SIZE) + // PAGE_ROUND_DOWN(16ULL * TERABYTE - 1) +#else +/* 16 TB */ +#define MAXIMUM_PAGEFILE_SIZE64 (16ULL * TERABYTE) +#endif + +#if defined(_M_IX86) + #define MAXIMUM_PAGEFILE_SIZE MAXIMUM_PAGEFILE_SIZE32 + /* PAE uses the same size as x64 */ + #define MAXIMUM_PAGEFILE_SIZE_PAE MAXIMUM_PAGEFILE_SIZE64 +#elif defined (_M_AMD64) || defined(_M_ARM64) + #define MAXIMUM_PAGEFILE_SIZE MAXIMUM_PAGEFILE_SIZE64 +#elif defined (_M_IA64) +/* 32 TB */ + #define MAXIMUM_PAGEFILE_SIZE (32ULL * TERABYTE) +#elif defined(_M_ARM) +/* Around 2 GB */ + // NOTE: No changes for NTDDI_WIN10 + #if (NTDDI_VERSION >= NTDDI_WINBLUE) // NTDDI_WIN81 + #define MAXIMUM_PAGEFILE_SIZE ((512ULL * 1024 - 1) * PAGE_SIZE) + // PAGE_ROUND_DOWN(2ULL * GIGABYTE - 1) + #else +/* 4095 MB */ + #define MAXIMUM_PAGEFILE_SIZE MAXIMUM_PAGEFILE_SIZE32 + #endif +#else +/* On unknown architectures, default to either one of the 32 or 64 bit sizes */ +#pragma message("Unknown architecture") + #ifdef _WIN64 + #define MAXIMUM_PAGEFILE_SIZE MAXIMUM_PAGEFILE_SIZE64 + #else + #define MAXIMUM_PAGEFILE_SIZE MAXIMUM_PAGEFILE_SIZE32 + #endif +#endif + /* This should be 32 MB, but we need more than that for 2nd stage setup */ #define MINIMUM_TO_KEEP_FREE (256 * MEGABYTE) #define FUZZ_FACTOR (16 * MEGABYTE) @@ -93,7 +148,7 @@ SmpCreatePagingFileDescriptor(IN PUNICODE_STRING PageFileToken) UNICODE_STRING PageFileName, Arguments, SecondArgument; /* Make sure we don't have too many */ - if (SmpNumberOfPagingFiles >= 16) + if (SmpNumberOfPagingFiles >= MAX_PAGING_FILES) { DPRINT1("SMSS:PFILE: Too many paging files specified - %lu\n", SmpNumberOfPagingFiles); @@ -110,7 +165,7 @@ SmpCreatePagingFileDescriptor(IN PUNICODE_STRING PageFileToken) if (!NT_SUCCESS(Status)) { /* Fail */ - DPRINT1("SMSS:PFILE: SmpParseCommandLine( %wZ ) failed - Status == %lx\n", + DPRINT1("SMSS:PFILE: SmpParseCommandLine(%wZ) failed - Status == %lx\n", PageFileToken, Status); return Status; } @@ -198,7 +253,8 @@ SmpCreatePagingFileDescriptor(IN PUNICODE_STRING PageFileToken) Descriptor->Name = PageFileName; Descriptor->MinSize.QuadPart = MinSize * MEGABYTE; Descriptor->MaxSize.QuadPart = MaxSize * MEGABYTE; - if (SystemManaged) Descriptor->Flags |= SMP_PAGEFILE_SYSTEM_MANAGED; + if (SystemManaged) + Descriptor->Flags |= SMP_PAGEFILE_SYSTEM_MANAGED; Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] = RtlUpcaseUnicodeChar(Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET]); if (Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] == '?') @@ -659,7 +715,7 @@ NTAPI SmpMakeSystemManagedPagingFileDescriptor(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor) { NTSTATUS Status; - LONGLONG MinimumSize, MaximumSize, Ram; + ULONGLONG MinimumSize, MaximumSize, Ram; SYSTEM_BASIC_INFORMATION BasicInfo; /* Query the page size of the system, and the amount of RAM */ @@ -693,8 +749,15 @@ NTAPI SmpValidatePagingFileSizes(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor) { NTSTATUS Status = STATUS_SUCCESS; - ULONGLONG MinSize, MaxSize; BOOLEAN WasTooBig = FALSE; + ULONGLONG MinSize, MaxSize; +#ifdef _M_IX86 + ULONGLONG MaxPageFileSize = + (SharedUserData->ProcessorFeatures[PF_PAE_ENABLED]) + ? MAXIMUM_PAGEFILE_SIZE_PAE : MAXIMUM_PAGEFILE_SIZE; +#else + static const ULONGLONG MaxPageFileSize = MAXIMUM_PAGEFILE_SIZE; +#endif /* Capture the min and max */ MinSize = Descriptor->MinSize.QuadPart; @@ -704,28 +767,19 @@ SmpValidatePagingFileSizes(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor) &Descriptor->Name, MinSize, MaxSize); /* Don't let minimum be bigger than maximum */ - if (MinSize > MaxSize) MaxSize = MinSize; + if (MinSize > MaxSize) + MaxSize = MinSize; - /* On PAE we can have bigger pagefiles... */ - if (SharedUserData->ProcessorFeatures[PF_PAE_ENABLED]) + /* Validate the minimum and maximum and trim them if they are too large */ + if (MinSize > MaxPageFileSize) { - /* But we don't support that yet */ - DPRINT1("ReactOS does not support PAE yet... assuming sizes OK\n"); + WasTooBig = TRUE; + MinSize = MaxPageFileSize; } - else + if (MaxSize > MaxPageFileSize) { - /* Validate the minimum and maximum and trim them if they are too large */ - if (MinSize > MAXIMUM_PAGEFILE_SIZE) - { - WasTooBig = TRUE; - MinSize = MAXIMUM_PAGEFILE_SIZE; - } - - if (MaxSize > MAXIMUM_PAGEFILE_SIZE) - { - WasTooBig = TRUE; - MaxSize = MAXIMUM_PAGEFILE_SIZE; - } + WasTooBig = TRUE; + MaxSize = MaxPageFileSize; } /* If we trimmed, write a flag in the descriptor */ @@ -752,7 +806,7 @@ SmpCreateSystemManagedPagingFile(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor, /* Make sure there is at least 1 paging file and that we are system-managed */ ASSERT(SmpNumberOfPagingFiles >= 1); ASSERT(!IsListEmpty(&SmpPagingFileDescriptorList)); - ASSERT(Descriptor->Flags & SMP_PAGEFILE_SYSTEM_MANAGED); // Descriptor->SystemManaged == 1 in ASSERT. + ASSERT(Descriptor->Flags & SMP_PAGEFILE_SYSTEM_MANAGED); /* Keep decreasing the pagefile by this amount if we run out of space */ FuzzFactor.QuadPart = FUZZ_FACTOR; diff --git a/ntoskrnl/mm/pagefile.c b/ntoskrnl/mm/pagefile.c index 1387a38527b..a57c4de0304 100644 --- a/ntoskrnl/mm/pagefile.c +++ b/ntoskrnl/mm/pagefile.c @@ -1,29 +1,9 @@ /* - * ReactOS kernel - * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/* - * PROJECT: ReactOS kernel - * FILE: ntoskrnl/mm/pagefile.c - * PURPOSE: Paging file functions - * PROGRAMMER: David Welch (welch@mcmail.com) - * Pierre Schweitzer - * UPDATE HISTORY: - * Created 22/05/98 + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Paging file functions + * COPYRIGHT: Copyright 1998-2003 David Welch + * Copyright 2010-2018 Pierre Schweitzer */ /* INCLUDES *****************************************************************/ @@ -34,7 +14,44 @@ /* GLOBALS *******************************************************************/ -#define PAIRS_PER_RUN (1024) +/* Minimum pagefile size is 256 pages (1 MB) */ +#define MINIMUM_PAGEFILE_SIZE (256ULL * PAGE_SIZE) + +/* Maximum pagefile sizes for different architectures */ +#if defined(_M_IX86) && !defined(_X86PAE_) +/* Around 4 GB */ + #define MAXIMUM_PAGEFILE_SIZE ((1ULL * 1024 * 1024 - 1) * PAGE_SIZE) + // PAGE_ROUND_DOWN(4ULL * GIGABYTE - 1) +/* PAE uses the same size as x64 */ +#elif (defined(_M_IX86) && defined(_X86PAE_)) || defined (_M_AMD64) || defined(_M_ARM64) +/* Around 16 TB */ + #if (NTDDI_VERSION >= NTDDI_WIN10) + #define MAXIMUM_PAGEFILE_SIZE ((4ULL * 1024 * 1024 * 1024 - 2) * PAGE_SIZE) + // PAGE_ROUND_DOWN(16ULL * TERABYTE - PAGE_SIZE - 1) + #else + #define MAXIMUM_PAGEFILE_SIZE ((4ULL * 1024 * 1024 * 1024 - 1) * PAGE_SIZE) + // PAGE_ROUND_DOWN(16ULL * TERABYTE - 1) + #endif +#elif defined (_M_IA64) +/* Around 32 TB */ + #define MAXIMUM_PAGEFILE_SIZE ((8ULL * 1024 * 1024 * 1024 - 1) * PAGE_SIZE) + // PAGE_ROUND_DOWN(32ULL * TERABYTE - 1) +#elif defined(_M_ARM) +/* Around 2 GB */ + #if (NTDDI_VERSION >= NTDDI_WIN10) + #define MAXIMUM_PAGEFILE_SIZE ((512ULL * 1024 - 2) * PAGE_SIZE) + // PAGE_ROUND_DOWN(2ULL * GIGABYTE - PAGE_SIZE - 1) + #elif (NTDDI_VERSION >= NTDDI_WINBLUE) // NTDDI_WIN81 + #define MAXIMUM_PAGEFILE_SIZE ((512ULL * 1024 - 1) * PAGE_SIZE) + // PAGE_ROUND_DOWN(2ULL * GIGABYTE - 1) + #else +/* Around 4 GB */ + #define MAXIMUM_PAGEFILE_SIZE ((1ULL * 1024 * 1024 - 1) * PAGE_SIZE) + // PAGE_ROUND_DOWN(4ULL * GIGABYTE - 1) + #endif +#else +#error Unknown architecture +#endif /* List of paging files, both used and free */ PMMPAGING_FILE MmPagingFile[MAX_PAGING_FILES]; @@ -409,33 +426,32 @@ NtCreatePagingFile( } /* - * Pagefiles can't be larger than 4GB and of course - * the minimum should be smaller than the maximum. + * Pagefiles cannot be larger than the platform-specific memory addressable + * limits, and of course the minimum should be smaller than the maximum. */ - // TODO: Actually validate the lower bound of these sizes! - if (0 != SafeMinimumSize.u.HighPart) + if (SafeMinimumSize.QuadPart < MINIMUM_PAGEFILE_SIZE || + SafeMinimumSize.QuadPart > MAXIMUM_PAGEFILE_SIZE) { return STATUS_INVALID_PARAMETER_2; } - if (0 != SafeMaximumSize.u.HighPart) + if (SafeMaximumSize.QuadPart < SafeMinimumSize.QuadPart || + SafeMaximumSize.QuadPart > MAXIMUM_PAGEFILE_SIZE) { return STATUS_INVALID_PARAMETER_3; } - if (SafeMaximumSize.u.LowPart < SafeMinimumSize.u.LowPart) - { - return STATUS_INVALID_PARAMETER_MIX; - } /* Validate the name length */ if ((PageFileName.Length == 0) || - (PageFileName.Length > 128 * sizeof(WCHAR))) + (PageFileName.Length > MAXIMUM_FILENAME_LENGTH)) { return STATUS_OBJECT_NAME_INVALID; } - /* We don't care about any potential UNICODE_NULL */ + /* Allocate a buffer to keep the name copy. Note that it is kept only + * for information purposes, so it gets allocated in the paged pool, + * even if it will be stored in the PagingFile structure, that is + * allocated from non-paged pool (see below). */ PageFileName.MaximumLength = PageFileName.Length; - /* Allocate a buffer to keep the name copy */ Buffer = ExAllocatePoolWithTag(PagedPool, PageFileName.Length, TAG_MM); if (Buffer == NULL) { @@ -488,42 +504,26 @@ NtCreatePagingFile( /* Initialize the DACL */ Status = RtlCreateAcl(Dacl, Count, ACL_REVISION); if (!NT_SUCCESS(Status)) - { - ExFreePoolWithTag(Dacl, TAG_DACL); - ExFreePoolWithTag(Buffer, TAG_MM); - return Status; - } + goto EarlyQuit; /* Grant full access to admins */ Status = RtlAddAccessAllowedAce(Dacl, ACL_REVISION, FILE_ALL_ACCESS, SeAliasAdminsSid); if (!NT_SUCCESS(Status)) - { - ExFreePoolWithTag(Dacl, TAG_DACL); - ExFreePoolWithTag(Buffer, TAG_MM); - return Status; - } + goto EarlyQuit; /* Grant full access to SYSTEM */ Status = RtlAddAccessAllowedAce(Dacl, ACL_REVISION, FILE_ALL_ACCESS, SeLocalSystemSid); if (!NT_SUCCESS(Status)) - { - ExFreePoolWithTag(Dacl, TAG_DACL); - ExFreePoolWithTag(Buffer, TAG_MM); - return Status; - } + goto EarlyQuit; /* Attach the DACL to the security descriptor */ Status = RtlSetDaclSecurityDescriptor(&SecurityDescriptor, TRUE, Dacl, FALSE); if (!NT_SUCCESS(Status)) - { - ExFreePoolWithTag(Dacl, TAG_DACL); - ExFreePoolWithTag(Buffer, TAG_MM); - return Status; - } + goto EarlyQuit; InitializeObjectAttributes(&ObjectAttributes, &PageFileName, - OBJ_KERNEL_HANDLE, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, &SecurityDescriptor); @@ -549,10 +549,10 @@ NtCreatePagingFile( 0, CreateFileTypeNone, NULL, - SL_OPEN_PAGING_FILE | IO_NO_PARAMETER_CHECKING); + IO_OPEN_PAGING_FILE | IO_NO_PARAMETER_CHECKING); /* If we failed, relax a bit constraints, someone may be already holding the * the file, so share write, don't attempt to replace and don't delete on close - * (basically, don't do anything conflicting) + * (basically, don't do anything conflicting). * This can happen if the caller attempts to extend a page file. */ if (!NT_SUCCESS(Status)) @@ -572,13 +572,9 @@ NtCreatePagingFile( 0, CreateFileTypeNone, NULL, - SL_OPEN_PAGING_FILE | IO_NO_PARAMETER_CHECKING); + IO_OPEN_PAGING_FILE | IO_NO_PARAMETER_CHECKING); if (!NT_SUCCESS(Status)) - { - ExFreePoolWithTag(Dacl, TAG_DACL); - ExFreePoolWithTag(Buffer, TAG_MM); - return Status; - } + goto EarlyQuit; /* We opened it! Check we are that "someone" ;-) * First, get the opened file object. @@ -592,33 +588,22 @@ NtCreatePagingFile( if (!NT_SUCCESS(Status)) { ZwClose(FileHandle); - ExFreePoolWithTag(Dacl, TAG_DACL); - ExFreePoolWithTag(Buffer, TAG_MM); - return Status; + goto EarlyQuit; } /* Find if it matches a previous page file */ PagingFile = NULL; - /* FIXME: should be calling unsafe instead, - * we should already be in a guarded region - */ KeAcquireGuardedMutex(&MmPageFileCreationLock); - if (MmNumberOfPagingFiles > 0) + + for (i = 0; i < MmNumberOfPagingFiles; ++i) { - i = 0; - - while (MmPagingFile[i]->FileObject->SectionObjectPointer != FileObject->SectionObjectPointer) + if (MmPagingFile[i]->FileObject->SectionObjectPointer == FileObject->SectionObjectPointer) { - ++i; - if (i >= MmNumberOfPagingFiles) - { - break; - } + /* Same object pointer: this is the matching page file */ + PagingFile = MmPagingFile[i]; + break; } - - /* This is the matching page file */ - PagingFile = MmPagingFile[i]; } /* If we didn't find the page file, fail */ @@ -627,9 +612,8 @@ NtCreatePagingFile( KeReleaseGuardedMutex(&MmPageFileCreationLock); ObDereferenceObject(FileObject); ZwClose(FileHandle); - ExFreePoolWithTag(Dacl, TAG_DACL); - ExFreePoolWithTag(Buffer, TAG_MM); - return STATUS_NOT_FOUND; + Status = STATUS_NOT_FOUND; + goto EarlyQuit; } /* Don't allow page file shrinking */ @@ -638,9 +622,8 @@ NtCreatePagingFile( KeReleaseGuardedMutex(&MmPageFileCreationLock); ObDereferenceObject(FileObject); ZwClose(FileHandle); - ExFreePoolWithTag(Dacl, TAG_DACL); - ExFreePoolWithTag(Buffer, TAG_MM); - return STATUS_INVALID_PARAMETER_2; + Status = STATUS_INVALID_PARAMETER_2; + goto EarlyQuit; } if ((SafeMaximumSize.QuadPart >> PAGE_SHIFT) < PagingFile->MaximumSize) @@ -648,9 +631,8 @@ NtCreatePagingFile( KeReleaseGuardedMutex(&MmPageFileCreationLock); ObDereferenceObject(FileObject); ZwClose(FileHandle); - ExFreePoolWithTag(Dacl, TAG_DACL); - ExFreePoolWithTag(Buffer, TAG_MM); - return STATUS_INVALID_PARAMETER_3; + Status = STATUS_INVALID_PARAMETER_3; + goto EarlyQuit; } /* FIXME: implement parameters checking and page file extension */ @@ -659,13 +641,13 @@ NtCreatePagingFile( KeReleaseGuardedMutex(&MmPageFileCreationLock); ObDereferenceObject(FileObject); ZwClose(FileHandle); - ExFreePoolWithTag(Dacl, TAG_DACL); - ExFreePoolWithTag(Buffer, TAG_MM); - return STATUS_NOT_IMPLEMENTED; + Status = STATUS_NOT_IMPLEMENTED; + goto EarlyQuit; } if (!NT_SUCCESS(Status)) { +EarlyQuit: DPRINT1("Failed creating page file: %lx\n", Status); ExFreePoolWithTag(Dacl, TAG_DACL); ExFreePoolWithTag(Buffer, TAG_MM); @@ -727,8 +709,10 @@ NtCreatePagingFile( /* Only allow page file on a few device types */ DeviceType = IoGetRelatedDeviceObject(FileObject)->DeviceType; - if (DeviceType != FILE_DEVICE_DISK_FILE_SYSTEM && DeviceType != FILE_DEVICE_NETWORK_FILE_SYSTEM && - DeviceType != FILE_DEVICE_DFS_VOLUME && DeviceType != FILE_DEVICE_DFS_FILE_SYSTEM) + if (DeviceType != FILE_DEVICE_DISK_FILE_SYSTEM && + DeviceType != FILE_DEVICE_NETWORK_FILE_SYSTEM && + DeviceType != FILE_DEVICE_DFS_VOLUME && + DeviceType != FILE_DEVICE_DFS_FILE_SYSTEM) { ObDereferenceObject(FileObject); ZwClose(FileHandle); @@ -738,7 +722,8 @@ NtCreatePagingFile( /* Deny page file creation on a floppy disk */ FsDeviceInfo.Characteristics = 0; - IoQueryVolumeInformation(FileObject, FileFsDeviceInformation, sizeof(FsDeviceInfo), &FsDeviceInfo, &Count); + IoQueryVolumeInformation(FileObject, FileFsDeviceInformation, + sizeof(FsDeviceInfo), &FsDeviceInfo, &Count); if (BooleanFlagOn(FsDeviceInfo.Characteristics, FILE_FLOPPY_DISKETTE)) { ObDereferenceObject(FileObject); @@ -747,7 +732,27 @@ NtCreatePagingFile( return STATUS_FLOPPY_VOLUME; } - PagingFile = ExAllocatePoolWithTag(NonPagedPool, sizeof(*PagingFile), TAG_MM); + /* + * Missing validation steps TODO: + * (see https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/mm/modwrite/create.htm ) + * + * - Verify that no file system driver or any filter driver has done file + * I/O while opening the file. + * Verify that nothing of the paging file is yet in memory. Specifically, + * the file object must either have no SectionObjectPointer or the latter + * must have neither a DataSectionObject nor an ImageSectionObject. + * Otherwise, we should fail, returning STATUS_INCOMPATIBLE_FILE_MAP. + * + * - Inform all the applicable drivers to prepare for the possibility of + * paging I/O. Much of the point to paging I/O is to resolve page faults. + * Especially important is that drivers that handle paging I/O do not + * cause more page faults. All the code and data that each driver might + * ever use for access to the paging file must be locked into physical + * memory. This can’t be left until paging I/O actually occurs. + * It must be done in advance. + */ + + PagingFile = ExAllocatePoolZero(NonPagedPool, sizeof(*PagingFile), TAG_MM); if (PagingFile == NULL) { ObDereferenceObject(FileObject); @@ -756,17 +761,15 @@ NtCreatePagingFile( return STATUS_INSUFFICIENT_RESOURCES; } - RtlZeroMemory(PagingFile, sizeof(*PagingFile)); - PagingFile->FileHandle = FileHandle; PagingFile->FileObject = FileObject; - PagingFile->MaximumSize = (SafeMaximumSize.QuadPart >> PAGE_SHIFT); PagingFile->Size = (SafeMinimumSize.QuadPart >> PAGE_SHIFT); - PagingFile->MinimumSize = (SafeMinimumSize.QuadPart >> PAGE_SHIFT); + PagingFile->MinimumSize = PagingFile->Size; + PagingFile->MaximumSize = (SafeMaximumSize.QuadPart >> PAGE_SHIFT); /* First page is never used: it's the header * TODO: write it */ - PagingFile->FreeSpace = (ULONG)(SafeMinimumSize.QuadPart / PAGE_SIZE) - 1; + PagingFile->FreeSpace = PagingFile->Size - 1; PagingFile->CurrentUsage = 0; PagingFile->PageFileName = PageFileName; ASSERT(PagingFile->Size == PagingFile->FreeSpace + PagingFile->CurrentUsage + 1); @@ -789,10 +792,9 @@ NtCreatePagingFile( (ULONG)(PagingFile->MaximumSize)); RtlClearAllBits(PagingFile->Bitmap); - /* FIXME: should be calling unsafe instead, - * we should already be in a guarded region - */ + /* Insert the new paging file information into the list */ KeAcquireGuardedMutex(&MmPageFileCreationLock); + /* Ensure the corresponding slot is empty yet */ ASSERT(MmPagingFile[MmNumberOfPagingFiles] == NULL); MmPagingFile[MmNumberOfPagingFiles] = PagingFile; MmNumberOfPagingFiles++;