7.1. Код, содержащий ошибки

 1: / * broken.с* /

 2:

 3: #include <stdlib.h>

 4: #include <stdio.h>

 5: #include <string.h>

 6:

 7: char global[5];

 8:

 9: int broken(void){

10:  char *dyn;

11:  char local[5];

12:

13:  /* Для начала немного перезаписать буфер */

14:  dyn = malloc(5);

15:  strcpy(dyn, '12345');

16:  printf ('1: %s ', dyn);

17:  free(dyn);

18:

19:  /* Теперь перезаписать буфер изрядно */

20:  dyn = malloc(5);

21:  strcpy(dyn, '12345678');

22:  printf('2: %s ', dyn);

23:

24:  /* Пройти перед началом выделенного с помощью malloc локального буфера */

25:  * (dyn-1) ='';

26:  printf ('3: %s ', dyn);

27:  /* обратите внимание, что указатель не освобожден! */

28:

29:  /* Теперь двинуться после переменной local */

30:  strcpy(local, '12345');

31:  printf ('4: %s ', local);

32:  local[-1] = '';

33:  printf('5: %s ', local);

34:

35:  /* Наконец, атаковать пространство данных global */

36:  strcpy(global, '12345');

37:  printf ('6: %s ', global);

38:

39:  /* И записать поверх пространства перед буфером global */

40:  global[-1] = '';

41:  printf('7: %s ', global);

42:

43:  return 0;

44: }

45:

46: int main (void) {

47:  return broken();

48: }

В этой главе мы рассмотрим проблемы в показанном выше сегменте кода. Этот код разрушает три типа областей памяти: память, выделенную из динамического пула памяти (кучи) с помощью malloc(), локальные переменные размещенные в стеке программы и глобальные переменные, хранящиеся в отдельной области памяти, которая была статически распределена при запуске программы[9]. Для каждого класса памяти эта тестовая программа выполняет запись за пределами зарезервированной области памяти (по одному байту) и также сохраняет байт непосредственно перед зарезервированной областью. К тому же в коде имеется утечка памяти, что позволит продемонстрировать, как с помощью различных средств отследить эти утечки.

Несмотря на то что в представленном коде кроется много проблем, в действительности, он работает нормально. Не означает ли это, что проблемы подобного рода не важны? Ни в коем случае! Переполнение буфера часто приводит к неправильному поведению программы задолго до фактического его переполнения, а утечки памяти в программах, работающих длительное время, приводят к пустой растрате ресурсов компьютера. Более того, переполнение буфера является классическим источником уязвимостей безопасности, как описано в главе 22.

Ниже показан пример выполнения программы.

$ gcc -Wall -о broken broken.с

$ ./broken

1: 12345

2: 12345678

3: 12345678

4: 12345

5: 12345

6: 12345

7: 12345

7.2. Средства проверки памяти, входящие в состав glibc

Библиотека GNU С (glibc) предлагает три простых средства проверки памяти. Первые два — mcheck() и MALLOC_CHECK_ — вызывают проверку на непротиворечивость структуры данных кучи, а третье средство — mtrace() — выдает трассировку распределения и освобождения памяти для дальнейшей обработки.

7.2.1. Поиск повреждений кучи

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

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

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

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

Отметить Добавить цитату