Search code examples
c++windowswinapimemory-managementvirtual-memory

How to tell if a virtual memory page has been locked?


Say, if at some point a range of the virtual memory in my process was locked as such:

//Memory was reserved & committed as such
void* pMem = ::VirtualAlloc(NULL, 4096, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

//...

//And then
::VirtualLock(pMem, 4096);

So having an arbitrary address of a page in a virtual memory in my process, can I tell if it was locked?


Solution

  • by using win32 api this is impossible. but if use ZwQueryVirtualMemory with undocumented MEMORY_INFORMATION_CLASS values - this is possible. for data structures definition - see ntmmapi.h

    we need use MemoryWorkingSetExInformation with MEMORY_WORKING_SET_EX_BLOCK and use ULONG_PTR Locked : 1; member

    demo test:

    NTSTATUS IsPageLocked(PVOID BaseAddress, BOOLEAN& Locked)
    {
        MEMORY_WORKING_SET_EX_INFORMATION mwsei = { BaseAddress };
    
        NTSTATUS status = ZwQueryVirtualMemory(NtCurrentProcess(), 0, MemoryWorkingSetExInformation, &mwsei, sizeof(mwsei), 0);
    
        if (0 <= status)
        {
            if (mwsei.VirtualAttributes.Valid)
            {
                Locked = mwsei.VirtualAttributes.Locked;
            }
            else
            {
                status = STATUS_INVALID_ADDRESS;
            }
        }
    
        return status;
    }
    
    NTSTATUS IsPageLockedEx(PVOID BaseAddress, BOOLEAN& Locked)
    {
        NTSTATUS status = IsPageLocked(BaseAddress, Locked);
        if (0 > status)
        {
            DbgPrint("IsPageLocked - error %x\n", status);
        }
        else
        {
            DbgPrint("IsPageLocked = %x\n", Locked);
        }
        return status;
    }
    
    void CheckVA()
    {
        if (PVOID pv = VirtualAlloc(NULL, PAGE_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE))
        {
            BOOLEAN Locked;
            NTSTATUS status = IsPageLockedEx(pv, Locked);
    
            if (status == STATUS_INVALID_ADDRESS)
            {
                // at this point  physical memory for pv not yet committed. reference memory for commit physical page
                if (*(PBYTE)pv) __nop();
    
                status = IsPageLockedEx(pv, Locked);
            }
    
            if (VirtualLock(pv, PAGE_SIZE))
            {
                IsPageLockedEx(pv, Locked);
    
                if (VirtualUnlock(pv, PAGE_SIZE))
                {
                    IsPageLockedEx(pv, Locked);
                }
                else
                {
                    __debugbreak();
                }
            }
    
            VirtualFree(pv, 0, MEM_RELEASE);
        }
    }
    

    and DbgPrint output

    IsPageLocked - error c0000141
    IsPageLocked = 0
    IsPageLocked = 1
    IsPageLocked = 0