如何保护mmap()分配的内存空间不被“' new”分配。电话?

时间:2015-03-11 10:40:57

标签: c++ memory-management

我正在使用

在特定地址分配空间

mmap(...,PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,...)

但是,如果我调用类似new char[large_number]的内容,那么创建的数组与先前分配的内存段重叠。所以我的问题是我该怎么做才能保护我之前试图分配的内存段。

编辑: 对于以下代码,

char* base_address = (char*)malloc(sizeof(long));
int page_size = getpagesize();
size_t length = page_size;
char* selected_address = (char*)base_address + (page_size - ((long)base_address % page_size));

char* allocated_address = (char*) mmap(selected_address, page_size * 4, PROT_WRITE | PROT_READ
    ,  MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED, -1, 0);

int* a = new int[page_size / 4];
// mprotect(allocated_address, page_size * 4, PROT_READ | PROT_WRITE);
int* b = new (allocated_address) int[page_size / 2];
int* c = new (allocated_address + page_size / 2 ) int[page_size/2];

int* d = (int*)malloc(page_size);

cout << "base_address:" << (int*)base_address << " selected_address:" << (int*)selected_address << " allocated_address:" << (int*)allocated_address << endl;
cout << "a=" << a << " end=" << &a[page_size/4 - 1] << endl;
cout << "b=" << b << " end=" << &b[page_size/2 - 1] << endl;
cout << "c=" << c << " end=" << &c[page_size/2 - 1] << endl;
cout << "d=" << d << " end=" << &d[page_size - 1] << endl;

我正在

base_address:0x603010 selected_address:0x604000 allocated_address:0x604000
a=0x603030 end=0x60402c
b=0x604000 end=0x605ffc
c=0x604800 end=0x6067fc
d=0x604040 end=0x60803c

不应该在mmap之外分配的a和d段分配空间吗?

4 个答案:

答案 0 :(得分:2)

  

mmap(...,PROT_WRITE | PROT_READ, MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED,...)

内存映射只能是MAP_SHAREDMAP_ANONYMOUS,但不能同时为两者。

  

但是,如果我调用类似new char[large_number]的内容,那么创建的数组与先前分配的内存段重叠。

这是不可能的。您忽略mmap调用的返回值,调用最有可能失败。

答案 1 :(得分:2)

从我系统上的mmap的手册页(OS X)

  

如果在flags中指定了MAP_FIXED,系统将尝试将映射放在指定的地址,可能会删除该位置已存在的映射。

您迫使mmap()映射到malloc()传递给您的地址上方的第一页。很可能new调用malloc()来获取内存块,而内存分配器可能认为它拥有您尝试映射到的页面。你没有告诉分配器它不能使用那个页面,所以它继续并且做到了。

请勿指定MAP_FIXED 。只需使用它指定的地址,而不指定MAP_FIXED

答案 2 :(得分:1)

您的实现平台可能会隐式增加堆,通过跟踪堆使用的最高页面地址,然后在需要时简单地增加它。第一次访问先前未使用的页面会产生页面错误,然后O / S会自动增加进程的虚拟内存地址空间,从故障地址开始。

在Linux上,请参阅sbrk(2)手册页以获取有关此过程的更完整描述。

所以这里发生的事情就是你明确要求mmap()编写一个地址,这个地址正好是C库接下来要使用的地址,用于分配的内存。并且C库很乐意继续增加数据段,侵入你的mmap-ed空间。

这是一个简单的案例“医生,当我这样挥动手臂时疼痛。”; “好吧,不要那样挥动你的手臂。”

Linux上的mmap()手册页提供了关于mmap()的第一个参数的注意事项:

  

如果addr为NULL,则内核选择要创建的地址   映射;这是创建新map-ping的最便携方法。

一个NULL地址导致内核将你的新内存段映射到一个远离其他任何地方的地址,并且远离进程的数据段,并且两者不会相互干扰。

如果您在虚拟地址空间中明确地mmap()某个特定地址,那么您有责任避免遇到麻烦,并确切知道您的流程中发生了什么,包括什么C库正在做。

我还应该注意,为了计算新的mmaped地址,你要计算一个超过当前分配数组末尾的指针地址。

严格地说,这是一种未定义的行为。在这一点上,所有的赌注都是关闭的,只要严格的C / C ++合规性也是如此。

答案 3 :(得分:1)

您的代码是undefined behavior。您mmap - newmalloc给出的地址MAP_FIXED。您不应该这样做(而new通常会失败,因为mallocchar* allocated_address = (char*) mmap(NULL, page_size * 4, PROT_WRITE | PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); if (allocated_address == MMAP_FAILED) { perror("mmap"); exit(EXIT_FAILURE); }; int* b = new ((void*)allocated_address) int[page_size / 2]; int* c = new ((void*)(allocated_address + (page_size / 2)*sizeof(int))) int[page_size/2]; 返回的地址很少页面对齐)。

我猜你正在使用Linux。所以代码:

int[page_size/2]

请注意,数组sizeof(int)*(page_size/2)的字节大小为sizeof(int)。在我的系统上(Linux / x86-64 / Debian / Sid)page_size为4,mmap为4096。

仔细阅读mmap(2)。由于MAP_FIXED的结果为cat /proc/1234/maps没有 /proc/self/maps,您应该几乎不会使用它,除非再次映射以前 mmaped地址段)从一次运行到下一次运行通常是不可重现的。

顺便说一下,当您的程序以pid 1234运行时,在一个单独的终端中运行{{1}}(从里面您的程序中,您甚至可以逐行读取并解析{{1} });它会显示进程1234的地址空间。阅读ASLRproc(5)