3

我正在为我的 UTF8 操作库编写一个单元测试,如果函数进入缓冲区溢出,我希望我的测试能够发生段错误。所以我想出了在内存中mmap两个相邻页面的想法,第一个是PROT_READ | PROT_WRITE,第二个是 PROT_NONE。这样,如果发生任何溢出,就可以保证段错误。这是一个例子:


void *addr1, *addr2; /* these are the pages; mmap call left out for simplicity */
char *p = (char *) (addr1 + getpagesize() - 8);

utf8_encode(aUtf8String, p, 8); // this shouldn't segfault

问题是,当我映射第二页时,我的程序出现了段错误。这是一个重现问题的示例程序(GNU/Linux):


#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>

void checkMap(void *p) 
{ 
    if(p == MAP_FAILED) {
        printf("error running mmap: %s\n", strerror(errno));
        exit(1);
    }   
}

int main(void)
{
    void *addr1, *addr2;
    size_t pagesize;

    pagesize = getpagesize();
    checkMap(addr1 = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
    checkMap(addr2 = mmap(addr1 + pagesize, pagesize, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0));  /* segfaults */
    munmap(addr1, pagesize);
    munmap(addr2, pagesize);

    return 0;
}

有趣的是,第一个 mmap() 之前的 printf() 语句会导致程序成功运行。有谁知道为什么 mmap 会出现段错误?如果使用 mmap() 无法实现我的目标,是否有人对如何测试我的代码是否存在缓冲区溢出有任何其他建议?

4

2 回答 2

3

您可以调用mprotect()来更改映射的内存上的保护标志mmap()。这可能比尝试mmap()使用不同保护的两个相邻页面更好的解决方案,因为这似乎是导致您的问题的原因。

(Linux 允许您mprotect()任何页面上调用,但 POSIX 只允许已分配的页面mmap()。)

这是Electric Fence用来捕获缓冲区溢出的技巧之一。

于 2009-02-27T18:31:53.197 回答
0

这与您的问题并不完全相关(除非您的最终目标确实是让您的测试正常工作),但是,恕我直言,依赖这种故障处理可能会产生误导(最明显的是,如果您针对多个平台,其中测试应该失败只是由于某些未指定的行为而没有触发段错误)。

也许另一种方法对您来说更有意义,例如,只需分配一个比必要更大的缓冲区,在其末尾放置一个标记,并检查它是否已被覆盖?

正如其他人所提到的,如果您愿意设置更复杂的测试环境,那么电栅栏、valgrind 或其他工具的分析可能会更精细。

于 2009-03-03T12:50:03.530 回答