19 смертных грехов, угрожающих безопасности программ
Шрифт:
Тестирование
Обнаружить гонку во время тестирования трудно, но существуют методики по искоренению этого греха. Одна из самых простых – прогонять тесты на быстрой многопроцессорной машине. Если вы наблюдаете отказы, которые не удается воспроизвести на однопроцессорной машине, значит, дело почти наверняка в гонке.
Для поиска ошибок, связанных с сигналами, напишите программу, которая будет один за другим посылать сигналы тестируемому приложению, и понаблюдайте за поведением последнего. Отметим, что одного прогона теста может оказаться недостаточно, так как ошибка возникает нерегулярно.
Чтобы
Примеры из реальной жизни
Следующие примеры взяты из базы данных CVE .
CVE–2001–1349
Цитата из бюллетеня CVE:
В программе sendmail до версии 8.11.4, а также версии 8.12.0 до 8.12.0.BetalO имеется гонка в обработчике сигнала, которая позволяет локальному пользователю провести DoS–атаку, возможно, затереть содержимое кучи и получить дополнительные привилегии.
Эта ошибка описана в статье Залевски о доставке сигналов, на которую мы уже ссылались выше. Ошибка, допускающая написание эксплойта, возникает из–за двойного освобождения памяти, на которую указывает глобальная переменная. Это происходит при повторном входе в обработчик сигнала. Хотя ни в документации по Sendmail, ни в базе уязвимостей SecurityFocus не приводится код общедоступного эксплойта, но в первоначальной статье такая ссылка (сейчас не работающая) была.
CAN–2003–1073
Цитата из бюллетеня CVE:В программе at, поставляемой в составе ОС Solaris версий с 2.6 по 9, может возникнуть гонка, позволяющая локальному пользователю удалить произвольный файл путем задания флага–г с аргументом, содержащим две точки (..) в имени задания. Для этого нужно изменить содержимое каталога после того, как программа проверит разрешение на удаление файла, но до выполнения самой операции удаления.
Этот эксплойт детально описан на странице www.securityfocus.com/archive/ 1 /308577/2003–01–27/2003–02–02/0. Помимо гонки, в нем используется еще одна ошибка: не проверяется наличие последовательности символов../ в имени файла, из–за чего планировщик at может удалить файлы, находящиеся вне каталога для хранения заданий.
CVE–2004–0849
Цитата из бюллетеня CVE:Гонка в сервере Microsoft Windows Media позволяет удаленному противнику вызвать отказ от обслуживания в сервисе Windows Media Unicast Service путем отправки специального запроса.
Подробные сведения об этой уязвимости можно найти на странице www.microsoft.com/technet/security/Bulletin/MS00–064.mspx.
Искупление греха
Прежде всего нужно разобраться в том, как правильно писать реентерабельный код. Даже если вы не намереваетесь запускать программу в многопоточной среде, могут найтись люди, которые захотят перенести ваше приложение на другую платформу и повысить его производительность за счет организации нескольких потоков. Они оценят ваше стремление избегать побочных эффектов. Говоря о переносимости, следует отметить, что в Windows не реализован системный вызов fork и создание нового процесса обходится очень дорого, зато создание потока не влечет почти никаких накладных расходов.
Решение о том, чем пользоваться – процессами или потоками, зависит от операционной системы и специфики приложения, но в любом случае код, не имеющий побочных эффектов, будет более переносим, и возникновение гонок в нем маловероятно.
Если в программе есть параллельные контексты исполнения, будь то процессы или потоки, то доступ к разделяемым ресурсам необходимо тщательно синхронизировать. Эта тема подробно рассматривается в других книгах, мы же лишь слегка затронем ее. Вот на что нужно обращать внимание:
□ если программа возбуждает исключение, не сняв замка, возможна тупиковая ситуация в другой части программы, которая ждет освобождения этого замка. Один из способов решения этой проблемы – инкапсулировать захват и освобождение замка в объект С++, тогда в ходе раскрутки стека будет вызван деструктор объекта, который и освободит замок. Отметим, что при этом захваченный ресурс может остаться в неопределенном состоянии; в некоторых случаях лучше допустить тупиковую ситуацию, чем продолжать работу с таким ресурсом;
□ при необходимости захватить несколько замков делайте это всегда в одном и том же порядке, а освобождайте их строго в обратном порядке. Если вам кажется, что для выполнения некоторой операции нужно захватить несколько замков, обдумайте ситуацию еще раз. Возможно, найдется более элегантное и не столь сложное проектное решение;
□ старайтесь освободить замок как можно скорее. В противоречие с предыдущим параграфом отметим, что иногда наличие нескольких замков позволяет уменьшить величину захваченного ресурса, за счет чего снижается вероятность тупиковой ситуации и значительно повышается производительность программы. Это скорее искусство, чем наука. Проектируйте тщательно и советуйтесь с коллегами;
□ никогда не рассчитывайте на то, что другой процесс или поток не может прервать системный вызов. Для выполнения системного вызова может потребоваться от нескольких тысяч до нескольких миллионов машинных команд. Раз нельзя ожидать, что даже один системный вызов отработает без прерывания, не смейте и думать о том, что между двумя системными вызовами исполнение программы не будет прервано.
В обработчике сигнала или исключения единственно безопасная вещь – это вызов exit. Наилучшие рекомендации по этому вопросу мы встречали в статье Михала Залевски «Delivering Signals for Fun and Profit: Understanding, Exploiting and Preventing Signal Handling Related Vulnerabilities*-: