Secure Coding in C · intermediate · ~12 min

Buffer overflow basics

Understand what a buffer overflow is, with a toy demo.

Overview

A buffer overflow happens when you write past the end of a buffer. Stack-based overflows can overwrite the saved return address and let attackers redirect execution to code of their choosing. Heap-based overflows can corrupt allocator metadata, also leading to arbitrary code execution.

Why it matters

Buffer overflows are the foundational memory-safety bug — Morris worm (1988), Code Red (2001), Heartbleed (2014), and dozens of recent kernel CVEs all derive from one. Understanding how they work makes it visceral why the defensive patterns in this course matter.

Core concepts

Stack layout. Local variables sit below the saved return address; writing past a stack buffer overwrites it. Return-address overwrite. Classic exploit goal: replace the return address with the address of your own code or a gadget. Defences. Stack canaries (compiler inserts a random word that must remain intact); ASLR (the stack moves around so the attacker can't predict addresses); non-executable stack (NX bit on data pages); FORTIFY_SOURCE (compile-time bounds checking).

Syntax notes

void bad(const char *src) {
    char buf[16];
    strcpy(buf, src);   // unsafe: no size limit
}
void good(const char *src) {
    char buf[16];
    snprintf(buf, sizeof buf, "%s", src);  // bounded
}

Lesson

A buffer overflow happens when a program writes past the end of an allocated buffer. The stray bytes overwrite other things — adjacent variables, return addresses, function pointers. Historically this gave attackers code execution.

This course only demonstrates overflows in toy programs you compile and run yourself. Never test memory-corruption techniques against real software you don't own. Modern toolchains add many mitigations (stack canaries, ASLR, NX); they reduce but don't eliminate the underlying class of bug.

Code examples

// Toy program that overflows a 16-byte buffer.
char buf[16];
strcpy(buf, "this string is too long for the buffer");
// adjacent stack memory now corrupted

Common mistakes

  • Treating compiler warnings as cosmetic. A stack-smashing detector message means stop and fix it.

Debugging tips

Compile with -fstack-protector-strong -D_FORTIFY_SOURCE=2 -fsanitize=address to catch overflows at runtime. AddressSanitizer prints the offending line and a backtrace. Modern linkers default to -z noexecstack, making classic return-to-stack exploits much harder.

Memory safety

The single biggest mitigation: never use gets, strcpy, strcat, sprintf, or scanf("%s"). Every replacement (fgets with size, snprintf, strlcpy) takes a size — that size is your safety net. Pair with the input-validation lesson and overflows become near-impossible.

Real-world uses

Defensive: stack canaries in every modern compiler. Offensive (for understanding only): Capture-The-Flag (CTF) challenges that use small, intentionally vulnerable binaries. Never run on real services without explicit permission.

Practice tasks

  1. Write a vulnerable program that overflows a 16-byte buffer with a 32-byte strcpy, then fix it with strncpy/snprintf. 2. Run both versions under ASan and compare error messages. 3. Read about a famous overflow CVE and write a one-paragraph summary.

Summary

A buffer overflow is writing past a buffer's end. The classic attack overwrites a return address; the classic defence is bounded writes + stack canaries + NX + ASLR + FORTIFY_SOURCE. Use bounded functions everywhere, enable the compiler defences, and the problem largely goes away.

Practice with these exercises