Changing the access property of a large memory page, by 2 calls to VirtualAlloc: fails

PIERRE REISS LABORDE 101 Reputation points
2022-11-24T16:49:23.357+00:00

The first test uses VirtualAlloc on a 2Mb page of writecombine type. A second VirtualAlloc attempts the switch of this page to shared type. It fails with error ERROR_INVALID_ADDRESS:
(The "lock pages in memory" privilege is assigned to my account, which owns administrator rights.)

#include "windows.h"  
#include "stdio.h"  
  
int main ()  
  {  
  TOKEN_PRIVILEGES ToPri;  
  LUID             Luid;  
  HANDLE           hToken;  
  LPVOID           Base, Page;  
  SIZE_T           Large = 0x200000;  
  int              *Pin;  
  
  if ( !OpenProcessToken (  
         GetCurrentProcess (),  
         TOKEN_ADJUST_PRIVILEGES,  
         &hToken ) )  
    { printf ( "hToken fails\n" );   return 1; }  
  
  if ( !LookupPrivilegeValue (  
         NULL,  
         "SeLockMemoryPrivilege",  
         &Luid ) )  
    { printf ( "Lookup fails\n" );   return 2; }  
  
  ToPri.PrivilegeCount = 1;  
  ToPri.Privileges[0].Luid = Luid;  
  ToPri.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;  
  if ( !AdjustTokenPrivileges (  
         hToken,  
         FALSE,  
         &ToPri,  
         0,  
         (PTOKEN_PRIVILEGES) NULL,  
         (PDWORD) NULL ) )  
    { printf ( "Adjust fails\n" );   return 3; }  
  if ( GetLastError () == ERROR_NOT_ALL_ASSIGNED )  
    { printf ( "token without privilege\n" );   return 4; }  
  
  Base = VirtualAlloc (  
           0,  
           Large,  
           MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES,  
           PAGE_READWRITE | PAGE_WRITECOMBINE );  
  if ( !Base )  
    { printf ( "1st VirtualAlloc fails\n" );   return 5; }  
  Pin  = (int*) Base;  
  *Pin = 22197;  
  MemoryBarrier ();  
  
  Page = VirtualAlloc (  
           Base,  
           Large,  
           MEM_COMMIT,  
           PAGE_READWRITE );  
  if ( Pin != (int*) Page   ||   *Pin != 22197 )  
    { printf ( "Page=%p   Err=%d\n", Page, GetLastError () );   return 6; }  
  
  VirtualFree ( Base, 0, MEM_RELEASE );  
  printf ( "normal end\n" );  
  return 0;  
  }  

The output is:
Page=0000000000000000 Err=487

The second test is similar, but it uses a 4Kb page instead. It runs normally:

#include "windows.h"  
#include "stdio.h"  
  
int main ()  
  {  
  LPVOID Base, Page;  
  SIZE_T Normal = 0x1000;  
  int    *Pin;  
  
  Base = VirtualAlloc ( 0, Normal,  
                        MEM_RESERVE | MEM_COMMIT,  
                        PAGE_READWRITE | PAGE_WRITECOMBINE );  
  if ( !Base )   return 1;  
  Pin  = (int*) Base;  
  *Pin = 22197;  
  MemoryBarrier ();  
  
  Page = VirtualAlloc ( Base, Normal,  
                        MEM_COMMIT,  
                        PAGE_READWRITE );  
  if ( Pin != (int*) Page   ||   *Pin != 22197 )   return 2;  
  
  VirtualFree ( Base, 0, MEM_RELEASE );  
  printf ( "normal end\n" );  
  return 0;  
  }  

The output is:
normal end

MemoryBarrier acts as a fencing operation, insuring that *Pin after the 2nd VirtualAlloc is not read before the store into memory is completed.

According to the MSDN doc, "VirtualAlloc can commit a page that is already committed. This means you can commit a range of pages, regardless of whether they have already been committed, and the function will not fail." That seems to apply to large pages.

My processor is an Intel Broadwell Core i7 5600U.
I use Visual Studio 2019 version 14.29.30133 and Windows Kits version 10.0.22000.0, under Windows10 version 21H2.

Is this problem a permanent restriction in the case of large pages? Or is it related
to the Intel chip capabilities, when changing the properties of a page? Or something else? Thanks by advance!

Windows 10
Windows 10
A Microsoft operating system that runs on personal computers and tablets.
10,708 questions
Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,430 questions
C++
C++
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
3,546 questions
{count} votes

2 answers

Sort by: Most helpful
  1. Xiaopo Yang - MSFT 11,501 Reputation points Microsoft Vendor
    2022-11-28T07:06:01.557+00:00

    The behavior, which the document doesn't mention, could be Large-Page Limitation and by design.

    0 comments No comments

  2. PIERRE REISS LABORDE 101 Reputation points
    2022-12-04T16:02:46.147+00:00

    That's clear. I accept the answer. Thank you.

    0 comments No comments