2009-02-27 3 views
3

Я пишу блок-тест для моей библиотеки обработки UTF8, и я хочу, чтобы мой тест был segfault, если функция переходит в переполнение буфера. Поэтому я придумал идею объединить две страницы рядом друг с другом в памяти, первая с PROT_READ | PROT_WRITE, а второй с PROT_NONE. Таким образом, если произойдет переполнение, гарантируется segfault. Вот пример:mmaping две последовательные страницы


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 является segfaulting? Если моя цель недостижима с помощью mmap(), есть ли у кого-нибудь другие советы о том, как я могу проверить свой код на переполнение буфера?

+0

Ваш код отлично работает для меня. Единственное, что вы не включаете unistd.h для getpagesize(). Компилятор угадает тип. Это может привести к проблемам на 64-битных системах, я думаю, если угадать 32-битный int. –

+0

Я тоже тестировал его, и я получаю segfault, но не внутри main(). Скорее, он падает в коде glibc, на выходе ... Действительно странно, это одно. Я тестировал на очень простой 32-битной машине. – unwind

+0

Любопытно, в какой версии ядра/дистрибутива вы запускаете его? Я просто попробовал это на 64-битном (после #including unistd.h), и теперь он терпит неудачу в munmap. – 2009-02-27 16:54:58

ответ

3

Вы можете позвонить по номеру mprotect(), чтобы изменить флаги защиты в памяти, отображаемые по номеру mmap(). Это может быть лучшим решением, чем попытка сопоставления двух соседних страниц с различными защитами, поскольку это, по-видимому, вызывает проблемы.

(Linux позволяет вызывать mprotect() на любую страницу, но POSIX позволяет только те страницы, которые уже были выделены на mmap().)

Это одна из уловок Electric Fence использует, чтобы поймать переполнение буфера.

+0

Это замечательно! Большое спасибо! – 2009-02-27 19:12:01

+1

Обратите внимание, что он также будет действителен для 'mmap' на 2 страницы, а затем' MAP_FIXED' новую страницу 'PROT_NONE' поверх второй страницы. То, что недействительно, - это просто выбрать адрес рядом с существующей страницей и сопоставить его с 'MAP_FIXED'; у вас нет способа узнать, что он еще не занят другим сопоставлением (например, частью libc). –

0

Это не полностью связано с вашим вопросом (если только ваша конечная цель - заставить работать тест), но ИМХО, полагаясь на этот вид обработки ошибок, может вводить в заблуждение (особенно если вы нацеливаете на несколько платформ , где тест должен был сбой только в том, что seg-fault не был вызван из-за некоторого неуказанного поведения).

Возможно, для вас может возникнуть другой подход, например, просто выделить больший буфер, чем необходимо, поместить маркер в конец его и проверить, была ли он перезаписана?

Как уже упоминалось, если вы хотите создать более сложную среду тестирования, электрический забор, valgrind или другие инструменты, вероятно, более сложны в их анализе.

Смежные вопросы