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.
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;
}