Главная страница проекта ИНФОРМАТИКА-21

Наука Школе

О проверках выхода индексов за границы массивов

В старых языках программирования (фортран и т.п.) при обращении к элементам массива компилятор, как правило, не предусматривает проверок, выходит ли индекс за границы массива. В другом старом языке — 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) — причем дыра оказалась именно из категории переполнение буфера.

В начале 2002 г. Майкрософт на месяц приостановила нормальную работу, чтобы программистский персонал мог специально сосредоточиться на проблемах безопасности и надежности программ. Если оценить количество программистов Майкрософт в 20 тысяч человек при зарплате от $150 тыс. в год и выше, то стоимость месячника повышения квалификации выйдет не меньше 250 миллионов долларов. Майкрософт в состоянии это себе позволить. Остальным, видимо, все же дешевле перейти на инструменты программирования, где проверки индексов массивов не отключаются. Впрочем, и сама компания Майкрософт в настоящее время переходит на платформу .NET, в которой главный язык программирования — т.наз. C# — смоделирован во многом, в том числе и в отношении безопасности программирования, по образцу Оберона.

Главная страница проекта ИНФОРМАТИКА-21

Наука Школе