如何分配完整的内存页

Lud*_*lze 4 c c++ linux memory-management x86-64

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

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

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

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

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

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

Lud*_*lze 5

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
Run Code Online (Sandbox Code Playgroud)

节目来源

   #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%lx\n",
               (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%lx\n", (long) buffer);

       if (mprotect(buffer + pagesize * 2, pagesize,
                   PROT_READ) == -1)
           handle_error("mprotect");

       for (p = buffer ; ; )
           *(p++) = 'a';

       printf("Loop completed\n");     /* Should never happen */
       exit(EXIT_SUCCESS);
   }
Run Code Online (Sandbox Code Playgroud)

前面引用的归属:

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