глобальное переполнение буфера, среда Windows

Несколько дней назад я задал вопрос об обнаружении переполнения буфера (глобальное переполнение буфера sprintf - как это обнаружить, Windows) и проблема может быть решена только cppcheck со стандартной функцией (не защищенная версия _s).

Я пошел глубже и изменил код с

#include <stdio.h>
char buffer[2];
void main()
{
  sprintf(buffer,"12345");
}

to

#include <stdio.h>
void f( char *b )
{
   sprintf(b,"12345");
}

char buffer[2];
void main()
{
   f( buffer );
}

Visual Studio 2012 / RTC может обрабатывать переполнение выделенного буфера стека - во время выполнения, но глобальные данные остаются незамеченными.

Я полагаю, что невозможно провести глубокий анализ с помощью cppcheck, и эта проблема не обнаруживается cppcheck-1.64. Кроме того, я попытался использовать clang с AddressSanitizer (Windows) также без хороших результатов.

Можно ли предотвратить такие проблемы под Windows (желательно бесплатный инструмент), если нет, может быть, какой-нибудь Linux-инструмент может помочь?


person bataliero1234    schedule 31.03.2014    source источник
comment
В целом, мне проще не выходить за пределы буферов.   -  person Martin James    schedule 31.03.2014
comment
Обычно разработчики специально не делают ошибок :)   -  person bataliero1234    schedule 31.03.2014


Ответы (3)


Я знаю, что ответ немного запоздал, но он все еще может помочь в подобных случаях.
Cppcheck постоянно улучшается и теперь может обнаруживать эту проблему.
Последняя версия Cppcheck (версия 1.86 на данный момент) выводит это сообщение об ошибке при анализе второго примера кода:

$ ./cppcheck global_buffer_overflow.c
Checking global_buffer_overflow.c ...
[global_buffer_overflow.c:10] -> [global_buffer_overflow.c:4]: (error) Buffer is accessed out of bounds: buffer

Я не уверен, что это уже работало в версии 1.85, но определенно не работает с 1.84 или более ранними версиями. Я думаю, если вы еще лучше скроете глобальный буфер, Cppcheck в конечном итоге больше не сможет обнаружить проблему. Анализ потока ценностей довольно сложен и требует определенных ресурсов (памяти, времени, ЦП).

person versat    schedule 21.12.2018

Вместо использования sprintf вам следует подумать об использовании snprintf. Его прототип (содержится в заголовке stdio.h) -

int snprintf(char *str, size_t size, const char *format, ...);

Функция записывает не более size байтов, включая завершающий нулевой байт, в буфер, на который указывает str. Следовательно, вы должны изменить подпись функции f, чтобы она также принимала длину буфера. Также обратите внимание, что подпись main должна быть одной из следующих:

int main(void);
int main(int argc, char *argv[]);

Предлагаю следующие изменения -

#include <stdio.h>

void f(char *b, size_t len) {
   sprintf(b, len, "12345");
}

char buffer[2];
int main(void) {
   f(buffer, sizeof buffer);
   return 0;
}
person ajay    schedule 31.03.2014

Это некоторые из причин, по которым нам следует избегать использования глобальной переменной в нашей программе. Я не думаю, что существуют такие инструменты, которые сообщали бы о повреждении глобальной переменной (сегмента данных). Существует хороший набор инструментов для обнаружения повреждения / переполнения памяти стека и сегмента кучи.

Поэтому, чтобы избежать такого сценария, мы должны минимизировать использование глобальной переменной в нашей программе. Если это невозможно, мы должны попытаться использовать подход защитного программирования самостоятельно при их использовании. Чтобы проиллюстрировать это, ваша программа может быть переписана следующим образом, чтобы избежать сценария глобального повреждения памяти в вашей программе.

#include <cstdio>

char buffer[2];


void f( char *b, size_t sz)
{
 // Now we have protected our global variable from overrun by
 // using the size information passed by caller.So even though
 // client "12345" has been passed, it would just copy 12.
 strncpy(b,"12345",sz);
}


int main() {
  size_t tmp = sizeof(buffer)/sizeof(buffer[0]);
  f( buffer, tmp);
  return 0;
}
person Mantosh Kumar    schedule 31.03.2014
comment
Итак, нет инструментов, которые, например, помещают канарейки / волшебное слово между переменными в сегменте данных (например, метод защиты от переполнения стека)? Я думаю, что AddressSanitizer сделает такое, но вчера я протестировал его и не смог обнаружить. code.google.com/p/address-sanitizer/wiki/ - person bataliero1234; 31.03.2014
comment
@ bataliero1234: Могут быть инструменты, и даже AddressSanitizer может работать. При работе с глобальным сегментом (данных) могут быть некоторые технические ограничения. Однако суть в том, что, если возможно, мы должны сделать нашу программу более надежной и безопасной (если возможно) и менее зависимой от инструмента. Это важный момент, на который следует обратить внимание, поскольку в реальном мире большую часть времени ваше программное обеспечение будет сталкиваться с некоторыми проблемами, которые вы не сможете воспроизвести в нашей собственной среде. И заказчик (обычно) не разрешает вам запускать какой-либо динамический инструмент на своих производственных машинах. Так что выбор за нами :) - person Mantosh Kumar; 31.03.2014