Наука — Школе |
В старых языках программирования (фортран и т.п.) при обращении к
элементам массива компилятор, как правило, не предусматривает проверок, выходит
ли индекс за границы массива. В другом старом языке — C,
где с массивами можно работать с помощью указателей — подобные проверки вообще
могут быть невозможны. В таких условиях, если происходит ошибочное обращение к
несуществующим элементам массива, то программа просто считывает содержимое ячеек
памяти, никакого отношения к данному массиву не имеющих, либо записывает туда
какую-то информацию, портя содержимое других переменных, возможно, в других
программах, и затем продолжает свою работу, уже скорее всего бессмыссленную.
В конце концов либо
выдается невнятный результат, либо программа аварийно останавливается в какой-то
точке, не имеющей никакого отношения к ошибке. Либо что-то неожиданное происходит с другими
программами на компьютере.
Понятно, что искать ошибку такого рода отнюдь не
легко — ведь при каждом новом вызове программа может загружаться в разные
области памяти и, соответственно, ошибочно обращаться к разным областям памяти.
Когда было обнаружено, насколько велика доля ошибок, связаных с выходом за границы массивов, в компиляторы стали добавлять возможность включать в код проверку индексов на предмет выхода за границы массива. Но это обычно делается, только если компилятор работает в специальном «отладочном» режиме. Разумеется, «настоящий» программист считает отладочный режим средством для новичков-«чайников».
Оберон/Компонентный Паскаль не предусматривает никакого специального отладочного режима, а проверки индексов на предмет выхода за границы массива отключить просто нельзя. В результате «настоящий» программист чувствует, что ему навязана роль «чайника», и выдвигает следующиее «практическое» возражение: дескать, включение таких проверок увеличивает и замедляет скомпилированную программу.
На самом деле экспериментальное изучение программ доказывает, что и
тот и другой эффект в подавляющем большинстве случаев ничтожен (при условии
тщательного дизайна языка и компилятора, как это имеет место для
Оберона/Компонентного Паскаля).
Жертвовать же надежностью всех программ
ради ускорения пусть даже на десяток процентов в редком случае — безответственно
(гораздо чаще имеет место замедление на уровне 1%, если его вообще удается
достоверно измерить).
Если же такая редкая программа предназначена для частого
использования, то и оптимизировать ее нужно особыми методами, например,
переписав соответствующий фрагмент непосредственно в машинных кодах (цикл, в
котором проверка индексов заметно сказывается на производительности, вряд ли
будет сложным, так что написать несколько команд в машинных кодах труда не
составит для специалистов, которые обычно занимаются такими программами; о
том, как включать фрагменты в машинных кодах непосредственно в программу на
Компонентном Паскале, см. в документации Блэкбокса
Platform-Specific Issues, раздел Code
procedures).
Возможно и возражение метафизическое, мол, ограничения такого рода (как и исключение
оператора GOTO,
и т.п.), навязываемые «профессионалу», ограничивают его свободу творчества. Но
во-первых, величина и сложность подавляющего большинства полезных программ
оставляет достаточный простор для творчества и за вычетом мелкой возни с
индексами массивов.
Во-вторых, программирование — сколько бы ни было в нем от
искусства — это прежде всего эффективное создание правильных, надежных и
эффективных программ (эффективность не имеет
смысла, если программа неправильная или приводит к взрыву ракеты или потере
финансовой отчетности).
Посмотрим с этой точки зрения на вариант ошибки такого рода из числа наихудших — а именно, на ситуацию, когда данные, записываемые в массив, поступают из Интернета. В таком случае, если программа не проверяет выход за границу буфера — массива, предназначенного для записи приходящей из Интернета информации, — можно заставить программу записать в какую-то область памяти исполняемый код и даже передать ему управление, тем самым перехватив управление компьютером. Именно в этом состоит излюбленный хакерами способ проникать в компьютеры, подключенные к Интернету — атака посредством переполнения буфера (buffer overflow).
Сообщениями о таких атаках пестрят новостные ленты компьютерного мира. Например, вспомним одну поучительную историю. В августе 2001 г. вице-президент Майкрософт Джим Олчин (Jim Allchin) объявил во время доклада на открытии конференции Intel Developers Forum в Сан Хосе, что в новой операционной системе Windows XP все возможные проблемы из разряда переполнение буфера были устранены посредством специального анализа исходных текстов на предмет безопасности (security audit). Но в декабре того же года была найдена «дыра» в одной из программ в составе Windows XP (в программах поддержки стандарта подключения внешних устройств Universal Plug and Play) — причем дыра оказалась именно из категории переполнение буфера.
Вопрос: Почему аудит исходных текстов, о котором объявил
Джим Олчин, не обнаружил этой проблемы?
Ответ: Потому что вовсе не равна нулю вероятность ошибиться живым
людям, выполнявшим аудит 50 миллионов строк программ на нечитабельном языке типа
C.
В начале 2002 г. Майкрософт на месяц приостановила нормальную работу, чтобы программистский персонал мог специально сосредоточиться на проблемах безопасности и надежности программ. Если оценить количество программистов Майкрософт в 20 тысяч человек при зарплате от $150 тыс. в год и выше, то стоимость месячника повышения квалификации выйдет не меньше 250 миллионов долларов. Майкрософт в состоянии это себе позволить. Остальным, видимо, все же дешевле перейти на инструменты программирования, где проверки индексов массивов не отключаются. Впрочем, и сама компания Майкрософт в настоящее время переходит на платформу .NET, в которой главный язык программирования — т.наз. C# — смоделирован во многом, в том числе и в отношении безопасности программирования, по образцу Оберона.
Наука — Школе |