Топ питань
Часова шкала
Чат
Перспективи
Безпека доступу до пам'яті
З Вікіпедії, вільної енциклопедії
Remove ads
Безпека доступу до пам'яті (англ. Memory safety) — концепція в розробці програмного забезпечення, метою якої є запобігти виникненню програмних помилок, що призводять до вразливостей пов'язаних з доступом до оперативної пам'яті комп'ютера, таким як переповнення буфера, завислі вказівники тощо.
Мови програмування з низьким рівнем абстракції, такі як C чи C++, що надають безпосередній доступ до пам'яті комп'ютера (довільна арифметика вказівників, виділення чи вивільнення пам'яті) та приведення типів, але в той же час не мають автоматичної перевірки меж масивів[en], не є безпечними з точки зору доступу до пам'яті[1][2].
Remove ads
Вразливості, пов'язані з доступом до пам'яті
Узагальнити
Перспектива
Одним із найрозповсюдженіших типів вразливостей програмного забезпечення є саме проблеми безпеки доступу до пам'яті[3][4]. Даний тип вразливостей відомий протягом понад 30 років[5]. Безпеку доступу до пам'яті слід розуміти як запобігання спробам використати або модифікувати дані в тих випадках, де це не було спроектовано при створенні програмного продукту[6].
Більшість критичних за продуктивністю програм створюють мовами програмування з низьким рівнем абстракції (C та C++), чим обумовлено виникнення вразливостей даного типу. Відсутність захищеності цих мов програмування дозволяє атакуючій стороні отримати повний контроль над програмою, змінювати потік керування, мати несанкціонований доступ до конфіденційної інформації[7]. Існують різні варіанти рішення проблеми щодо доступу до пам'яті, механізми захисту повинні бути водночас ефективними як з точки зору безпеки, так і з точки зору продуктивності її виконання[8].
Перше історичне освітлення помилки пам'яті мало місце в 1972 році[9]. Відтоді й надалі вона була проблемою багатьох програмних продуктів, засобом, що дозволяє застосовувати експлойти. Наприклад, Хробак Морріса використовував численні вразливості, значна частина котрих була зв'язана саме з помилками роботи із пам'яттю[10].
Remove ads
Різновидності помилок пам'яті
Узагальнити
Перспектива
Розрізняють декілька видів помилок пам'яті (вразливостей), які можуть виникати в деяких мовах програмування:[11][12][13]
- Порушення меж масивів[en] (або вихід за межі масиву; англ. bounds checking) — спроба використання значень, що знаходяться поза допустимими межами. За звичай помилка виникає, якщо намагатись зберегти значення змінної поза межами яку не підтримує тип даних. Іншим відомим випадком порушення меж масивів є спроба звертатися до неіснуючої комірки масиву не перевіривши його межі[14]. Окремо виділяють помилку на одиницю[15], логічна помилка в алгоритмі, коли задана кількість ітерацій циклу виявляється на одиницю більше або менше необхідного, або ж виникає плутанина з початком відліку індексації масиву (у багатьох мовах вона починається з нуля а не з одиниці).
- Переповнення буфера (англ. buffer overflow) — запис за межами виділеного об'єму пам'яті буфера. Виникає при спробі запису в буфер блоку даних, що перевищує розмір цього буфера. В результаті переповнення інші дані що знаходяться поруч з буфером може бути пошкоджено[16], інтерпретація інформації як виконуючого коду може бути порушена[17]. Використання даної вразливості є однією з найбільш популярних способів злому комп'ютерних систем[18].
- Читання поза межами буфера[en] (англ. buffer over-read) — аномальне читання чи його спроба поза межами виділеного в пам'яті буфера. Наслідками можуть стати порушення безпеки системи (втрата конфіденційності), нестабільна та неправильна поведінка виконання програмного коду, помилки прав доступу до пам'яті[19]. Ця вразливість входить у список найбільш поширених та небезпечних помилок в програмному забезпеченні[20].
- Помилки при роботі з динамічною пам'яттю — неправильне використання динамічно виділяємої пам'яті та вказівниками. В даному випадку виділення пам'яті під об'єкти здійснюється під час виконання програми[21], що може спричинити помилки часу виконання[en] (англ. runtime system). До цієї вразливості схильні мови програмування з низьким рівнем абстракції, що підтримують безпосередній доступ до пам'яті комп'ютера (C, C++)[22].
- Завислий вказівник (або символ покажчик; англ. dangling pointer)[23] — вказівник, що не має посилання на допустимий об'єкт відповідного типу. Цей вид вказівників виникає в разі, коли об'єкт був видалений (або переміщений), але значення вказівника не було змінено на нульове. У цьому разі він все ще вказує на ділянку пам'яті, де знаходився цей об'єкт і може стати причиною отримання конфіденційної інформації зловмисником. Також можливий випадок коли система вже перерозподілила адресну пам'ять під інший об'єкт, а доступ через завислий вказівник може зіпсувати розташовані там дані[24]. Особливий підтип помилки — використання після вивільнення (англ. use after free) (звернення до вже вивільненої області пам'яті) — є найбільш поширеною причиною помилок програм[25], наприклад вразливостей інтернет браузерів[26].
- Звернення за нульовим вказівником (англ. null pointer) — так як нульовий вказівник має спеціальне зарезервоване значення, що повідомляє що даний вказівник не посилається на допустимий об'єкт[27], звернення за нульовим вказівником стане причиною обробки винятків[28] і призведе до аварійної зупинки програми.
- Вивільнення завчасно не виділеної пам'яті — спроба вивільнити область оперативної пам'яті, яка не є виділеною (тобто на даний момент вільна). Найбільш часто це проявляться у випадку подвійного вивільнення пам'яті[29], коли виникає повторна спроба вивільнити вже вивільнену пам'ять. Дана дія може спричинити помилку керування пам'яттю в менеджері пам'яті[30]. Наприклад в мові програмування C це виникає при повторному виклику функції
free
з одним і тим же вказівником, де другий виклик намагається вивільнити не виділену пам'ять.
- Вивільнення завчасно не виділеної пам'яті — спроба вивільнити область оперативної пам'яті, яка не є виділеною (тобто на даний момент вільна). Найбільш часто це проявляться у випадку подвійного вивільнення пам'яті[29], коли виникає повторна спроба вивільнити вже вивільнену пам'ять. Дана дія може спричинити помилку керування пам'яттю в менеджері пам'яті[30]. Наприклад в мові програмування C це виникає при повторному виклику функції
- Використання різних менеджерів пам'яті — помилка полягає в розриві зв'язку аллокатор-деаллокатор пам'яті з використанням різних засобів для роботи з одним сегментом. Наприклад, в C++ використати
free
для ділянки пам'яті, виділеною за допомогоюnew
або ж, аналогічно, використатиdelete
після викликуmalloc
. Стандарт C++ не описує який-небудь зв'язок міжnew/delete
та функціями роботи з динамічною пам'яттю з мови C, хочаnew/delete
в загальному випадку і реалізовані через обгорткиmalloc/free
[31][32], та змішане використання може спричинити невизначену поведінку програми[33].
- Використання різних менеджерів пам'яті — помилка полягає в розриві зв'язку аллокатор-деаллокатор пам'яті з використанням різних засобів для роботи з одним сегментом. Наприклад, в C++ використати
- Втрата вказівника — втрата адреси виділеного фрагмента пам'яті під час перезапису його новим значення, що посилається на іншу ділянку пам'яті[34]. При цьому адресована попереднім вказівником пам'ять стає недосяжною. Такий тип помилки приводить до явища витоку пам'яті (англ. memory leak), так як виділена пам'ять більше не може бути вивільнена. В мові програмування C це може трапитися при повторному присвоюванні результату функції
malloc
одному і тому ж вказівнику, без проміжного вивільнення пам'яті.
- Втрата вказівника — втрата адреси виділеного фрагмента пам'яті під час перезапису його новим значення, що посилається на іншу ділянку пам'яті[34]. При цьому адресована попереднім вказівником пам'ять стає недосяжною. Такий тип помилки приводить до явища витоку пам'яті (англ. memory leak), так як виділена пам'ять більше не може бути вивільнена. В мові програмування C це може трапитися при повторному присвоюванні результату функції
- Неініціалізовані змінні[en] (англ. uninitialized variable) — змінні, що були об'явлені[en] без присвоєння значення. При спробі їх використання значення вони все ж матимуть, але, загалом, важко передбачуване (зчитується попередня, неперезаписана у ділянку пам'яті інформація). Вразливість для пам'яті може виникати за наявності неініціалізованих завислих («диких») вказівників[35]. Такі вказівники в своїй поведінці схожі з завислими вказівниками, спроба звернення до них у більшості випадків буде супроводжуватися помилками сегментації чи пошкодженням даних. Однак, можливе отримання конфіденційної інформації, тої що могла лишитися в даній області пам'яті після попереднього використання[36][37].
- Помилки нестачі пам'яті — проблеми, що виникають при нестачі кількості доступної пам'яті для даної програми.
- Переповнення стека (англ. stack overflow) — перевищення програмою кількості інформації, яка може знаходитися у стеку викликів (вказівник вершини стеку виходить за межі допустимої області). При цьому програма аварійно завершується[38]. Причиною помилки може бути глибока (або нескінченна) рекурсія, або виділення великої кількості пам'яті для локальних змінних у стеку[39].
- Переповнення купи[en] (англ. out of memory) — спроба програми виділити більшу кількість пам'яті, ніж їй доступно. Виникає внаслідок частого і, частіше всього, невірного користування динамічною пам'яттю[40]. У разі виникнення помилки, операційна система завершить найбільш умісний з її точки зору процес (той що викликав помилку, але інколи — довільний[41]).
Remove ads
Виявлення помилок
Можливі помилки роботи з пам'яттю можуть бути встановлені як під час компіляції програми, так і під час її виконання[en] (налагодження програми).
Окрім попереджень з боку компілятора, для виявлення помилок до моменту збірки програми[en] використовуються статичні аналізатори коду. Вони дозволяють покрити значну частину небезпечних ситуацій досліджуючи вихідний код більш детально, ніж поверхневий аналіз компілятора. Статичні аналізатори можуть виявити:[42][43][44][45]
- вихід за межі масивів;
- використання завислих (а також нульових або неініціалізованих) вказівників;
- неправильне використання бібліотечних функцій;
- витік пам'яті, як наслідок неправильної роботи з вказівниками.
Під час налагодження програми можуть використовуватися спеціальні менеджери пам'яті. У даному випадку навколо аллоційованих в купі об'єктів створюються «мертві» області пам'яті, потрапляючи в які стає можливим виявити помилки[46]. Альтернативою є спеціалізовані віртуальні машини, що перевіряють доступ до пам'яті (Valgrind). Виявити помилки допомагають системи інструментування[en] коду, в тому числі забезпечені компілятором (Sanitizer[47]).
Способи забезпечення безпеки
Узагальнити
Перспектива
Більшість мов програмування високого рівня забезпечують рішення таких проблем шляхом видалення з мови арифметики вказівників, обмеженням можливості приведення типів, а також введенням збирання сміття (англ. garbage collection) як єдиної схеми управління пам'яттю[48]. На відміну від низькорівневих мов, де важливою є швидкість виконання, високорівневі, загалом, здійснюють додаткові перевірки[49], наприклад меж при звертанні до масивів та об'єктів[50].
Щоб уникнути витоку пам'яті і ресурсів та забезпечити безпеку щодо винятків у сучасному C++ використовуються розумні вказівники. Зазвичай вони являють собою клас, що імітує інтерфейс звичайного вказівника, чим розширює його функціональність[51], наприклад перевірку меж масивів та об'єктів, автоматичне управління виділенням та вивільненням пам'яті для виконуваного об'єкта. Вони допомагають реалізувати ідіому «Отримання ресурсу є ініціалізація», що означає: отримання об'єкта неподільно зв'язано з його ініціалізацією, а вивільнення — із його знищенням[52].
При використанні бібліотечних функцій слід приділяти увагу значенням що з них повертаються[en], щоб виявити можливі порушення в їх роботі[53]. Функції для роботи з динамічною пам'яттю в мові C сигналізують про помилку (нестача вільної пам'яті запрошеного розміру), повертаючи замість вказівника на блок пам'яті нульовий вказівник[54]; в C++ використовується обробка винятків[55]. Правильна обробка даних ситуацій дозволяє уникнути неправильного (аварійного) завершення програми[56].
Підвищенню безпеки сприяє перевірка меж при використанні вказівників. Подібні перевірки додаються під час компіляції та можуть сповільнювати роботу програм; для їх пришвидшення були розроблені спеціальні апаратні додатки (наприклад Intel MPX[57]).
На нижніх рівнях абстракцій існують спеціальні системи, що забезпечують безпеку пам'яті. На рівні операційної системи цим займається менеджер віртуальної пам'яті[en], він розподіляє доступні області пам'яті для окремих процесів (підтримка багатозадачності), та засоби синхронізації для підтримання багатопоточності[58]. Апаратний рівень також, як правило, включає певні механізми, такі як кільця захисту[59].
Remove ads
Див. також
Примітки
Література
Посилання
Wikiwand - on
Seamless Wikipedia browsing. On steroids.
Remove ads