student@ubuntu:~$
Topic Week 5 3 min overview

Dynamic Memory

Stack vs heap, malloc and free, and the bug taxonomy you will meet for the rest of your career

In a nutshell

Every variable you have declared so far has been on the stack: a region of memory that lives as long as the enclosing function call and is reclaimed automatically when the function returns. The stack is fast but rigid: its size is known at compile time, and you cannot keep a stack variable alive past its function’s return. The heap is the alternative. You ask the OS for a block of memory with malloc, you get a pointer, the memory stays alive until you call free. Heap memory is how C programs implement anything whose size is not known at compile time: dynamic arrays that grow, buffers sized from user input, linked data structures.

The cost is that every allocation needs a matching free (leaks otherwise), every pointer you use must still be alive (use-after-free otherwise), and every buffer size must be tracked by you (overflow otherwise). Valgrind is the tool that catches the mistakes the compiler cannot.

Why it matters

Every non-trivial program allocates dynamically. Every CTF challenge with memory corruption involves heap mechanics. Every historic memory-safety CVE — Heartbleed, Baron Samedit, the WhatsApp GIF overflow — is a heap bug. Writing Valgrind-clean code from the start is the single professional-grade habit that distinguishes someone who uses C from someone who can be trusted with C.

Key takeaways

  • malloc(n) returns a void * to n bytes of uninitialized heap memory, or NULL on failure. Always check the return value.
  • calloc(n, sz) is malloc(n * sz) zero-initialized. Use when you need zeroed memory (and when you want the standard’s overflow check on the multiplication).
  • realloc(p, new_n) resizes an existing allocation. It may return a different pointer; always assign back: p = realloc(p, new_n);.
  • free(p) returns memory to the heap. Every malloc/calloc/realloc needs exactly one matching free. free(NULL) is safe and does nothing.
  • Stack lifetime ends at function return. Returning a pointer to a local variable from a function is a use-after-free waiting to happen.
  • The heap is slower than the stack. Allocation is a system-level operation. Prefer stack allocation when the size is known and the lifetime is bounded.
  • Dynamic arrays use the {data, size, capacity} pattern. size is how many elements are used; capacity is how many slots are allocated; grow by doubling when size == capacity.
  • Valgrind catches what the compiler cannot. Run valgrind --leak-check=full ./prog on every lab.

Bug taxonomy (the six you will meet)

  1. Memory leak. Forgot to free. Small ones compound in long-running programs.
  2. Double free. Called free twice on the same pointer. Can crash or (with a heap spray) give an attacker control of the allocator.
  3. Use-after-free. Dereferenced a pointer after calling free on it. Classic browser CVE pattern.
  4. Buffer overflow. Wrote past the end of a heap allocation. Adjacent heap metadata or objects corrupt.
  5. Uninitialized read. Read bytes from a fresh malloc block before writing them. Leaks stack contents (see: Heartbleed-style bugs).
  6. Out-of-bounds index. Read or wrote arr[i] where i < 0 or i >= capacity. Silently corrupts adjacent memory.

Lessons in this topic

Lesson What it covers
Stack vs Heap & Memory Layout The four regions (text, data, bss, stack, heap) and how the OS lays them out
malloc, free, calloc & realloc The four allocation primitives, the return-value check, the one-free-per-alloc rule
Valgrind & Dynamic Arrays Running Valgrind, reading its output, the {data, size, capacity} growable-array pattern

Practice and deep dives

Practice this topic: Dynamic Memory or Memory Layout drills, or browse the practice gallery.

For the OS-level picture (stack frames, what malloc asks the kernel for, how the heap grows), see the machine model deep dive. For concrete exploit walk-throughs of CWE-120, heap buffer overflow, and the Baron Samedit CVE, see the memory-safety deep dive.

What comes next

Files — another kind of handle (FILE *) that you open, read or write through, and close. The mental model is the same as heap memory: you ask for a resource, you use it, you return it.