Я пытаюсь сделать 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 должен быть двумерным)
Похоже, что логика в 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
.
По умолчанию GCC только добавляет код для обнаружения разбития стека, когда вы делаете что-то особенно опасное, как alloca
(или gets
, как вы упоминаете в комментарии), или объявляете большой автоматический массив.
Если вы хотите включить защиту для всех функций, используйте -fstack-protector-all
. Вы также можете запросить предупреждение о незащищенных функциях с помощью -Wstack-protector
.