EMZETT.
Увійти

Переповнення буфера та безпека пам’яті: чому програми на 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: володіння та запозичення