stringtranslate.com

Нулевой указатель

В вычислительной технике нулевой указатель или нулевая ссылка — это значение, сохраненное для указания того, что указатель или ссылка не ссылается на допустимый объект . Программы обычно используют нулевые указатели для представления таких условий, как конец списка неизвестной длины или невозможность выполнить какое-либо действие; такое использование нулевых указателей можно сравнить с типами, допускающими значение null , и со значением Nothing в опциональном типе .

Не следует путать нулевой указатель с неинициализированным указателем : нулевой указатель гарантированно не равен любому указателю, указывающему на допустимый объект. Однако, в общем, большинство языков не предоставляют такой гарантии для неинициализированных указателей. Он может быть равен другим допустимым указателям или равен нулевым указателям. Он может делать и то, и другое в разное время, или поведение сравнения может быть неопределенным . Кроме того, в языках, предлагающих такую ​​поддержку, правильное использование зависит от индивидуального опыта каждого разработчика и инструментов линтера. Даже при правильном использовании нулевые указатели семантически неполны , поскольку они не дают возможности выразить разницу между значением «Неприменимо» и значением «Неизвестно» или значением «Будущее».

Поскольку нулевой указатель не указывает на значимый объект, попытка доступа к данным, хранящимся в этой (недопустимой) ячейке памяти, может вызвать ошибку времени выполнения или немедленный сбой программы. Это ошибка нулевого указателя . Это один из наиболее распространенных типов уязвимостей программного обеспечения, [1] и Тони Хоар , который представил эту концепцию, назвал ее «ошибкой на миллиард долларов». [2]

С

В языке C два нулевых указателя любого типа гарантированно считаются равными при сравнении. [3] Макрос препроцессора NULLопределен как константа нулевого указателя, определяемая реализацией в , [4] который в C99 может быть переносимо выражен как , целочисленное значение, преобразованное в тип (см. указатель на тип void ). [5] Стандарт C не говорит, что нулевой указатель совпадает с указателем на адрес памяти  0, хотя на практике это может иметь место. Разыменование нулевого указателя является неопределенным поведением в C, [6] и соответствующая реализация может предполагать, что любой разыменованный указатель не является нулевым.<stdlib.h>((void *)0)0 void*

На практике разыменование нулевого указателя может привести к попытке чтения или записи из памяти , которая не отображена, вызывая ошибку сегментации или нарушение доступа к памяти. Это может проявиться как сбой программы или трансформироваться в программное исключение , которое может быть перехвачено программным кодом. Однако существуют определенные обстоятельства, когда это не так. Например, в реальном режиме x86 адрес доступен для чтения и также обычно доступен для записи, и разыменование указателя на этот адрес является совершенно допустимым, но обычно нежелательным действием, которое может привести к неопределенному, но не вызывающему сбоя поведению в приложении; если нулевой указатель представлен как указатель на этот адрес, его разыменование приведет к такому поведению. Бывают случаи, когда разыменование указателя на нулевой адрес является преднамеренным и четко определенным; например, код BIOS , написанный на языке C для 16-битных устройств реального режима x86, может записывать таблицу дескрипторов прерываний (IDT) по физическому адресу 0 машины, разыменовывая указатель с тем же значением, что и нулевой указатель для записи. Компилятор также может оптимизировать разыменование нулевого указателя, избегая ошибки сегментации, но вызывая другое нежелательное поведение. [7]0000:0000

С++

В C++, хотя NULLмакрос был унаследован от C, целочисленный литерал для нуля традиционно предпочитался для представления константы нулевого указателя. [8] Однако в C++11 была введена явная константа нулевого указателя nullptrи тип, nullptr_tкоторые следует использовать вместо нее.

Другие языки

В некоторых средах языка программирования (например, по крайней мере в одной фирменной реализации Lisp) [ требуется ссылка ] значение, используемое в качестве нулевого указателя (называемого nilв Lisp ), на самом деле может быть указателем на блок внутренних данных, полезных для реализации (но явно не доступных из пользовательских программ), что позволяет использовать тот же регистр в качестве полезной константы и быстрого способа доступа к внутренним данным реализации. Это известно как вектор nil.

В языках с теговой архитектурой возможно нулевой указатель можно заменить теговым объединением , которое обеспечивает явную обработку исключительного случая; фактически, возможно нулевой указатель можно рассматривать как теговый указатель с вычисляемым тегом.

В языках программирования используются различные литералы для нулевого указателя . Например, в Python нулевое значение называется None. В Pascal и Swift нулевой указатель называется nil. В Eiffel он называется voidссылкой.

Разыменование null

Поскольку нулевой указатель не указывает на значимый объект, попытка разыменовать (т. е. получить доступ к данным, хранящимся в этой ячейке памяти) нулевой указатель обычно (но не всегда) вызывает ошибку времени выполнения или немедленный сбой программы. MITRE перечисляет ошибку нулевого указателя как одну из наиболее часто используемых уязвимостей программного обеспечения. [9]

Смягчение

Хотя у нас могут быть языки без нулевых значений, в большинстве из них вероятность нулевых значений есть, поэтому существуют методы, позволяющие избежать или облегчить отладку разыменования нулевых указателей. [12] Бонд и др. [12] предлагают модифицировать виртуальную машину Java (JVM) для отслеживания распространения нулевых значений.

У нас есть три уровня обработки нулевых ссылок в порядке эффективности:

1. языки без нуля;

2. языки, которые могут статически анализировать код, чтобы избежать возможности разыменования null во время выполнения;

3. если во время выполнения может произойти разыменование null, инструменты, помогающие отладить.

Чистые функциональные языки являются примером уровня 1, поскольку прямой доступ к указателям не предоставляется, а весь код и данные являются неизменяемыми. Пользовательский код, работающий на интерпретируемых или виртуальных машинных языках, обычно не страдает от проблемы разыменования нулевого указателя. [ dubiousdiscussion ]

В тех случаях, когда язык предоставляет или использует указатели, которые могут стать недействительными, можно избежать разыменования null во время выполнения, обеспечив проверку во время компиляции с помощью статического анализа или других методов, с синтаксической помощью языковых функций, таких как те, которые можно увидеть в языке программирования Eiffel с безопасностью Void [13], чтобы избежать разыменования null, D [ 14] и Rust [15] .

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

Последним средством уровня 3 является случай, когда во время выполнения возникает нулевая ссылка; в этом случае могут помочь средства отладки.

Альтернативы нулевым указателям

Как правило, для каждого типа структуры или класса определяются некоторые объекты, представляющие некоторое состояние бизнес-логики, заменяющие неопределенное поведение на null. Например, «future» для указания поля внутри структуры, которое не будет доступно прямо сейчас (но для которого мы заранее знаем, что в будущем оно будет определено), «not applied» для указания поля в ненормализованной структуре, «error», «timeout» для указания того, что поле не может быть инициализировано (вероятно, остановив нормальное выполнение полной программы, потока, запроса или команды).

История

В 2009 году Тони Хоар заявил [16] [17] , что он изобрел нулевую ссылку в 1965 году как часть языка ALGOL W. В этой ссылке 2009 года Хоар описывает свое изобретение как «ошибку на миллиард долларов»:

Я называю это своей ошибкой на миллиард долларов. Это было изобретение нулевой ссылки в 1965 году. В то время я проектировал первую всеобъемлющую систему типов для ссылок в объектно-ориентированном языке (ALGOL W). Моей целью было гарантировать, что все использование ссылок должно быть абсолютно безопасным, с проверкой, выполняемой автоматически компилятором. Но я не смог устоять перед искушением ввести нулевую ссылку, просто потому, что это было так легко реализовать. Это привело к бесчисленным ошибкам, уязвимостям и сбоям системы, которые, вероятно, нанесли миллиард долларов боли и ущерба за последние сорок лет.

Смотрите также

Примечания

  1. ^ "CWE-476: Разыменование нулевого указателя". MITRE .
  2. ^ "Null References: The Billion Dollar Mistake". InfoQ . Получено 5 сентября 2024 г. .
  3. ^ ISO/IEC 9899, ​​пункт 6.3.2.3, абзац 4.
  4. ^ ISO/IEC 9899, ​​пункт 7.17, параграф 3: NULL... который расширяется до константы нулевого указателя, определяемой реализацией...
  5. ^ ISO/IEC 9899, ​​пункт 6.3.2.3, абзац 3.
  6. ^ ab ISO/IEC 9899, ​​пункт 6.5.3.2, параграф 4, особенно сноска 87.
  7. ^ Лэттнер, Крис (2011-05-13). «Что каждый программист на C должен знать о неопределенном поведении № 1/3». blog.llvm.org . Архивировано из оригинала 2023-06-14 . Получено 2023-06-14 .
  8. ^ Страуструп, Бьярне (март 2001 г.). «Глава 5: Квалификатор (§5.4) предотвращает случайное переопределение и гарантирует, что может использоваться там, где требуется константа». Язык программирования C++ (14-е издание 3-го изд.). Соединенные Штаты и Канада: Addison–Wesley. стр. 88. ISBN
    constNULLNULL 0-201-88954-4.
  9. ^ "CWE-476: Разыменование нулевого указателя". MITRE .
  10. ^ Язык программирования Objective-C 2.0 , раздел «Отправка сообщений в nil».
  11. ^ "Эксплуатируемое ядро ​​OS X разыменование указателя NULL в AppleGraphicsDeviceControl"
  12. ^ ab Bond, Michael D.; Nethercote, Nicholas; Kent, Stephen W.; Guyer, Samuel Z.; McKinley, Kathryn S. (2007). "Отслеживание плохих яблок". Труды 22-й ежегодной конференции ACM SIGPLAN по системам и приложениям объектно-ориентированного программирования - OOPSLA '07 . стр. 405. doi :10.1145/1297027.1297057. ISBN 9781595937865. S2CID  2832749.
  13. ^ "Void-safety: Предыстория, определение и инструменты" . Получено 24.11.2021 .
  14. ^ Бартош Милевски. "SafeD – D Programming Language" . Получено 17 июля 2014 г.
  15. ^ "Fearless Security: Memory Safety". Архивировано из оригинала 8 ноября 2020 г. Получено 4 ноября 2020 г.
  16. Тони Хоар (25 августа 2009 г.). «Нулевые ссылки: ошибка на миллиард долларов». InfoQ.com.
  17. ^ Тони Хоар (25.08.2009). «Презентация: «Нулевые ссылки: ошибка на миллиард долларов»». InfoQ.com.

Ссылки