Безпека доступу до пам'яті () — концепція в розробці програмного забезпечення, метою якої є запобігти виникненню програмних помилок, що призводять до вразливостей пов'язаних з доступом до оперативної пам'яті комп'ютера, таким як переповнення буфера, завислі вказівники тощо.
Мови програмування з низьким рівнем абстракції, такі як C чи C++, що надають безпосередній доступ до пам'яті комп'ютера (довільна арифметика вказівників, виділення чи вивільнення пам'яті) та приведення типів, але в той же час не мають автоматичної перевірки меж масивів, не є безпечними з точки зору доступу до пам'яті / «Language features that break memory safety include …» / «Memory corruption bugs in software written in low-level languages like C or C++ are one of the oldest problems in computer security.».
Вразливості, пов'язані з доступом до пам'яті
Одним із найрозповсюдженіших типів вразливостей програмного забезпечення є саме проблеми безпеки доступу до пам'яті / «… and still rank among the top 3 most dangerous software errors.» / «In fact, after configuration errors, implementation errors are probably the largest single class of security errors exploited in practice.». Даний тип вразливостей відомий протягом понад 30 років / «This problem has existed for more than 30 years …». Безпеку доступу до пам'яті слід розуміти як запобігання спробам використати або модифікувати дані в тих випадках, де це не було спроектовано при створенні програмного продукту / «… preventing attackers from reading or writing to memory locations other than those intended by the programmer.».
Більшість критичних за продуктивністю програм створюють мовами програмування з низьким рівнем абстракції (C та C++), чим обумовлено виникнення вразливостей даного типу. Відсутність захищеності цих мов програмування дозволяє атакуючій стороні отримати повний контроль над програмою, змінювати потік керування, мати несанкціонований доступ до конфіденційної інформації / «Applications written in low-level languages like C or C++ are prone to these kinds of bugs. The lack of memory safety … enables attackers to exploit memory bugs by maliciously altering the program's behavior or even taking full control over the control-flow.». Існують різні варіанти рішення проблеми щодо доступу до пам'яті, механізми захисту повинні бути водночас ефективними як з точки зору безпеки, так і з точки зору продуктивності її виконання / «… in finding the balance betweeneffectiveness(security)andefficiency.».
Перше історичне освітлення помилки пам'яті мало місце в 1972 році / «Memory errors were first publicly discussed in 1972 by the Computer Security Technology Planning Study Panel.». Відтоді й надалі вона була проблемою багатьох програмних продуктів, засобом, що дозволяє застосовувати експлойти. Наприклад, Хробак Морріса використовував численні вразливості, значна частина котрих була зв'язана саме з помилками роботи із пам'яттю / «The Internet Worm exploited a number of vulnerabilities, including memory error-related ones.».
Різновидності помилок пам'яті
Розрізняють декілька видів помилок пам'яті (вразливостей), які можуть виникати в деяких мовах програмування:
Порушення меж масивів (або вихід за межі масиву; ) — спроба використання значень, що знаходяться поза допустимими межами. За звичай помилка виникає, якщо намагатись зберегти значення змінної поза межами яку не підтримує тип даних. Іншим відомим випадком порушення меж масивів є спроба звертатися до неіснуючої комірки масиву не перевіривши його межі / «One response to this analysis is to discard C, since this lack of efficient checkability is responsible for many software failures.». Окремо виділяють помилку на одиницю / «… the use of the other three conventions has been a constant source of clumsiness and mistakes …», логічна помилка в алгоритмі, коли задана кількість ітерацій циклу виявляється на одиницю більше або менше необхідного, або ж виникає плутанина з початком відліку індексації масиву (у багатьох мовах вона починається з нуля а не з одиниці).
Переповнення буфера () — запис за межами виділеного об'єму пам'яті буфера. Виникає при спробі запису в буфер блоку даних, що перевищує розмір цього буфера. В результаті переповнення інші дані що знаходяться поруч з буфером може бути пошкоджено, інтерпретація інформації як виконуючого коду може бути порушена. Використання даної вразливості є однією з найбільш популярних способів злому комп'ютерних систем / «Buffer overflows are an extremely common and dangerous security flaw …».
() — аномальне читання чи його спроба поза межами виділеного в пам'яті буфера. Наслідками можуть стати порушення безпеки системи (втрата конфіденційності), нестабільна та неправильна поведінка виконання програмного коду, помилки прав доступу до пам'яті / «This typically occurs when the pointer or its index is incremented to a position beyond the bounds of the buffer …». Ця вразливість входить у список найбільш поширених та небезпечних помилок в програмному забезпеченні.
Помилки при роботі з динамічною пам'яттю — неправильне використання динамічно виділяємої пам'яті та вказівниками. В даному випадку виділення пам'яті під об'єкти здійснюється під час виконання програми / «The runtime environment defines not only how memory is allocated and freed …», що може спричинити помилки часу виконання (). До цієї вразливості схильні мови програмування з низьким рівнем абстракції, що підтримують безпосередній доступ до пам'яті комп'ютера (C, C++).
Завислий вказівник (або символ покажчик; ) — вказівник, що не має посилання на допустимий об'єкт відповідного типу. Цей вид вказівників виникає в разі, коли об'єкт був видалений (або переміщений), але значення вказівника не було змінено на нульове. У цьому разі він все ще вказує на ділянку пам'яті, де знаходився цей об'єкт і може стати причиною отримання конфіденційної інформації зловмисником. Також можливий випадок коли система вже перерозподілила адресну пам'ять під інший об'єкт, а доступ через завислий вказівник може зіпсувати розташовані там дані / «… уязвимости, к которым может привести неправильное использование указателей и ссылок.». Особливий підтип помилки — використання після вивільнення () (звернення до вже вивільненої області пам'яті) — є найбільш поширеною причиною помилок програм / «Referencing memory after it has been freed can cause a program to crash, use unexpected values, or execute code.», наприклад вразливостей інтернет браузерів / «Use-after-free vulnerabilities are rapidly growing in popularity, especially for exploiting web browsers.».
Звернення за нульовим вказівником () — так як нульовий вказівник має спеціальне зарезервоване значення, що повідомляє що даний вказівник не посилається на допустимий об'єкт / «The language definition states that for each pointer type, there is a special value …», звернення за нульовим вказівником стане причиною обробки винятків / «Thrown when an application attempts to use null in a case where an object is required.» і призведе до аварійної зупинки програми.
Вивільнення завчасно не виділеної пам'яті — спроба вивільнити область оперативної пам'яті, яка не є виділеною (тобто на даний момент вільна). Найбільш часто це проявляться у випадку подвійного вивільнення пам'яті / «When a program calls free() twice with the same argument …», коли виникає повторна спроба вивільнити вже вивільнену пам'ять. Дана дія може спричинити помилку керування пам'яттю в менеджері пам'яті / «If free(p) has already been called before, undefined behavior occurs.». Наприклад в мові програмування C це виникає при повторному виклику функції free з одним і тим же вказівником, де другий виклик намагається вивільнити не виділену пам'ять.
Використання різних менеджерів пам'яті — помилка полягає в розриві зв'язку аллокатор-деаллокатор пам'яті з використанням різних засобів для роботи з одним сегментом. Наприклад, в C++ використати free для ділянки пам'яті, виділеною за допомогою new або ж, аналогічно, використати delete після виклику malloc. Стандарт C++ не описує який-небудь зв'язок між new/delete та функціями роботи з динамічною пам'яттю з мови C, хоча new/delete в загальному випадку і реалізовані через обгортки malloc/free / «… it is usually implemented as a thin wrapper around the C heap allocator …» / «For example, the GNU C++ compiler's new operator actually invokes the C runtime malloc() function.», та змішане використання може спричинити невизначену поведінку програми / «The C++ operators new and delete guarantee proper construction and destruction … The C-style functions … don't ensure that.».
Втрата вказівника — втрата адреси виділеного фрагмента пам'яті під час перезапису його новим значення, що посилається на іншу ділянку пам'яті. При цьому адресована попереднім вказівником пам'ять стає недосяжною. Такий тип помилки приводить до явища витоку пам'яті (), так як виділена пам'ять більше не може бути вивільнена. В мові програмування C це може трапитися при повторному присвоюванні результату функції malloc одному і тому ж вказівнику, без проміжного вивільнення пам'яті.
() — змінні, що були оголошені без присвоєння значення. При спробі їх використання значення вони все ж матимуть, але, загалом, важко передбачуване (зчитується попередня, неперезаписана у ділянку пам'яті інформація). Вразливість для пам'яті може виникати за наявності неініціалізованих завислих («диких») вказівників / «Ничто так не беспокоит, как „дикие“ указатели!». Такі вказівники в своїй поведінці схожі з завислими вказівниками, спроба звернення до них у більшості випадків буде супроводжуватися помилками сегментації чи пошкодженням даних. Однак, можливе отримання конфіденційної інформації, тої що могла лишитися в даній області пам'яті після попереднього використання / «We're looking at the following situation then …» / «An attacker can sometimes control or read these contents.».
Помилки нестачі пам'яті — проблеми, що виникають при нестачі кількості доступної пам'яті для даної програми.
Переповнення стека () — перевищення програмою кількості інформації, яка може знаходитися у стеку викликів (вказівник вершини стеку виходить за межі допустимої області). При цьому програма аварійно завершується. Причиною помилки може бути глибока (або нескінченна) рекурсія, або виділення великої кількості пам'яті для локальних змінних у стеку / «The two most common causes for a stack overflow …».
() — спроба програми виділити більшу кількість пам'яті, ніж їй доступно. Виникає внаслідок частого і, частіше всього, невірного користування динамічною пам'яттю / «An „out of memory“ error can be catastrophic for a program, especially one written in a language such as Java that uses memory allocation frequently.». У разі виникнення помилки, операційна система завершить найбільш умісний з її точки зору процес (той що викликав помилку, але інколи — довільний / «… you can no longer allocate more memory and the kernel kills a task (usually the current running one).»).
Виявлення помилок
Можливі помилки роботи з пам'яттю можуть бути встановлені як під час компіляції програми, так і під час її виконання (налагодження програми).
Окрім попереджень з боку компілятора, для виявлення помилок до моменту використовуються статичні аналізатори коду. Вони дозволяють покрити значну частину небезпечних ситуацій досліджуючи вихідний код більш детально, ніж поверхневий аналіз компілятора. Статичні аналізатори можуть виявити: / «Detect various kinds of bugs in your code …» / «Programs with pointers can commit a variety of errors in accessing memory …»
вихід за межі масивів; використання завислих (а також нульових або неініціалізованих) вказівників; неправильне використання бібліотечних функцій; витік пам'яті, як наслідок неправильної роботи з вказівниками.