[SMSS][NTOS:MM] Implement the architecture-specific pagefile size limits + code revie... 4843/head
authorHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Wed, 2 Nov 2022 23:12:09 +0000 (00:12 +0100)
committerHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Wed, 16 Nov 2022 20:54:31 +0000 (21:54 +0100)
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
ntoskrnl/mm/pagefile.c

index 2eed8a7..c811595 100644 (file)
 //
 #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;
index 1387a38..a57c4de 100644 (file)
@@ -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 <welch@mcmail.com>
+ *              Copyright 2010-2018 Pierre Schweitzer <pierre@reactos.org>
  */
 
 /* INCLUDES *****************************************************************/
 
 /* 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)
-        {
-            i = 0;
 
-            while (MmPagingFile[i]->FileObject->SectionObjectPointer != FileObject->SectionObjectPointer)
+        for (i = 0; i < MmNumberOfPagingFiles; ++i)
+        {
+            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++;