Безопасность памяти
Безопасность памяти — это состояние защиты от различных ошибок программного обеспечения и уязвимостей безопасности при доступе к памяти , таких как переполнение буфера и висячие указатели . [1] Например, Java считается безопасной для памяти, поскольку ее функция обнаружения ошибок во время выполнения проверяет границы массива и разыменование указателей. [1] Напротив, C и C++ допускают произвольную арифметику указателей с указателями, реализованными как прямые адреса памяти, без возможности проверки границ . [2] и, следовательно, потенциально небезопасны для памяти . [3]
История
[ редактировать ]Ошибки памяти впервые рассматривались в контексте систем управления ресурсами (вычислений) и разделения времени , чтобы избежать таких проблем, как « вилочные бомбы» . [4] Разработки носили в основном теоретический характер до появления червя Морриса , который использовал переполнение буфера в Fingerd . [5] После этого сфера компьютерной безопасности быстро развивалась, расширяясь за счет множества новых атак, таких как атака с возвратом в libc , и методов защиты, таких как неисполняемый стек. [6] и рандомизация структуры адресного пространства . Рандомизация предотвращает большинство атак на переполнение буфера и требует от злоумышленника использования распыления кучи или других методов, зависящих от приложения, для получения адресов, хотя ее внедрение происходит медленно. [5] Однако внедрение этой технологии обычно ограничивается рандомизацией библиотек и расположением стека.
Влияние
[ редактировать ]В 2019 году инженер по безопасности Microsoft сообщил, что 70% всех уязвимостей безопасности были вызваны проблемами безопасности памяти. [7] В 2020 году команда Google аналогичным образом сообщила, что 70% всех «серьезных ошибок безопасности» в Chromium были вызваны проблемами безопасности памяти. Многие другие громкие уязвимости и эксплойты в критическом программном обеспечении в конечном итоге возникли из-за недостаточной безопасности памяти, включая Heartbleed. [8] и давняя ошибка повышения привилегий в sudo . [9] Распространенность и серьезность уязвимостей и эксплойтов, возникающих из-за проблем с безопасностью памяти, побудили некоторых исследователей безопасности описать выявление проблем с безопасностью памяти как «стрелять рыбу в бочку» . [10]
Подходы
[ редактировать ]Большинство [ нужна ссылка ] современные языки программирования высокого уровня по умолчанию безопасны для памяти, хотя и не полностью, поскольку они проверяют только свой собственный код, а не систему, с которой взаимодействуют. Автоматическое управление памятью в форме сборки мусора является наиболее распространенным методом предотвращения некоторых проблем безопасности памяти, поскольку оно предотвращает распространенные ошибки безопасности памяти, такие как использование после освобождения для всех данных, выделенных во время выполнения языка. [11] В сочетании с автоматической проверкой границ при любом доступе к массиву и отсутствием поддержки арифметики с необработанными указателями языки со сборкой мусора обеспечивают надежные гарантии безопасности памяти (хотя гарантии могут быть более слабыми для операций низкого уровня, явно помеченных как небезопасные, таких как использование внешнего интерфейса функции). ). Однако издержки производительности, связанные со сборкой мусора, делают эти языки непригодными для некоторых приложений, критичных к производительности. [1]
Для языков, использующих ручное управление памятью , безопасность памяти обычно не гарантируется средой выполнения. Вместо этого свойства безопасности памяти должны либо гарантироваться компилятором посредством статического анализа программы и автоматического доказательства теорем , либо тщательно управляться программистом во время выполнения. [11] Например, язык программирования Rust реализует проверку заимствований для обеспечения безопасности памяти. [12] в то время как C и C++ не предоставляют никаких гарантий безопасности памяти. Значительное количество программного обеспечения, написанного на C и C++, стимулировало разработку внешних инструментов статического анализа, таких как Coverity , который предлагает статический анализ памяти для C. [13]
Крепкий орешек, [14] его редизайн DieHarder, [15] и инструмент распределенной отладки Allinea — это специальные распределители кучи, которые распределяют объекты на их собственной случайной странице виртуальной памяти, позволяя останавливать и отлаживать недопустимые операции чтения и записи и выполнять отладку по той инструкции, которая их вызывает. Защита основана на аппаратной защите памяти, поэтому накладные расходы обычно невелики, хотя они могут значительно возрасти, если программа интенсивно использует выделение. [16] Рандомизация обеспечивает только вероятностную защиту от ошибок памяти, но часто может быть легко реализована в существующем программном обеспечении путем перелинковки двоичного файла.
Инструмент проверки памяти Valgrind использует симулятор набора команд и запускает скомпилированную программу на виртуальной машине с проверкой памяти, обеспечивая гарантированное обнаружение подмножества ошибок памяти во время выполнения. Однако обычно это замедляет работу программы в 40 раз. [17] и, кроме того, он должен быть явно проинформирован о пользовательских распределителях памяти. [18] [19]
Имея доступ к исходному коду, существуют библиотеки, которые собирают и отслеживают допустимые значения указателей («метаданные») и проверяют достоверность каждого доступа к указателю по метаданным, например сборщик мусора Boehm . [20] В общем, безопасность памяти можно безопасно гарантировать, используя отслеживание сборки мусора и вставку проверок во время выполнения при каждом доступе к памяти; этот подход имеет накладные расходы, но меньшие, чем у Valgrind. Все языки со сборкой мусора используют этот подход. [1] Для C и C++ существует множество инструментов, которые выполняют преобразование кода во время компиляции для проверки безопасности памяти во время выполнения, например CheckPointer. [21] и AddressSanitizer , который устанавливает средний коэффициент замедления 2. [22]
BoundWarden — это новый подход к использованию пространственной памяти, который использует комбинацию методов преобразования во время компиляции и методов одновременного мониторинга во время выполнения. [23]
Виды ошибок памяти
[ редактировать ]Могут возникнуть различные типы ошибок памяти: [24] [25]
- Ошибки доступа : недопустимое чтение/запись указателя.
- Переполнение буфера — записи за пределами буфера могут повредить содержимое соседних объектов или внутренние данные (например, бухгалтерскую информацию для кучи ) или возврата . адреса
- Чрезмерное чтение буфера — чтение за пределами границ может раскрыть конфиденциальные данные или помочь злоумышленникам обойти рандомизацию структуры адресного пространства .
- Ошибка неверной страницы – доступ к указателю за пределами пространства виртуальной памяти. Разыменование нулевого указателя часто приводит к исключению или завершению программы в большинстве сред, но может привести к повреждению ядер операционной системы или систем без защиты памяти , а также когда использование нулевого указателя предполагает большое или отрицательное смещение.
- Использовать после освобождения — разыменование висячего указателя, хранящего адрес удаленного объекта.
- Неинициализированные переменные – используется переменная, которой не присвоено значение. Он может содержать нежелательное или, в некоторых языках, поврежденное значение.
- нулевого указателя Разыменование – разыменование недопустимого указателя или указателя на память, которая не была выделена.
- Дикие указатели возникают, когда указатель используется до инициализации некоторого известного состояния. Они ведут себя так же хаотично, как и висячие указатели, хотя у них меньше шансов остаться незамеченными.
- Утечка памяти – когда использование памяти не отслеживается или отслеживается неправильно.
- Исчерпание стека — происходит, когда программе не хватает места в стеке, обычно из-за слишком глубокой рекурсии . Защитная страница обычно останавливает программу, предотвращая повреждение памяти, но функции с большими кадрами стека могут обходить эту страницу.
- Исчерпание кучи — программа пытается выделить больше памяти, чем доступно. В некоторых языках это условие необходимо проверять вручную после каждого распределения.
- Двойное освобождение — повторные вызовы free могут преждевременно освободить новый объект по тому же адресу. Если точный адрес не использовался повторно, могут возникнуть другие повреждения, особенно в распределителях, использующих свободные списки .
- Invalid free — передача недействительного адреса в free может привести к повреждению кучи .
- Несоответствующее свободное пространство – когда используются несколько распределителей, попытка освободить память с помощью функции освобождения другого распределителя. [26]
- Нежелательное псевдонимирование — когда одна и та же ячейка памяти выделяется и изменяется дважды для несвязанных целей.
Некоторые списки могут также включать условия гонки (одновременное чтение/запись в разделяемую память) как часть безопасности памяти (например, для контроля доступа). Язык программирования Rust по умолчанию предотвращает многие виды гонок за память, поскольку он гарантирует наличие не более одного записывающего устройства или одного или нескольких читателей. Многие другие языки программирования, такие как Java, не предотвращают автоматически состояния гонки за память, но по-прежнему считаются «безопасными для памяти» языками. Поэтому противодействие условиям гонки обычно не считается необходимым для того, чтобы язык считался безопасным для памяти.
Ссылки
[ редактировать ]- ^ Jump up to: а б с д Дхурджати, Динакар; Ковшик, Сумант; Адве, Викрам; Латтнер, Крис (1 января 2003 г.). «Безопасность памяти без проверок во время выполнения и сборки мусора» (PDF) . Материалы конференции ACM SIGPLAN 2003 года по языку, компилятору и инструментам для встраиваемых систем . АКМ. стр. 69–80. дои : 10.1145/780732.780743 . ISBN 1581136471 . S2CID 1459540 . Проверено 13 марта 2017 г.
- ^ Кениг, Эндрю. «Как C затрудняет проверку границ массива» . Доктор Добб . Проверено 13 марта 2017 г.
- ^ Акритидис, Периклис (июнь 2011 г.). «Практическая безопасность памяти для C» (PDF) . Технический отчет – Кембриджский университет. Компьютерная лаборатория . Компьютерная лаборатория Кембриджского университета. ISSN 1476-2986 . UCAM-CL-TR-798 . Проверено 13 марта 2017 г.
- ^ Андерсон, Джеймс П. «Исследование планирования компьютерной безопасности» (PDF) . 2 . Центр электронных систем . ЭСД-ТР-73-51.
{{cite journal}}
: Для цитирования журнала требуется|journal=
( помощь ) - ^ Jump up to: а б ван дер Вин, Виктор; Датт-Шарма, Нитиш; Кавалларо, Лоренцо; Бос, Герберт (2012). «Ошибки памяти: прошлое, настоящее и будущее» (PDF) . Исследования в области атак, вторжений и защиты . Конспекты лекций по информатике. Том. 7462. стр. 86–106. дои : 10.1007/978-3-642-33338-5_5 . ISBN 978-3-642-33337-8 . Проверено 13 марта 2017 г.
- ^ Войчук, Рафаль. «Победа над неисполняемым патчем стека Solar Designer» . insecure.org . Проверено 13 марта 2017 г.
- ^ «Майкрософт: 70 процентов всех ошибок безопасности связаны с безопасностью памяти» . ЗДНЕТ . Проверено 21 сентября 2022 г.
- ^ «CVE-2014-0160» . Распространенные уязвимости и риски . Митра. Архивировано из оригинала 24 января 2018 года . Проверено 8 февраля 2018 г.
- ^ Гудин, Дэн (4 февраля 2020 г.). «Серьезная ошибка, которая скрывалась в sudo в течение 9 лет, лишает root-прав» . Арс Техника .
- ^ «Рыба в бочке» . fishinabarrel.github.io . Проверено 21 сентября 2022 г.
- ^ Jump up to: а б Крайтон, Уилл. «CS 242: Безопасность памяти» . stanford-cs242.github.io . Проверено 22 сентября 2022 г.
- ^ «Ссылки» . Рустономикон . Руст.орг . Проверено 13 марта 2017 г.
- ^ Бесси, Эл; Энглер, Доусон; Блок, Кен; Челф, Бен; Чоу, Энди; Фултон, Брайан; Халлем, Сет; Анри-Гро, Шарль; Камский, Ася; Макпик, Скотт (1 февраля 2010 г.). «Несколько миллиардов строк кода спустя». Коммуникации АКМ . 53 (2): 66–75. дои : 10.1145/1646353.1646374 . S2CID 2611544 .
- ^ Бергер, Эмери Д.; Зорн, Бенджамин Г. (1 января 2006 г.). «DieHard: Вероятностная безопасность памяти для небезопасных языков» (PDF) . Материалы 27-й конференции ACM SIGPLAN по проектированию и реализации языков программирования . АКМ. стр. 158–168. дои : 10.1145/1133981.1134000 . ISBN 1595933204 . S2CID 8984358 . Проверено 14 марта 2017 г.
- ^ Новарк, Джин; Бергер, Эмери Д. (1 января 2010 г.). «DieHarder: защита кучи» (PDF) . Материалы 17-й конференции ACM «Компьютерная и коммуникационная безопасность» . АКМ. стр. 573–584. дои : 10.1145/1866307.1866371 . ISBN 9781450302456 . S2CID 7880497 . Проверено 14 марта 2017 г.
- ^ «Отладка памяти в Allinea DDT» . Архивировано из оригинала 3 февраля 2015 г.
- ^ Джилленхол, Джон. «Использование инструмента Memcheck от Valgrind для поиска ошибок и утечек памяти» . Computing.llnl.gov . Архивировано из оригинала 7 ноября 2018 года . Проверено 13 марта 2017 г.
- ^ «Memcheck: детектор ошибок памяти» . Руководство пользователя Валгринд . valgrind.org . Проверено 13 марта 2017 г.
- ^ Крейнин, Йоси. «Почему пользовательские распределители/пулы сложны» . Правильная фиксация . Проверено 13 марта 2017 г.
- ^ «Использование сборщика мусора в качестве детектора утечек» . www.hboehm.info . Проверено 14 марта 2017 г.
- ^ «Семантический дизайн: CheckPointer по сравнению с другими инструментами проверки безопасности» . www.semanticdesigns.com . Семантический дизайн, Inc.
- ^ «Адрессанитайзерперформанснумберс» . Гитхаб .
- ^ Думбумрунг, Смит (2020). «BoundWarden: безопасность пространственной памяти, обеспечиваемая потоками, посредством преобразований во время компиляции». Наука компьютерного программирования . 198 : 102519. doi : 10.1016/j.scico.2020.102519 . S2CID 224925197 .
- ^ Гв, Навин. «Как избежать, найти (и исправить) ошибки памяти в коде C/C++» . Cprogramming.com . Проверено 13 марта 2017 г.
- ^ «CWE-633: Слабые стороны, влияющие на память» . Перечень слабостей сообщества . МИТРА . Проверено 13 марта 2017 г.
- ^ «CWE-762: Несовпадающие процедуры управления памятью» . Перечень слабостей сообщества . МИТРА . Проверено 13 марта 2017 г.