Издательский дом ООО "Гейм Лэнд"СПЕЦВЫПУСК ЖУРНАЛА ХАКЕР #58, СЕНТЯБРЬ 2005 г.

Инструктаж перед боем

Крис Касперски

Спецвыпуск: Хакер, номер #058, стр. 058-018-2


snprintf(buf,BUF_SIZE, "%s %s", FirstName, LastName);

snprintf(&buf[strlen(buf)], BUF_SIZE, " %s", Alias);

Во-первых, не "buf, BUF_SIZE", а "buf, BUF_SIZE-1", поскольку функция snprintf ожидает не размер буфера, а максимальное количество возвращаемых байт, за которыми должен следовать завершающий нуль, но... он не следует. Если strlen(FirsName)+strlen(" ")+strlen(LastName)) == BUF_SIZE, то snpritnf "забывает" о нем. Хорошенькое начало, нечего сказать! Если программист не поставит его туда самостоятельно, программа рухнет окончательно! Не найдя завершающего нуля, функция strlen выйдет далеко за пределы строки и остановится неизвестно где.

Во-вторых, "&buf[strlen(buf)], BUF_SIZE" должно быть заменено на "BUF_SIZE - strlen(buf) - 1". Программист по инерции использовал BUF_SIZE, не заметив, что часть буфера уже занята. И такие ошибки встречаются постоянно!

Правильный вариант выглядит так:

memset(buf, 0, BUF_SIZE);

if (snprintf(buf,BUF_SIZE-1,

"%s %s", FirstName, LastName)==-1) log("warring");

if (snprintf(&buf[strlen(buf)],BUF_SIZE- strlen(buf)-1,

" %s", Alias); log("warring");

Прямо не программа, а сплошное минное поле получается. Маленькая небрежность рушит все! Поэтому на чистом С слабонервным лучше не программировать :).

Лучшее средство от переполнения - это динамические массивы, которые легко реализовать на С++. Необходимость "ручного" контроля за границами стразу же отпадает. Под массив отводится именно столько памяти, сколько ему требуется, а если не удается выделить память, возбуждается исключение. Но это уже крайний случай. Для надежности можно перекрыть оператор [], выполняя автоматическую проверку границ при каждом обращении к массиву (впрочем, некоторые компиляторы умеют делать и самостоятельно - нужно только найти соответствующую опцию и активировать ее). Собственно говоря, динамические массивы являются частным случаем списков. Списки - это потрясающий инструмент, очень простой в управлении и не подверженный никаким переполнениям!

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

Пример использования динамических буферов (правильный вариант, не подверженный ошибкам переполнения):

dynchar buf = FirstName + " " + LastName + " " + Alias;

Он предельно прост. Только никогда не используй CString. Это жуткий класс. Пиши свои собственные динамические буфера, отличные примеры которых можно найти в "Искусстве программирования" Кнута.

Криптография наоборот

Назад на стр. 058-018-1  Содержание  Вперед на стр. 058-018-3