Проверка границ
Проверка границ — это любой метод определения, лежит ли переменная внутри некоторых границ перед тем, как переменная будет использована. Обычно она употребляется, чтобы убедиться, что число соответствует некоторому типу (проверка диапазона), или что значение переменной, которая используется в качестве индекса массива, находится в допустимых границах этого массива (проверка индекса). Неудачная проверка границ, как правило, приводит к генерации сигнала исключения.
Поскольку выполнение проверки границ при каждом использовании может занимать много времени, она не всегда осуществляется. Устранение проверки границ — это техника оптимизирующего компилятора, которая исключает ненужные проверки границ.
Проверка диапазона
Проверка диапазона — это проверка, гарантирующая, что число находится в пределах заданного интервала. Например, что значение, которое собираются присвоить 16-битному целому находится внутри его допустимой ёмкости (то есть проверка на переполнение). Это не совсем то же самое, что проверка типов. Другая проверка диапазона может быть более строга. Например, переменная, содержащая календарный месяц в виде номера, может быть объявлена принимающей границы только от 1 до 12.
Пример на языке Python:
def set_month(month: int):
if month < 1 or month > 12:
raise ValueError("The month must be between 1 and 12")
Проверка индекса
Проверка индекса означает, что во всех выражениях индексирования массива значение индекса проверяются на границы массива (которые были определены во время определения массива), и, если индекс вне границ, дальнейшее выполнение приостанавливается посредством некоторого вида ошибки. Поскольку чтение, а особенно запись, значения вне границ массива может вызвать неверную работу программы, сбой, или уязвимость безопасности (см. Переполнение буфера), проверка индекса является частью многих высокоуровневых языков программирования.
Ранние компилируемые языки программирования с проверкой индекса включали ALGOL 60, ALGOL 68 и Паскаль, а также интерпретируемые языки программирования, такие как Бейсик.
Многие языки программирования, такие как Си, для повышения скорости никогда не осуществляют автоматическую проверку границ. Однако, это оставляет много невыловленных ошибок на единицу и переполнения буфера. Многие программисты считают, что эти языки жертвуют слишком многим ради быстрого выполнения[1]. В своей лекции на вручении премии Тьюринга Ч. Э. Р. Хоар в 1980 году описал свой опыт разработки ALGOL 60, языка, который включал проверку границ, и сказал:
В результате этого принципа проверка каждого индекса каждой индексированной переменной при каждом обращении выполнялась во время выполнения программы с учетом как верхней, так и нижней границы массива, объявленной при его создании. Много лет спустя мы спросили наших клиентов, желают ли они, чтобы мы предоставили возможность отключить эти проверки в интересах повышения эффективности при производственных запусках. Единогласно они настоятельно рекомендовали нам этого не делать, поскольку уже знали, как часто возникают ошибки с индексами в производственных запусках, где их необнаружение могло бы привести к катастрофическим последствиям. С тревогой и ужасом отмечаю, что даже в 1980 году разработчики языков и пользователи так и не усвоили этот урок. В любой уважающей себя отрасли инженерии несоблюдение таких элементарных мер предосторожности давно было бы противозаконным.
Ведущие языки, которые осуществляют проверку во время выполнения, включают Ada, C#, Haskell, Java, JavaScript, Лисп, PHP, Python, Ruby, Rust и Visual Basic. Языки D и OCaml имеют проверку границ во время выполнения, которую можно включить или отключить при компиляции. В C++ проверка границ во время выполнения не является частью языка, но является частью STL и включается при компиляции ключом (_GLIBCXX_DEBUG=1 или _LIBCPP_DEBUG=1). C# поддерживает также небезопасные области — секции кода, которые (среди прочего) временно приостанавливают проверку границ для повышения эффективности. Это полезно для ускорения небольших критически важных участков кода («бутылочных горлышек») без ущерба для безопасности всей программы.
Язык программирования JS++ способен анализировать выход за границы индекса массива или ключа коллекции при компиляции с помощью особых «existent» типов. Например, есть тип int и есть «existent» тип int+. Эти типы можно рассматривать как «типы с проверкой границ»[2]. Это номинальный тип[3] описывающий, находится ли индекс или ключ в пределах допустимых границ или за ними, и направляющий генерацию кода[2].
Аппаратная проверка границ
Надёжность, добавленная проверкой границ, обязательно стоит ЦПУ затрат, если проверка осуществляется программно. Однако, если проверка может быть осуществлена аппаратно, надёжность во время исполнения может быть обеспечена «бесплатно». Ранней системой с аппаратной поддержкой проверки границ была серия мейнфреймов ICL 2900 Series, представленная в 1974 году[4]. Компьютер VAX имеет ассемблерную инструкцию INDEX для проверки индекса массива, которая принимает 6 операндов и все они могут использовать любой тип адресации системы VAX. B6500 и аналогичные компьютеры компании Burroughs осуществляют проверку границ аппаратно, независимо от языка программирования, из которого были получены машинные коды. Ограниченное количество более поздних процессоров имеют специализированные инструкции для проверки границ, например, инструкция CHK2 в серии Motorola 68000.
Исследования методов использования встроенного блока управления виртуальной памятью процессоров x86 для обеспечения безопасности доступа к массивам и буферам ведутся как минимум с 2005 года[5]. В 2015 году компания Intel представила расширения Intel MPX в своей архитектуре процессоров Skylake, которые сохраняют границы в регистре ЦПУ и таблице в памяти. По состоянию на начало 2017 года, как минимум компилятор GCC поддерживает расширения MPX.
См. также
Примечания
- ↑ Cowan, Wagle, etc, 1999, с. 119–129.
- ↑ 1 2 JS++ 0.9.0: Efficient Compile Time Analysis of Out-of-Bounds Errors – JS++ Blog (11 января 2019). Архивировано 12 января 2019 года.
- ↑ Номинальная и структурная типизация. Runebook.dev. Дата обращения: 30 ноября 2025.
- ↑ Buckle, 1978, с. 17,77.
- ↑ Lam, Chiueh, 2005, с. 388–397.
Литература
- Cowan C., Wagle F., Calton Pu, Beattie S., Walpole J. Buffer overflows: Attacks and defenses for the vulnerability of the decade // Proceedings DARPA Information Survivability Conference and Exposition. DISCEX'00. — 1999. — Т. 2. — ISBN 978-0-7695-0490-2. — doi:10.1109/DISCEX.2000.821514.
- J. K. Buckle. The ICL 2900 Series. — Macmillan Computer Science Series, 1978. — ISBN 978-0-333-21917-1. — [Архивировано из оригинала 20 апреля 2018 года.]
- Lap-Chung Lam, Tzi-Cker Chiueh. Checking Array Bound Violation Using Segmentation Hardware // 2005 International Conference on Dependable Systems and Networks (DSN'05). — 2005. — ISBN 0-7695-2282-3. — doi:10.1109/DSN.2005.25.
Ссылки
- “On The Advantages Of Tagged Architecture”, IEEE Transactions On Computers, Volume C-22, Number 7, July, 1973.
- “The Emperor’s Old Clothes Архивировано 2 октября 2017 года.”, The 1980 ACM Turing Award Lecture, CACM volume 24 number 2, February 1981, pp 75–83.
- “Bcc: Runtime checking for C programs”, Samuel C. Kendall, Proceedings of the USENIX Summer 1983 Conference.
- “Bounds Checking for C”, Richard Jones and Paul Kelly, Imperial College, July 1995.
- “ClearPath Enterprise Servers MCP Security Overview”, Unisys, April 2006.
- “Secure Virtual Architecture: A Safe Execution Environment for Commodity Operating Systems”, John Criswell, Andrew Lenharth, Dinakar Dhurjati, Vikram Adve, SOSP'07 21st ACM Symposium on Operating Systems Principles, 2007.
- “Fail-Safe C”, Yutaka Oiwa. Implementation of the Memory-safe Full ANSI-C Compiler. ACM SIGPLAN Conference on Programing Language Design and Implementations (PLDI2009), June 2009.
- “address-sanitizer”, Timur Iskhodzhanov, Alexander Potapenko, Alexey Samsonov, Kostya Serebryany, Evgeniy Stepanov, Dmitriy Vyukov, LLVM Dev Meeting, November 18, 2011.
- Safe C Library of Bounded APIs
- The Safe C Library // Dr. Dobb's Journal. — 2009. — Февраль. Архивировано из оригинала 2 декабря 2013 года.
- Safe C API—Concise solution of buffer overflow, The OWASP Foundation, OWASP AppSec, Beijing 2011
- The GNU C++ Library Manual Macros
- libc++ 11.0 documentation Debug Mode