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

редактировать
Эта статья о защите памяти при разработке программного обеспечения. Для аппаратной защиты памяти см. Защита памяти.

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

СОДЕРЖАНИЕ
  • 1 История
  • 2 Подходы
  • 3 Типы ошибок памяти
  • 4 Рекомендации
История

Ошибки памяти сначала были рассмотрены в контексте систем управления ресурсами и разделения времени, чтобы избежать таких проблем, как « бомбы вилки». Разработки были в основном теоретическими, пока не появился червь Морриса, который использовал переполнение буфера в fingerd. С тех пор область компьютерной безопасности быстро развивалась, обостряясь с появлением множества новых атак, таких как атака с возвратом к libc, и такие методы защиты, как неисполняемый стек и рандомизация разметки адресного пространства. Рандомизация предотвращает большинство атак на переполнение буфера и требует от злоумышленника использования распыления кучи или других зависимых от приложения методов для получения адресов, хотя его внедрение было медленным. Однако развертывание технологии обычно ограничивается рандомизацией библиотек и расположением стека.

Подходы

DieHard, его переработанный DieHarder и Allinea Distributed Debugging Tool - это специальные распределители кучи, которые выделяют объекты на своей собственной странице случайной виртуальной памяти, позволяя останавливать и отлаживать недопустимые операции чтения и записи и выполнять отладку по той инструкции, которая их вызывает. Защита полагается на аппаратную защиту памяти, поэтому накладные расходы обычно невелики, хотя они могут значительно возрасти, если программа интенсивно использует выделение памяти. Рандомизация обеспечивает только вероятностную защиту от ошибок памяти, но часто может быть легко реализована в существующем программном обеспечении путем повторного связывания двоичного файла.

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

Имея доступ к исходному коду, существуют библиотеки, которые собирают и отслеживают допустимые значения указателей («метаданные») и проверяют каждый доступ указателя на соответствие метаданным на предмет достоверности, например сборщик мусора Boehm. В общем, безопасность памяти можно безопасно гарантировать, используя трассировку сборки мусора и вставку проверок времени выполнения при каждом доступе к памяти; у этого подхода есть накладные расходы, но меньше, чем у Valgrind. Все языки со сборкой мусора используют этот подход. Для C и C ++ существует множество инструментов, которые выполняют преобразование кода во время компиляции для проверки безопасности памяти во время выполнения, например CheckPointer и AddressSanitizer, которые налагают средний коэффициент замедления равный 2.

Другой подход использует статический анализ программы и автоматическое доказательство теорем, чтобы гарантировать, что программа свободна от ошибок памяти. Например, в языке программирования Rust реализована проверка заимствований для обеспечения безопасности памяти. Такие инструменты, как Coverity, предлагают статический анализ памяти для C. Интеллектуальные указатели C ++ являются ограниченной формой этого подхода.

Типы ошибок памяти

Могут возникать самые разные типы ошибок памяти:

  • Ошибки доступа: недопустимое чтение / запись указателя
  • Неинициализированные переменные - используется переменная, которой не присвоено значение. Он может содержать нежелательное или, в некоторых языках, искаженное значение.
    • Null указатель разыменования - разыменования неверный указатель или указатель на памятькоторая не была выделена
    • Дикие указатели возникают, когда указатель используется до инициализации некоторого известного состояния. Они демонстрируют такое же беспорядочное поведение, что и висячие указатели, хотя с меньшей вероятностью останутся незамеченными.
  • Утечка памяти - когда использование памяти не отслеживается или отслеживается неправильно
    • Исчерпание стека - возникает, когда программе не хватает места в стеке, обычно из-за слишком глубокой рекурсии. Страница охранник обычно останавливает программу, предотвращая повреждение памяти, но функции с большими кадрами стеки могут обойти страницу.
    • Исчерпание кучи - программа пытается выделить больше памяти, чем доступно. В некоторых языках это условие необходимо проверять вручную после каждого распределения.
    • Double free - повторные звонки на free могут преждевременно освободить новый объект по тому же адресу. Если точный адрес не использовался повторно, может произойти другое повреждение, особенно в распределителях, которые используют списки свободных адресов.
    • Invalid free - передача недопустимого адреса в free может повредить кучу.
    • Mismatched free - при использовании нескольких распределителей, попытка освободить память с помощью функции освобождения другого распределителя
    • Нежелательное сглаживание - когда одна и та же область памяти выделяется и изменяется дважды для несвязанных целей.
Рекомендации
Последняя правка сделана 2024-01-02 06:56:00
Содержание доступно по лицензии CC BY-SA 3.0 (если не указано иное).
Обратная связь: support@alphapedia.ru
Соглашение
О проекте