Безопасность доступа к памяти

Материал из Википедии — свободной энциклопедии
Перейти к: навигация, поиск

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

Языки программирования с низким уровнем абстракций, такие как Си и Си++, поддерживающие непосредственный доступ к памяти компьютера (произвольную арифметику указателей, выделение и освобождение памяти) и приведение типов, но не имеющие автоматической проверки границ (англ.) массивов, не являются безопасными с точки зрения безопасности доступа к памяти[1].

Типы ошибок памяти[править | править вики-текст]

Различают несколько видов ошибок памяти (уязвимостей), которые могут возникать в некоторых языках программирования:

  • Нарушение границ массивов (англ.) — выражение, индексирующее массив, выходит из диапазона значений, установленного при определении этого массива. Отдельно выделяется особый подтип — ошибка неучтённой единицы[2]. Встречается при отсутствии проверок границ массивов и строк (Си, Си++)[3].
    • Переполнение буфера — запись за пределами выделенного в памяти буфера. Возникает при попытке записи в буфер блока данных, превышающего размер этого буфера. В результате переполнения могут быть испорчены данные, расположенные рядом с буфером[4], либо программа вовсе изменит своё поведение, вплоть до интерпретации записанных данных как исполняемого кода[5]. Использование данной уязвимости является одним из наиболее популярных способов взлома компьютерных систем[6].
    • Чтение за границами буфера (англ.) — чтение за пределами выделенного в памяти буфера. Последствиями могут служить нарушения безопасности системы (утрата конфиденциальности), нестабильное и неправильное поведение программы, ошибки прав доступа к памяти[7].
  • Ошибки при работе с динамической памятью — неправильное распоряжение динамически выделяемой памятью и указателями. В данном случае выделение памяти под объекты осуществляется во время выполнения программы[8], что может повлечь за собой ошибки времени исполнения. Данной уязвимости подвержены языки программирования с низким уровнем абстракций, поддерживающие непосредственный доступ к памяти компьютера (Си, Си++)[9].
    • Висячий указатель[10] — указатель, не ссылающийся на допустимый объект соответствующего типа. Данный вид указателей возникает, когда объект был удалён (или перемещён), но значение указателя не изменили на нулевое. В данном случае он всё ещё указывает на область памяти, где находился данный объект. В некоторых случаях это может стать причиной получения конфиденциальной информации злоумышленником; либо, если система уже перераспределила адресуемую память под другой объект, доступ по висячему указателю может повредить расположенные там данные[11].
    • Обращение по нулевому указателю. Нулевой указатель имеет специальное зарезервированное значение, показывающее, что данный указатель не ссылается на допустимый объект[12]. Обращение по нулевому указателю будет причиной исключительной ситуации[13] и аварийной остановки программы.
    • Освобождение ранее не выделенной памяти — попытка освободить область оперативной памяти, которая не является на данный момент выделенной (то есть свободна). Наиболее часто это проявляется в двойном освобождение памяти[14], когда происходит повторная попытка освободить уже освобождённую память. Данное действие может вызвать ошибку менеджера памяти[15]. В Си это происходит при повторном вызове функции free с одним и тем же указателем, без промежуточного выделения памяти.
    • Использование различных менеджеров памяти — ошибка, заключающаяся в разрыве связки аллокатор-деаллокатор памяти и использованием различных средств для работы с одним сегментом. Например, в Си++ использованием free для участка памяти, выделенного с помощью new или, аналогично, использованием delete после malloc. Стандарт Си++ не описывает какую-либо связь между new / delete и функциями работы с динамической памятью из Си, хотя new / delete в общем случае реализованы как обёртки malloc / free[16][17]. Смешанное использование может стать причиной неопределённого поведения[18].
    • Потеря указателя — утеря адреса выделенного фрагмента памяти при перезаписи его новым значением, который ссылается на другую область памяти[19]. При этом адресуемая предыдущим указателем память более недосягаема. Такой тип ошибки приводит к утечкам памяти, так как выделенная память не может быть освобождена. В Си это может случиться при повторном присваивании результата функции malloc одному и тому же указателю, без промежуточного освобождения памяти.


  • Неинициализированные переменные (англ.) — переменные, которые были объявлены, но не установлены в какое-либо значение, известное до времени их использования. Переменные будут иметь значение, но, в общем случае, труднопредсказуемое. Уязвимость для памяти могут возникнуть при наличии неинициализировынных указателей[20]. Эти указатели в своём поведении схожи с висячими указателями, попытка обращения по ним в большинстве случаев будет сопровождаться ошибками доступа или повреждением данных.
  • Ошибки нехватки памяти — проблемы, возникающие при недостатке количества доступной памяти для данной программы. [21]
    • Переполнение стека — превышение программой количества информации, которое может находиться в стеке вызовов. При этом программа аварийно завершается. Причиной ошибки может быть глубокая рекурсия, либо выделение большого количества памяти для локальных переменных на стеке.
    • Переполнение кучи

Обнаружение[править | править вики-текст]

Способы обеспечения[править | править вики-текст]

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

Примечания[править | править вики-текст]

  1. "3.2 Memory safety" / Erik Poll, "Lecture Notes on Language-Based Security". Radboud University Nijmegen, January 21, 2016 / "Language features that break memory safety include ..."
  2. "Why numbering should start at zero" / prof.dr. Edsger W. Dijkstra, "Why numbering should start at zero (EWD 831)". Plataanstraat 5, 5671 AL NUENEN, The Netherlands, 11 August 1982 / "... the use of the other three conventions has been a constant source of clumsiness and mistakes ..."
  3. "Approaches to bounds checking" / Richard Jones and Paul Kelly, "Bounds Checking for C". Imperial College, July 1995 / "One response to this analysis is to discard C, since this lack of efficient checkability is responsible for many software failures."
  4. Джон Эриксон. Хакинг. Искусство эксплойта. — СПб. : Символ-Плюс, 2010. — 0x320 Переполнение буфера. — С. 139. — ISBN 978-5-93286-158-5.
  5. Джон Эриксон. Хакинг. Искусство эксплойта. — СПб. : Символ-Плюс, 2010. — 0x321 Переполнение буфера в стеке. — С. 142. — ISBN 978-5-93286-158-5.
  6. "Chapter 6. Restrict Operations to Buffer Bounds (Avoid Buffer Overflow)" / David A. Wheeler, "Secure Programming HOWTO". Published v3.72, 2015-09-19 / "Buffer overflows are an extremely common and dangerous security flaw ..."
  7. "CWE-126: Buffer Over-read" / Common Weakness Enumeration, "CWE-126: Buffer Over-read". December 08, 2015 / "This typically occurs when the pointer or its index is incremented to a position beyond the bounds of the buffer ..."
  8. "C Runtime Memory Management" / Guy Keren, "Unix And C/C++ Runtime Memory Management For Programmers". 2001-2002 / "The runtime environment defines not only how memory is allocated and freed ..."
  9. Robert C. Seacord. Secure Coding in C and C++. — Addison-Wesley, 2013. — 4.4 Common C++ Memory Management Errors. — С. 162. — ISBN 978-0-321-82213-0.
  10. "Dangling Pointer. Smashing the Pointer for Fun and Profit" / Jonathan Afek, Adi Sharabani, "Dangling Pointer. Smashing the Pointer for Fun and Profit". Watchfire Corporation, 2007
  11. "Ссылка в никуда, или сломанный указатель" / Компьютерная газета, "Ссылка в никуда, или сломанный указатель" / "... уязвимости, к которым может привести неправильное использование указателей и ссылок."
  12. "What is this infamous null pointer, anyway?" / comp.lang.c Frequently Asked Questions, "Question 5.1" / "The language definition states that for each pointer type, there is a special value ..."
  13. "Class NullPointerException" / Oracle, "Java Platform, Standard Edition 7 API Specification" / "Thrown when an application attempts to use null in a case where an object is required."
  14. "CWE-415: Double Free" / Common Weakness Enumeration, "CWE-415: Double Free". December 08, 2015 / "When a program calls free() twice with the same argument ..."
  15. "Dynamic Memory Management in C" / Yan Huang, "Heap Overflows and Double-Free Attacks" / "If free(p) has already been called before, undefined behavior occurs."
  16. "4.1 The Default Free Store Allocator" / Andrei Alexandrescu, "Modern C++ Design: Generic Programming and Design Patterns Applied". Addison Wesley, February 01, 2001 / "... it is usually implemented as a thin wrapper around the C heap allocator ..."
  17. "C++ Runtime Memory Management" / Guy Keren, "Unix And C/C++ Runtime Memory Management For Programmers". 2001-2002 / "For example, the GNU C++ compiler's new operator actually invokes the C runtime malloc() function."
  18. "Can I free() pointers allocated with new? Can I delete pointers allocated with malloc()?" / Standard C++, "Memory Management" / "The C++ operators new and delete guarantee proper construction and destruction ... The C-style functions ... don’t ensure that."
  19. "Examples" / OWASP, "Memory leak" / "Example 1"
  20. Проблемы, связанные с указателями | Программирование на C и C
  21. NSS Library Memory Leak DoS

Ссылки[править | править вики-текст]