Создание тестового сценария для ошибки переполнения буфера (C/c ++)

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

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

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

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

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

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

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

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

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

6
задан tPP 15 July 2010 в 06:06
поделиться

7 ответов

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

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

РЕДАКТИРОВАТЬ: Дополнительная информация:

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

    void foo ()
    {

     char buffer [5];
    
    
    strcpy (буфер, «StackOverflow»);
    
    // Явно переполнение. Необходимо выяснить в обзорах кода или статическом анализе.
    
    
    }
    
4
ответ дан 9 December 2019 в 20:39
поделиться

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

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

1
ответ дан 9 December 2019 в 20:39
поделиться

, как уже указывалось, сложно поймать каждое переполнение буфера . В моем предыдущем проекте у компилятора не было опций, подобных тем, на которые указывает 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)
  • программа может сохранить защитные байты нетронутыми, но переполнение за пределами диапазона

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

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

4
ответ дан 9 December 2019 в 20:39
поделиться

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

Пример: (взято из здесь )


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;
}

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

2
ответ дан 9 December 2019 в 20:39
поделиться

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

0
ответ дан 9 December 2019 в 20:39
поделиться

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

Если вы действительно хотите проиллюстрировать на 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]);

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

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

1
ответ дан 9 December 2019 в 20:39
поделиться

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

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

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);
0
ответ дан 9 December 2019 в 20:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: