c++winapimemory-managementvirtualallocvirtualquery

Can't VirtualAlloc on free region returned by VirtualQuery


I am trying to allocate a certain amount of memory within some memory range in a DLL that gets loaded within a Windows Application.

The way I am doing it, is using VirtualQuery() to search for a region of memory that is marked as free and is within the boundaries where I require to do the allocation. What I am seeing is that even though the region is marked as MEM_FREE VirtualAlloc() fails sometimes to allocate the memory.

The code is very close to the following:

LPVOID address = NULL, mem = NULL;

for (address = LOWER_RANGE; address < UPPER_RANGE;) {
    MEMORY_BASIC_INFORMATION mbi = {0};

    if (VirtualQuery(address, &mbi, sizeof(mbi))) {
        if (mbi.State == MEM_FREE && mbi.RegionSize >= ALLOC_SIZE) {
            mem = VirtualAlloc(address, ALLOC_SIZE, 
                MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READ);
            if (mem) {
                break;
            }
        }
    }

    address = mbi.BaseAddress + mbi.RegionSize;
}

When VirtualAlloc() fails, GetLastError() returns ERROR_INVALID_ADDRESS (487).

The way I have worked around it is, if it is big enough, scan through mbi.RegionSize using page size steps to find an address that will allow me to allocate the memory that I need.

Why is it that according to VirtualQuery the entire region should be free and I should be able to allocate inside any address I want, but usually when the first VirtualAlloc fails I have to loop for several steps until it is finally successful.


Solution

  • I found a solution that worked for me. I my previous example I was trying to allocate and reserve at the same time; and the addresses I was using were not aligned with the allocation granularity. So I had to round up to the nearest multiple of allocation granularity that fits inside the region.

    Something like this worked (note, I have not tested this code).

    PVOID address = NULL, mem = NULL;
    
    for (address = LOWER_RANGE; address < UPPER_RANGE;) {
        MEMORY_BASIC_INFORMATION mbi = {0};
        if (VirtualQuery(address, &mbi, sizeof(mbi))) {
            PVOID reserveAddr = mbi.BaseAddress + (mbi.BaseAddress % alloc_gran);
            PVOID end_addr = mbi.BaseAddress + mbi.RegionSize;
    
            if (mbi.State == MEM_FREE && 
                    mbi.RegionSize >= ALLOC_SIZE &&
                    reserveAddr + ALLOC_SIZE <= end_addr) {
                mem = VirtualAlloc(reserveAddr, ALLOC_SIZE, 
                    MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READ);
                if (mem) {
                    break;
                }
            }
        }
    
        address = mbi.BaseAddress + mbi.RegionSize;
    }