19 смертных грехов, угрожающих безопасности программ. Майкл Ховард
Чтение книги онлайн.
Читать онлайн книгу 19 смертных грехов, угрожающих безопасности программ - Майкл Ховард страница 7
char buf[MAX_PATH];
sprintf(buf, "%s – %d\n", path, errno);
Если не считать нескольких граничных случаев, функцию sprintf почти невозможно использовать безопасно. Для Microsoft Windows было выпущено извещение о критической ошибке, связанной с применением sprintf для отладочного протоколирования. Подробности см. в бюллетене MS04–011 (точная ссылка приведена в разделе «Другие ресурсы»).
А вот еще пример:
char buf [ 32] ;
strncpy(buf, data, strlen(data));
Что неверно? В последнем аргументе передана длина входного буфера, а не размер целевого буфера!
Еще один способ столкнуться с проблемой – по ошибке считать байты вместо символов. Если вы работаете с кодировкой ASCII, то между ними нет разницы, но в кодировке Unicode один символ представляется двумя байтами. Вот пример:
_snwprintf(wbuf, sizeof(wbuf), «%s\n», input);
Следующее переполнение несколько интереснее:
bool CopyStructs(InputFile* pInFile, unsigned long count)
{
unsigned long i;
m_pStructs = new Structs[count];
for(i = 0; i < count; i++)
{
if(!ReadFromFile(pInFile, &(m_pStructs[i])))
break;
}
}
Как здесь может возникнуть ошибка? Оператор new[] в языке С++ делает примерно то же, что такой код:
ptr = malloc(sizeof(type) * count);
Если значение count может поступать от пользователя, то нетрудно задать его так, чтобы при умножении возникло переполнение. Тогда будет выделен буфер гораздо меньшего размера, чем необходимо, и противник сможет его переполнить. В компиляторе С++, который будет поставляться в составе Microsoft Visual Studio 2005, реализована внутренняя проверка для недопущения такого рода ошибок. Аналогичная проблема может возникнуть во многих реализациях функции calloc, которая выполняет примерно такую же операцию. В этом и состоит коварство многих ошибок, связанных с переполнением целых чисел: опасно не само это переполнение, а вызванное им переполнение буфера. Но подробнее об этом мы расскажем в грехе 3.
Вот как еще может возникать переполнение буфера:
#define MAX_BUF 256
void BadCode(char* input)
{
short len;
char buf[MAX_BUF];
len = strlen(input);
// конечно, мы можем использовать strcpy безопасно
if(len < MAX_BUF)
strcpy(buf, input);
}
На первый взгляд, все хорошо, не так ли? Но на самом деле здесь ошибка на ошибке. Детали мы отложим до обсуждения переполнения целых числе в грехе 3, а пока заметим, что литералы всегда имеют тип signed int. Если длина входных данных (строка input) превышает 32К, то переменная len станет отрицательна, она будет расширена до типа int с сохранением знака и окажется меньше MAX_BUF, что приведет к переполнению. Еще одна ошибка возникнет, если длина строки превосходит 64К. В этом случае мы имеем ошибку усечения: len оказывается маленьким положительным числом. Основной способ исправления – объявлять переменные для хранения размеров как имеющие тип size_t. Еще одна скрытая проблема заключается в том,