2010-07-15 3 views
4

Как создать единичный тестовый пример в c для переполнения буфера, который не вызывает ошибку памяти, такую ​​как segfault?Создание тестового примера для ошибки переполнения буфера (C/C++)

I.e. Учитывая простой переполнение буфера, например

int function() { 
    int exampleArray[10]; 
    exampleArray[10] = 5; 
    return 0; 
} 

Как создать единичный тест для этого кода? Очевидно, есть ошибка, мы пишем прошлый конец массива. Однако вы можете запустить такую ​​функцию без каких-либо доказательств ошибки.

Примечание: Мне нужно иметь возможность создавать тестовые примеры, когда указатель на массив поставляется во время выполнения пользователем, а также в упрощенном случае.

На управляемом языке, таком как Java, код генерирует исключение (ArrayIndexOutOfBoundsException), которое может быть обнаружено. Поэтому создание тестового примера является простым (блок try-catch для исключения).

Как можно было бы создать такой тест в c? Может ли какая-либо из модулей тестирования модулей для C справиться с такой ситуацией?

Справочная информация: Я пытаюсь создать автоматическое создание тестового примера. Я знаю, где ошибки, и хотел бы иметь возможность создать единичный тест для сбоев в этих ошибках.

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

ответ

4

Один из способов проверки заключается в том, чтобы выделить дополнительные байты до и после буфера, если вы используете кучу. Но будет сложно отслеживать каждую переменную. После завершения функции вы можете проверить, были ли данные в этих буферах изменены. Вы должны создать отдельную библиотеку, чтобы удерживать эти значения для вас.

В качестве альтернативы можно проверить это link. Надеемся, что это даст вам больше информации о тестировании переполнения буфера.

EDIT: Некоторые больше информации:

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

    void foo()
    {

    char buffer [5]; 
    
    
    strcpy(buffer, "StackOverflow"); 
    
    // Clearly overflow. Has to be found out in Code reviews or static analysis 
    
    
    } 
    
+0

Это решение будет работать хорошо для всех связанных кучи переполнение буфера. Мне не нужно беспокоиться о том, чтобы отслеживать каждую переменную, поскольку я уже знаю, какие переменные задействованы в переполнении буфера (уже определенные посредством статического анализа кода). Есть ли способ обработать простейший случай, когда переполненный массив находится в стеке? Благодарим за помощь :) – tPP

+0

@tpp -Это приносит нам два вопроса. 1) Просмотрен ли код? Потому что, если вы не передаете какую-либо переменную функции, и она терпит неудачу, вам нужно нажать эту точку путем повторного тестирования. 2) Знаете ли вы, какую переменную вы хотите проверить для переполнения? –

+0

1) Нет кода не рассматривается. 2) Я знаю переменную, которую я хочу проверить для переполнения. И я знаю, что переменная действительно переполнена. Я просто не знаю, как создать тестовый пример, если переменная находится в стеке. Благодарю. – tPP

1

Вы можете только попытаться обнаружить доступ к индексам прямо под нолем и справа над последним элементом - что-то вроде выделения двух дополнительных массивов и заполнения их значениями маркера, а затем проверьте значения.

Вы не можете обнаружить все переполнения буфера в C легко. Ваш код может получить доступ к элементу далеко за пределами границ буфера и, например, к поврежденной куче молча. Или в многопоточной программе один поток может случайно перезаписать стопку другого потока. Доступ к памяти неограничен в C - если адрес отображается в адресное пространство и записывается, ваша программа может там писать, вы не можете легко обнаружить это.

2

Вы не указали платформу, но библиотека GNU C имеет переменную, называемую __malloc_hook, которая является указателем на форму static void *my_malloc_hook(size_t, const void *).

Пример: (взято из here)


static void * 
my_malloc_hook (size_t size, const void *caller) 
{ 
     void *result; 
     /* Restore all old hooks */ 
     __malloc_hook = old_malloc_hook; 
     __free_hook = old_free_hook; 
     /* Call recursively */ 
     result = malloc (size); 
     /* Save underlying hooks */ 
     old_malloc_hook = __malloc_hook; 
     old_free_hook = __free_hook; 
     /* printf might call malloc, so protect it too. */ 
     printf ("malloc (%u) returns %p\n", (unsigned int) size, result); 
     /* Restore our own hooks */ 
     __malloc_hook = my_malloc_hook; 
     __free_hook = my_free_hook; 
     return result; 
} 

С помощью этого можно выделить немного больше памяти, чем это необходимо (что вам нужно следить за какими-то образом) и заполнить это пространство с некоторым предопределенным маркером значение, возвращая указатель каким-то образом в пространство. Тогда, конечно, вам придется вставлять проверки границ в соответствующие места в вашем коде, что может и не быть тем, что вы хотите. Надеюсь, что это дает некоторые идеи, по крайней мере.

0

Если вы работаете на Intel, вы можете запустить код внутри Valgrind. Он имеет экспериментальный буферный переполненный детектор, поэтому YMMV. Я не могу придумать простой способ обнаружения переполнения буфера внутри самой C, не добавляя большого количества трещин, чтобы вставлять и обнаруживать защитные переменные.

2

как уже указано ловли каждые переполнение буфера - это сложно. В моем предыдущем проекте у компилятора не было таких параметров, как те, на которые указывает manneorama. К счастью, вся база кода никогда не называлась malloc напрямую, правила кода (строго соблюдаемые) каждый вызов malloc вызывали функцию, которая по умолчанию используется для вызова malloc (давайте назовите его mymalloc).

Так что мы сделали, чтобы создать дополнительный размер буфера, скажем, 4 байта дополнительно (2 до и 2 после запрошенной памяти. Заполните их байтами, которые вы ожидаете, что код не будет писать (и это решение не является звук)

в верхнем заголовочном файле определить макрос:

#define mymalloc(X) testmalloc(X,__FILE__,__LINE__) 

и определим функцию testmalloc следующим образом:

void * testmalloc(size_t size, char *filename, int linenum) 
{ 
    void *buff = malloc(size + 4) 
    char *bytebuff = (char *) buff 
    //bookkeeping, keep record of buff 
    bytebuff[0] = 0xBA; 
    bytebuff[1] = 0xBA; 

    bytebuff[size+2] = 0xBA; 
    bytebuff[size+3] = 0xBA; 

    return bytebuff[2]; 
} 

(этот код из моя память, у меня нет фактического кода со мной, поэтому у меня могут быть небольшие ошибки)

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

Это не гарантирует все перетоки поймают как:

  • программа может переполнить с точно такой же байт (0xBA здесь)
  • программа может держать сторожевых байты нетронутыми, но переливается за пределы диапазона

Это была простая задача для нас, поскольку очень дисциплинированная база кода сделала этот код несколько минут усилий, и мы поймали некоторые интересные ошибки.

Также этот механизм ограничен кучками выделенных массивов.

0

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

Например, если у вас есть

void writeSomeString(char * buf, size_t len); 

вы можете проверить его уважает размер буфера, как это:

char testBuf[100]; 
memset(&testBuf, 42, sizeof(testBuf)); 
writeSomeString(&testBuf, sizeof(testBuf)-1); 
assert(testBuf[sizeof(testBuf)-1] == 42); 
1

Код, который вы представили не блок-проверяемым. Вам нужно контролировать, что вам нужно проверить: параметры и возвращаемые значения.

Если вы действительно хотите, чтобы проиллюстрировать C, что другие языки делать, представьте себе функции, которые делают что-то вроде:

int setArrayInt(int *array, unsigned int arraySize, unsigned int index, int value) 
{ 
    if (index >= arraySize) 
    { 
    return -1; /* error */ 
    } 

    array[index] = value; 
    return 0; /* success */ 
} 

Вы бы тогда быть в состоянии модульного тестирования этой функции (cxxtest синтаксис):

/* Function returning failure if accessing out of range index */ 
TS_ASSERT_EQUALS(-1, setArrrayInt(exampleArray, sizeof(exampleArray), 10, 123)); 

/* Function returning success if we stay inside its range */ 
TS_ASSERT_EQUALS(-1, setArrrayInt(exampleArray, sizeof(exampleArray), 9, 123)); 
/* Our array really gets modified if our call is successful */ 
TS_ASSERT_EQUALS(123, exampleArray[9]); 

Наконец, вы должны использовать эту функцию для управления массивами, потому что вы подтвердили, что она работает правильно.

Я не подразумеваю, что вы должны создавать такие функции манипуляции с массивами в ваших программах на C. Чтобы уловить ошибки, которые вы приводили в примере, вам определенно нужны инструменты статического анализа или инструментария кода.

+0

Это очень хороший ответ ИМО, и он должен быть принятым ответом. –

0

В вашем примере, тестирование локальных переменных assigments может быть сделано только со статическими инструментами анализа, как Klocwork (http://www.klocwork.com) или, возможно, Lint (http://en.wikipedia.org/wiki/Lint_(software)) или аналогичный.

Но, если для простоты [1] инкапсулировать массив в типе (typedef int MY_ARRAY[10]) вы могли бы сделать что-то вдоль линий:

static union 
{ 
    MY_ARRAY fixed_array; 
    unsigned int dummy_array_overlay[sizeof(MY_ARRAY)+1]; 
} array_layout; 

#define myarray    array_layout.fixed_array 
#define dummy_array_overlay array_layout.dummy_array_overlay 

memset(&dummy_array_overlay, 0xFF, sizeof(dummy_array_overlay)); 

//Do your thing 

ASSERT_EQUAL((unsigned int)0xFF, dummy_array_overlay[sizeof(MY_ARRAY)]); 

Обратите внимание, как объединение помогает компоновки массива и нижележащих 0xFF тестовые значения с одинаковым смещением.

[1] Это обычно считается плохой идеей инкапсуляции массива, как это - использовать на структуру вложенности массива, а