Переповнення буфера та безпека пам’яті: чому програми на C вразливі
Коротко: Переповнення буфера виникає, коли програма записує в область пам’яті більше даних, ніж вона вміщує; сусідні дані або адреси повернення можуть бути перезаписані. Сучасні захисти та memory-safe мови сповільнюють це.
Докладніше: У мовах на кшталт C/C++ межі масивів ніхто автоматично не перевіряє. Якщо код пише за кінець, він перезаписує сусідні дані: це корінь багатьох серйозних вразливостей (хробак Морріса 1988, Heartbleed 2014 як надмірне читання). Стаття пояснює принцип і захист без інструкцій з атаки.
Подробиці
Принцип
Програма резервує, наприклад, 8 байт під ім’я (char name[8]) у стеку. Якщо вона копіює більше символів без перевірки (strcpy, gets), дані заходять у сусідню пам’ять: інші змінні, збережену адресу повернення або вказівники на функції. Так зловмисник може перенаправити хід програми. Споріднене: переповнення купи, use-after-free (використання пам’яті після звільнення), double free, off-by-one, помилки форматного рядка, цілочисельне переповнення (дає надто малий буфер), читання за межами (Heartbleed).
Механізми захисту
- Stack canary: контрольне значення перед адресою повернення, перевіряється перед поверненням.
- ASLR (випадкові адреси пам’яті), PIE.
- NX/DEP (області даних не виконуються), W^X.
- RELRO, CFI/shadow stack (цілісність потоку керування), Intel CET, ARM PAC/MTE.
- Попередження та захист компілятора (
-Wall -Wextra,-fstack-protector-strong,-D_FORTIFY_SOURCE=2). - Санітайзери (AddressSanitizer, UBSan) і Valgrind знаходять помилки під час тестування, фаззинг (AFL++, libFuzzer) їх провокує. Жоден захист не ідеальний; зловмисники комбінують техніки (ROP). Найкраща стратегія — уникати помилок.
Безпечніше програмування
- Безпечні функції:
fgetsзамістьgets,snprintf/strlcpyзамістьsprintf/strcpy, довжини перевіряти завжди. - C++:
std::string,std::vector,std::span,.at(), розумні вказівники замістьnew/delete(RAII). - Перевіряти введення, розміри, стежити за цілочисельним переповненням.
- Memory-safe мови: Rust (володіння, borrow checker), Go, Java, C#, Python, Swift: межі масивів перевіряються, пам’ять керується (але блоки
unsafe/нативні розширення лишаються ризиком). - Політика: відомства (BSI, CISA) рекомендують memory-safe мови для нового ПЗ; ядро Linux, Windows, Android впроваджують Rust.
- Рев’ю коду, статичний аналіз (clang-tidy, Coverity, CodeQL).
Класифікація й навчання
- Приблизно 60–70 % серйозних вразливостей у великих кодових базах C/C++ (Microsoft, Chromium) були помилками пам’яті.
- Встановлюйте оновлення: багато атак використовують відомі помилки в старих програмах.
- Навчання (легально): досліджуйте власні невеликі програми на C з ASan/Valgrind, CTF-завдання (pwn) на навчальних платформах, лекційні матеріали (стек/купа, асемблер).
- Heartbleed як приклад: відсутня перевірка довжини → пам’ять сервера читається.
- Іспит: назвати будову стека, причини, протидію.
- Споріднене: segmentation fault, витік пам’яті, стек і купа, Valgrind.
Див. також: Segmentation fault, Стек і купа, Valgrind і санітайзери, Rust: володіння та запозичення