Стек разбивает не пожары. Почему?

0

Я пытаюсь сделать glibc обнаружение разбития стека, и я использую следующий код:

 #include <stdio.h>
 #include <string.h>

 static const int n = 5;

 int main(int argc, char *argv[])
 {
  if (argc != 2)
  {
     printf("usage: %s string\n", argv[0]);
     return -1;
  }
  printf("%s, len = %d\n", argv[1], strlen(argv[1]));
  unsigned char a[n][n];
  unsigned char * b = a[n - 1];
  memcpy(b, argv[1], (strlen(argv[1]) + 1) * sizeof(unsigned char));
  return 0;
 }

Если argv [1] длина больше 5, я ожидаю обнаружения ошибки разбиения стека, однако я этого не делаю, и valgrind не обнаруживает ошибок. Что я должен изменить, чтобы получить эту ошибку? (массив a должен быть двумерным)

Теги:
glibc
stack-smash

2 ответа

1
Лучший ответ

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

-fstack-протектор

Извлеките дополнительный код, чтобы проверить переполнение буфера, например атаки с разбивкой пакетов. Это делается путем добавления защитной переменной к функциям с уязвимыми объектами. Сюда входят функции, которые вызывают alloca, и функции с буферами размером более 8 байтов. Охранники инициализируются, когда функция вводится, а затем проверяется, когда функция выходит. Если проверка безопасности завершается с ошибкой, выводится сообщение об ошибке, и программа завершается

Таким образом, мы должны ожидать, что функция с локальными буферами размером менее 8 байт будет незащищенной. Например, это:

int unprotected() {
    char a[5];
    strcpy(a, "this is much too long");
    return a[0];
}

скомпилированный с gcc -fstack-protector -Wstack-protector, дает предупреждение, подобное

предупреждение: защита стека не защищает функцию: все локальные массивы имеют длину менее 8 байтов [-Wstack-protector]

Итак, вы можете подумать, что ваш char[5][5] будет защищен, так как он больше 8 байтов. Однако, когда я компилирую это для ассемблера, я не получаю предупреждения или защиты стека (вы можете найти ассемблера для поиска в этой статье доктора Доббса). Кажется, что gcc рассматривает его как 5 буферов по 5 байт вместо одного буфера в 25 байт.

Вы можете убедить gcc включить защиту стека, указав один буфер более 8 байтов:

void protected(char *arg) {
    union {
        char dummy[5 * 5];
        char a[5][5];
    } u;
    memcpy(u.a[4], arg, (strlen(arg) + 1));
}

или просто используя -fstack-protector-all.

  • 0
    Одиночный буфер размером более 8 байт не получил эту ошибку, похоже, он по-прежнему не защищен, но -fstack-protector-all помогло. Благодарю.
  • 1
    И никогда не называйте функции словом «защищены» =)
Показать ещё 1 комментарий
3

По умолчанию GCC только добавляет код для обнаружения разбития стека, когда вы делаете что-то особенно опасное, как alloca (или gets, как вы упоминаете в комментарии), или объявляете большой автоматический массив.

Если вы хотите включить защиту для всех функций, используйте -fstack-protector-all. Вы также можете запросить предупреждение о незащищенных функциях с помощью -Wstack-protector.

  • 0
    Майк, есть ли точное значение для "большого" числа, которое ты упомянул?
  • 0
    @Anton: согласно документации , 8 байт. Но это не похоже на то, что вы видите, поэтому документация может не полностью соответствовать действительности.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню