Есть ли утилита динамической проверки, которая может пометить переполнение статического буфера в структуре?
Существует ли утилита динамической проверки, которая может пометить следующую ошибку? Вальгринд не может. Можно ли очистить или застраховать ++? Это последняя версия Linux Ubuntu.
struct A {
char buff1[8];
int jj;
char buff2[8];
int ii;
char buff3[8];
} a;
main(int argc, char *args[])
{
// Set intermediate fields to known flag value
a.ii = a.jj = 0xdeadbeef;
// Write 8 char string into 8 byte buffer - null will overflow into neighboring int field. ERROR
sprintf(a.buff2, "ABCDEFGH");
}
4 ответа
Не в моих знаниях. Большинство (или, скорее: все?) Инструменты проверки памяти работают таким образом, что встраивают защищенные от чтения и записи страницы в качестве защитных зон между переменными и вокруг них, чтобы вызывать ловушки при доступе за пределы законно выделенных областей.
Без серьезного нарушения выравнивания и целостности конструкции это невозможно сделать в середине конструкции.
РЕДАКТИРОВАТЬ: Еще один момент: есть конструкции, где запись за границы элементов структуры совершенно законно и единственная разумная возможность достичь того, что вы хотите. Одним из примеров является копирование структур в кучу:
struct x orig, *copy;
orig.a = 100;
strcpy (orig.str, "Test");
copy = malloc (sizeof (struct x));
memcpy (copy, &orig, sizeof (struct x));
Это также записывает за пределы элементов структуры, но это единственный разумный (и совершенно законный) способ поместить структуру в кучу (кроме утомительного и медленного копирования для членов).
Другой пример будет
p = malloc (NUM_STRUCTS * sizeof (struct x));
memset (p, NUM_STRUCTS * sizeof (struct x), 0);
Это совершенно допустимая конструкция, которая позволяет очищать массив структур в куче - и она даже не записывает через внутренние границы структур, но также между структурами.
В некотором смысле даже calloc() будет писать за пределами структурных элементов....
И, как определенный ответ из (по общему признанию более старого) Руководства пользователя Purify, я случайно обнаружил в одном из своих ящиков стола:
Purify обнаруживает ошибки границ массивов в массивах в структурах C, только когда доступ выходит за пределы всей структуры
Это считается "нет" для меня.
Это должен быть динамический инструмент? Единственный известный мне инструмент, который обнаружит описанный выше сценарий, - это Coverity, статический инструмент из Synopsis. Для приведенного выше случая будет подготовлен отчет в форме:
Error: OVERRUN:
...
sprintf_overrun: "sprintf" will overrun its first argument "a.buff2" which can accommodate 8 bytes. The number of bytes written may be 9 bytes, including the terminating null.
Компиляция со всеми включенными предупреждениями - хорошее начало:
chqrlie@mac ~/dev/stackru > clang -O3 -std=c11 -Weverything -lm -o 35996676 35996676.c
35996676.c:9:1: warning: type specifier missing, defaults to 'int' [-Wimplicit-int]
main(int argc, char *args[])
^
35996676.c:12:19: warning: implicit conversion changes signedness: 'unsigned int' to 'int' [-Wsign-conversi
a.ii = a.jj = 0xdeadbeef;
~ ^~~~~~~~~~
35996676.c:15:5: warning: implicitly declaring library function 'sprintf' with type 'int (char *, const cha
sprintf(a.buff2, "ABCDEFGH");
^
35996676.c:15:5: note: include the header <stdio.h> or explicitly provide a declaration for 'sprintf'
35996676.c:9:10: warning: unused parameter 'argc' [-Wunused-parameter]
main(int argc, char *args[])
^
35996676.c:9:22: warning: unused parameter 'args' [-Wunused-parameter]
main(int argc, char *args[])
^
35996676.c:7:3: warning: no previous extern declaration for non-static variable 'a' [-Wmissing-variable-dec
} a;
^
6 warnings generated.
Исправление с помощью простых исправлений и добавление дополнительных очевидных ошибок:
#include <stdio.h>
#include <string.h>
static struct A {
char buff1[8];
unsigned int jj;
char buff2[8];
unsigned int ii;
char buff3[8];
} a;
int main(void) {
// Set intermediate fields to known flag value
a.ii = a.jj = 0xdeadbeef;
// Write 8 char string into 8 byte buffer - null will overflow into neighboring int field. ERROR
sprintf(a.buff1, "ABCDEFGH");
strcpy(a.buff2, "ABCDEFGH");
sprintf(a.buff3, "%s", "ABCDEFGH");
return 0;
}
Компилируется без предупреждений, несмотря на очевидное sprintf
а также strcpy
ошибки, которые должен был поймать упрощенный статический анализ.
gcc
с -Wall -W -Wextra
тоже не видит ничего плохого.
Рекомендуется использовать snprintf
вместо sprintf
, но это не помешало бы strcpy
проблема. strcpy
в общем случае небезопасно, но при наличии строкового литерала в качестве источника компилятор должен определенно жаловаться.
Вам следует попробовать Frama-c, мощную среду анализа исходного кода, доступную в открытом исходном коде по http://frama-c.com/
По моему опыту, Purify может делать то, что вы хотите. Это отрывок из старого руководства пользователя Purify, опубликованного в Интернете:
Как Purify проверяет статически выделенную память
В дополнение к обнаружению ошибок доступа в динамической памяти, Purify обнаруживает ссылки за пределами данных в глобальных и статических переменных, то есть данных, которые статически выделяются во время соединения, а не динамически во время выполнения.
Вот пример данных, которые обрабатываются функцией статической проверки:
int array[10]; main() { array[11] = 1; }
В этом примере Purify сообщает об ошибке ABW при назначении
array[11]
потому что это 4 байта за конец массива.