如何分配完整内存页

How to allocate full memory pages

本文关键字:内存 分配 何分配      更新时间:2023-10-16

在 C 或 C++ 中,在 Linux 上,我想在系统内存页面大小的整页中分配堆内存。

(目的是我想增加有害缓冲区溢出导致分段错误的可能性。

当我使用数组 new (pointer = new char[size]) 分配内存时C++其中大小是sysconf(_SC_PAGESIZE)的倍数,那么分配的内存通常不会是sysconf(_SC_PAGESIZE)的倍数(虚拟)地址,表明我有一个更大块的子集,这一事实证实了写入指针[size] 和一点点超出(强制缓冲区溢出)通常不会导致分段错误。

我的问题是,我能否以某种方式影响内存分配以给我完整的内存页。

我感兴趣的处理器架构是x86_64又名amd64。操作系统要么是最新的Ubuntu,要么是稳定的CentOS Linux(7.3),后者带有内核3.10和gcc-4.8。

我不在乎解决方案是 C 还是C++,因此我要求在这个问题中保留 C 标签。

1)只是从pointer = new char[size]切换到pointer = aligned_alloc(sysconf(_SC_PAGESIZE), size)会导致正确的页面对齐,并且(到目前为止,使用小型测试程序)在超出分配范围时一致地生成分段错误。正如@JohnBollinger在对该问题的第一条评论中指出的那样,仅靠分配方法并不能保证分割错误的产生。这可以通过 2) 修复:

2) 函数 mprotect 的 Linux 手册页包含限制对内存页的访问的完整示例。该示例还为 SIGSEGV 提供了一个信号处理程序,我对它不感兴趣,默认操作(中止)对我来说已经足够了。下面是手册页中的示例部分。请注意,将 mprotect 应用于与 mmap 无关的内存区域是 POSIX 未涵盖的特定于 Linux 的扩展。

下面的程序分配四页内存,使第三页 这些页面是只读的,然后执行一个遍历 向上通过分配的区域修改字节。

运行程序时我们可能会看到的一个例子是 以后:

$ ./a.out
Start of region:        0x804c000
Got SIGSEGV at address: 0x804e000

程序源

#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/mman.h>
#define handle_error(msg) 
do { perror(msg); exit(EXIT_FAILURE); } while (0)
static char *buffer;
static void
handler(int sig, siginfo_t *si, void *unused)
{
printf("Got SIGSEGV at address: 0x%lxn",
(long) si->si_addr);
exit(EXIT_FAILURE);
}
int
main(int argc, char *argv[])
{
char *p;
int pagesize;
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = handler;
if (sigaction(SIGSEGV, &sa, NULL) == -1)
handle_error("sigaction");
pagesize = sysconf(_SC_PAGE_SIZE);
if (pagesize == -1)
handle_error("sysconf");
/* Allocate a buffer aligned on a page boundary;
initial protection is PROT_READ | PROT_WRITE */
buffer = memalign(pagesize, 4 * pagesize);
if (buffer == NULL)
handle_error("memalign");
printf("Start of region:        0x%lxn", (long) buffer);
if (mprotect(buffer + pagesize * 2, pagesize,
PROT_READ) == -1)
handle_error("mprotect");
for (p = buffer ; ; )
*(p++) = 'a';
printf("Loop completedn");     /* Should never happen */
exit(EXIT_SUCCESS);
}

前述引文的出处:

此页面是 Linux 手册页项目 4.04 版的一部分。
项目描述、有关报告 bug 的信息,以及 此页面的最新版本可以在 http://www.kernel.org/doc/man-pages/找到。