EMZETT.
Login

Buffer overflow and memory safety: why C programs are vulnerable

In short: A buffer overflow occurs when a program writes more data into a memory area than it holds; neighbouring data or return addresses can then be overwritten. Modern protections and memory-safe languages slow this down.

In more detail: In languages such as C/C++ nobody automatically checks array bounds. If code writes beyond the end, it overwrites neighbouring data: this is the root of many serious vulnerabilities (Morris worm 1988, Heartbleed 2014 as an over-read). The article explains principle and protection without attack instructions.

In Depth

The principle

A program reserves e.g. 8 bytes for a name (char name[8]) on the stack. If it copies more characters unchecked (strcpy, gets), the data runs into neighbouring memory: other variables, the saved return address or function pointers. An attacker can thus redirect program flow. Related: heap overflow, use-after-free (memory reused after being freed), double free, off-by-one, format-string bugs, integer overflow (leads to a too small buffer), out-of-bounds read (Heartbleed).

Protection mechanisms

  • Stack canaries: guard value before the return address, checked before returning.
  • ASLR (random memory addresses), PIE.
  • NX/DEP (data areas not executable), W^X.
  • RELRO, CFI/shadow stack (control-flow integrity), Intel CET, ARM PAC/MTE.
  • Compiler warnings and protection (-Wall -Wextra, -fstack-protector-strong, -D_FORTIFY_SOURCE=2).
  • Sanitizers (AddressSanitizer, UBSan) and Valgrind find errors during testing, fuzzing (AFL++, libFuzzer) provokes them. No single protection is perfect; attackers combine techniques (ROP). Best strategy: avoid the bugs.

Programming more safely

  • Safe functions: fgets instead of gets, snprintf/strlcpy instead of sprintf/strcpy, always check lengths.
  • C++: std::string, std::vector, std::span, .at(), smart pointers instead of new/delete (RAII).
  • Validate inputs, check sizes, mind integer overflows.
  • Memory-safe languages: Rust (ownership, borrow checker), Go, Java, C#, Python, Swift: array bounds are checked, memory managed (but unsafe blocks/native extensions remain a risk).
  • Policy: authorities (BSI, CISA) recommend memory-safe languages for new software; Linux kernel, Windows, Android introduce Rust.
  • Code reviews, static analysis (clang-tidy, Coverity, CodeQL).

Classification and learning

  • Roughly 60–70 % of severe vulnerabilities in large C/C++ code bases (Microsoft, Chromium) were memory errors.
  • Install updates: many attacks use known flaws in old programs.
  • Learning (legal): examine your own small C programs with ASan/Valgrind, CTF tasks (pwn) on practice platforms, lecture material (stack/heap, assembler).
  • Heartbleed as a case study: missing length check → server memory readable.
  • Exam: name stack layout, causes, countermeasures.
  • Related: segmentation fault, memory leak, stack and heap, Valgrind.

See also: Segmentation fault, Stack and heap, Valgrind and sanitizers, Rust: ownership and borrowing