Иллюстрированный самоучитель по теории операционных систем

Ошибки программирования

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

Наибольшую опасность с точки зрения безопасности представляют ошибки в модулях, связанных с проверкой ACL, авторизацией и повышением уровня привилегий процессора (например, в диспетчере системных вызовов), а в системах семейства Unix – в setuid-программах.

Одна из наиболее опасных – и в то же время довольно распространенная в современных программах – ошибка приведена в примере 2.4. Рассмотрим этот код еще раз (пример 12.2).

Пример 12.2. Пример программы, подверженной срыву стека.

/* Фрагмент примитивной реализации сервера SMTP (RFC822) */
int parse_line(FILE * socket)
{
/* Согласно RFC822, команда имеет длину не более 4 байт,
а вся строка – не более 255 байт */
char cmd[5], args[255];
fscanf(socket, "%s %s\n", and, args);
/* Остаток программы нас не интересует */

Видно, что наша программа считывает из сетевого соединения строку, которая должна состоять из двух полей, разделенных пробелом, и заканчиваться символом перевода строки. В соответствии со спецификациями протокола SMTP, первое поле (команда) не может превышать четырех символов (к сожалению, не определено в протоколе более длинных команд), а строка целиком не может быть длиннее 255 байт.

Если наш партнер на другом конце соединения полностью соответствует требованиям [RFC 0822], наш код будет работать без проблем. Проблемы – причем серьезнейшие – возникнут, если нам передадут строку, которая этим требованиям не соответствует.

Превышение допустимой длины кодом команды не представляет большой опасности: лишние байты будут записаны в начало массива args и потеряны при записи в него его собственного поля. Настоящая опасность – это превышение длины всей строки. Для нашего партнера не представляет никаких сложностей сгенерировать последовательность из более чем 255 символов, не содержащую переводов строки (рис. 12.21).

Иллюстрированный самоучитель по теории операционных систем › Безопасность › Ошибки программирования
Рис. 12.21. Срыв буфера

Тогда буфер arg переполнится, но за ним в памяти следует вовсе не другой буфер, а – ни много, ни мало, заголовок стекового кадра, в котором содержится адрес возврата нашей подпрограммы. Важно подчеркнуть, впрочем, что даже если бы там и находились другие переменные, это не было бы для нашего диверсанта препятствием – ему достаточно просто передать более длинный блок данных.

Если Вы заметили ошибку, выделите, пожалуйста, необходимый текст и нажмите CTRL + Enter, чтобы сообщить об этом редактору.