VirtualAlloc failing
Asked Answered
P

2

6

I am trying to use VirtualAlloc to reserve and commit a block of memory and then again to extend that block. Unfortunately, it is returning NULL with error ERROR_INVALID_ADDRESS despite VirtualQuery saying that the address range requested is free. Here's my code:

void* allocation = VirtualAlloc(NULL, 4096, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
void* desiredNextAllocation = (char*)allocation + 4096;
MEMORY_BASIC_INFORMATION info;
size_t memory_info = VirtualQuery(desiredNextAllocation, &info, sizeof(info));
void* extended = VirtualAlloc(desiredNextAllocation, 4096, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

The first allocation returns 0x00000000000d0000. The call to VirtualQuery results in the following data in 'info':

    BaseAddress 0x00000000000d1000  void *
    AllocationBase  0x0000000000000000  void *
    AllocationProtect   0x00000000  unsigned long
    RegionSize  0x00000000000ff000  unsigned __int64
    State   0x00010000  unsigned long
    Protect 0x00000001  unsigned long
    Type    0x00000000  unsigned long

I interpret that to mean that there are 0xff available pages beginning at 0xd1000 which are in the MEM_FREE state. So why does my attempt to commit the page at 0xd1000 fail?

I'm running Windows 7 and this is a 64 bit build.

I have read several StackOverflow posts about VirtualAlloc but they all seem to imply that this code should work as does my understanding of the documentation.

Persona answered 26/4, 2015 at 4:45 Comment(0)
C
1

From the documentation for VirtualAlloc:

If the memory is being reserved, the specified address is rounded down to the nearest multiple of the allocation granularity.

In this case, address 0xd1000 is rounded down to address 0xd0000, which is already reserved and hence invalid.

Capetian answered 26/4, 2015 at 12:16 Comment(0)
L
4

If you want to specify contiguous pages for allocations, you want to separate allocating address space from allocating memory to back it. Keeping that in mind, we could implement code something like this:

#include <windows.h>
#include <iostream>
#include <iomanip>

std::ostream &operator<<(std::ostream &os, MEMORY_BASIC_INFORMATION const &mi) {
    return os   << std::setw(20) << "Allocation Base: " << mi.AllocationBase << "\n"
                << std::setw(20) << "BaseAddress: " << mi.BaseAddress << "\n"
                << std::setw(20) << "Protection: " << mi.Protect << "\n"
                << std::setw(20) << "Region size: " << mi.RegionSize;
}

void show_page(void *page) {
    MEMORY_BASIC_INFORMATION info;

    VirtualQuery(page, &info, sizeof(info));
    std::cout << info << "\n\n";
}

static const int page_size = 4096;

void *alloc_page(char *address) {

    void *ret = VirtualAlloc(address, page_size, MEM_COMMIT, PAGE_READWRITE);
    show_page(ret);
    return ret;
}

int main() {
    static const int region_size = 65536;

    char * alloc = static_cast<char *>(VirtualAlloc(NULL, region_size, MEM_RESERVE, PAGE_READWRITE));

    for (int i = 0; i < 4; i++)
        alloc_page(alloc + page_size * i);
}

Example result:

Allocation Base: 00000000000C0000
    BaseAddress: 00000000000C0000
     Protection: 4
    Region size: 4096

Allocation Base: 00000000000C0000
    BaseAddress: 00000000000C1000
     Protection: 4
    Region size: 4096

Allocation Base: 00000000000C0000
    BaseAddress: 00000000000C2000
     Protection: 4
    Region size: 4096

Allocation Base: 00000000000C0000
    BaseAddress: 00000000000C3000
     Protection: 4
    Region size: 4096

As you can see, all the allocations now succeed. As an aside: when you're reserving address space, the smallest size you can allocate is 64K (as shown above). You should really get the page size and minimum region size by calling GetSystemInfo, and using the dwPageSize and dwAllocationGranularity in the SYSTEM_INFO structure it gives you.

Lelalelah answered 26/4, 2015 at 5:57 Comment(0)
C
1

From the documentation for VirtualAlloc:

If the memory is being reserved, the specified address is rounded down to the nearest multiple of the allocation granularity.

In this case, address 0xd1000 is rounded down to address 0xd0000, which is already reserved and hence invalid.

Capetian answered 26/4, 2015 at 12:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.