]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-144319: obtain SeLockMemoryPrivilege on Windows (#144928)
authorChris Eibl <138194463+chris-eibl@users.noreply.github.com>
Mon, 23 Mar 2026 23:00:26 +0000 (00:00 +0100)
committerGitHub <noreply@github.com>
Mon, 23 Mar 2026 23:00:26 +0000 (23:00 +0000)
Doc/using/cmdline.rst
Doc/using/configure.rst
Python/pylifecycle.c

index ce6872f3c0fda3a9ed99ad2d524bd0cab8efb5c8..73cd8d31d0b20db5a2accc8abfd9aacebb4c8e59 100644 (file)
@@ -1132,6 +1132,14 @@ conflict.
       and kill the process.  Only enable this in environments where the
       huge-page pool is properly sized and fork-safety is not a concern.
 
+      On Windows you need a special privilege. See the
+      `Windows documentation for large pages
+      <https://learn.microsoft.com/windows/win32/memory/large-page-support>`_
+      for details. Python will fail on startup if the required privilege
+      `SeLockMemoryPrivilege
+      <https://learn.microsoft.com/previous-versions/windows/it-pro/windows-10/security/threat-protection/security-policy-settings/lock-pages-in-memory>`_
+      is not held by the user.
+
    .. versionadded:: 3.15
 
 
index 6bef290d181fc9729613b50eb728e9bde7963d8f..084c27d2155d0b67c9d41da5dda85fcf359f3495 100644 (file)
@@ -795,9 +795,18 @@ also be used to improve performance.
 
    Even when compiled with this option, huge pages are **not** used at runtime
    unless the :envvar:`PYTHON_PYMALLOC_HUGEPAGES` environment variable is set
-   to ``1``. This opt-in is required because huge pages carry risks on Linux:
-   if the huge-page pool is exhausted, page faults (including copy-on-write
-   faults after :func:`os.fork`) deliver ``SIGBUS`` and kill the process.
+   to ``1``. This opt-in is required because huge pages
+
+   * carry risks on Linux: if the huge-page pool is exhausted, page faults
+     (including copy-on-write faults after :func:`os.fork`) deliver ``SIGBUS``
+     and kill the process.
+
+   * need a special privilege on Windows. See the `Windows documentation for large pages
+     <https://learn.microsoft.com/windows/win32/memory/large-page-support>`_
+     for details. Python will fail on startup if the required privilege
+     `SeLockMemoryPrivilege
+     <https://learn.microsoft.com/previous-versions/windows/it-pro/windows-10/security/threat-protection/security-policy-settings/lock-pages-in-memory>`_
+     is not held by the user.
 
    The configure script checks that the platform supports ``MAP_HUGETLB``
    and emits a warning if it is not available.
index 68052a91d6a1f152905fc01d0570bd66073411c0..5da0f3e5be3a70f85efc5959b695a2f0134d678a 100644 (file)
 #include "pycore_jit.h"           // _PyJIT_Fini()
 #endif
 
+#if defined(PYMALLOC_USE_HUGEPAGES) && defined(MS_WINDOWS)
+#include <Windows.h>
+#endif
+
 #include "opcode.h"
 
 #include <locale.h>               // setlocale()
@@ -486,6 +490,41 @@ pyinit_core_reconfigure(_PyRuntimeState *runtime,
     return _PyStatus_OK();
 }
 
+#if defined(PYMALLOC_USE_HUGEPAGES) && defined(MS_WINDOWS)
+static PyStatus
+get_huge_pages_privilege(void)
+{
+    HANDLE hToken;
+    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
+    {
+        return _PyStatus_ERR("failed to open process token");
+    }
+    TOKEN_PRIVILEGES tp;
+    if (!LookupPrivilegeValue(NULL, SE_LOCK_MEMORY_NAME, &tp.Privileges[0].Luid))
+    {
+        CloseHandle(hToken);
+        return _PyStatus_ERR("failed to lookup SeLockMemoryPrivilege for huge pages");
+    }
+    tp.PrivilegeCount = 1;
+    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+    // AdjustTokenPrivileges can return with nonzero status (i.e. success)
+    // but without having all privileges adjusted (ERROR_NOT_ALL_ASSIGNED).
+    BOOL status = AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL);
+    DWORD error = GetLastError();
+    if (!status || (error != ERROR_SUCCESS))
+    {
+        CloseHandle(hToken);
+        return _PyStatus_ERR(
+            "SeLockMemoryPrivilege not held; "
+            "grant it via Local Security Policy or disable PYTHON_PYMALLOC_HUGEPAGES");
+    }
+    if (!CloseHandle(hToken))
+    {
+        return _PyStatus_ERR("failed to close process token handle");
+    }
+    return _PyStatus_OK();
+}
+#endif
 
 static PyStatus
 pycore_init_runtime(_PyRuntimeState *runtime,
@@ -500,6 +539,15 @@ pycore_init_runtime(_PyRuntimeState *runtime,
         return status;
     }
 
+#if defined(PYMALLOC_USE_HUGEPAGES) && defined(MS_WINDOWS)
+    if (runtime->allocators.use_hugepages) {
+        status = get_huge_pages_privilege();
+        if (_PyStatus_EXCEPTION(status)) {
+            return status;
+        }
+    }
+#endif
+
     /* Py_Finalize leaves _Py_Finalizing set in order to help daemon
      * threads behave a little more gracefully at interpreter shutdown.
      * We clobber it here so the new interpreter can start with a clean