windowsmemory-managementoperating-systemvirtual-memorypage-fault

Triggering page fault exception on Windows


I'm trying to do some tests on page fault exception on Windows. The requirement is to put some data into the page boundary so that reading the data will trigger a page fault exception.

Concretely, the test consists of 7 bytes (for example) which are logically consecutive and allocated. I need the first 5 bytes to be located on a physically allocated page, but the next 2 bytes are located on a page which is not yet physically allocated.

So that reading this 7 bytes will trigger a page fault exception, while reading only 4 bytes will not.

I intially think that I should allocate two pages, write 7 byte data over the boundary, and page out the second page.

Can I do this using some user-mode Windows API?


Solution

  • Yes, this is possible in user-land. You'd usually reserve all the pages at once with VirtualAlloc and then commit part of it. You can change the protection back and forth with VirtualProtect after committing.

    #include <cstdint>
    #include <iostream>
    #include <Windows.h>
    
    int main()
    {
        //Retrieve currently configured page-size
        SYSTEM_INFO info;
        GetSystemInfo(&info);
        DWORD pageSize = info.dwPageSize;
        //Reserve 2 pages
        void *mem = VirtualAlloc(NULL, pageSize*2, MEM_RESERVE, PAGE_NOACCESS);
        //Commit first page with read/write premissions
        VirtualAlloc(mem, pageSize, MEM_COMMIT, PAGE_READWRITE);
        //get pointer with 5 bytes in the first page
        uint8_t* ptrAcross = (uint8_t*)mem + pageSize - 5;
        //Fill first 5 bytes
        FillMemory(ptrAcross, 5, 0x55);
        try
        {
            //Try to fill 6th byte
            FillMemory(ptrAcross+5, 1, 0x55);
        }
        catch(...) // only catches the access violation when compiled with /EHa
        {
            std::cout << "Access violation" << std::endl;
        }
        std::cout << "Program finished" <<std::endl;
        VirtualFree(mem, 0, MEM_RELEASE);
    
        return 0;
    }