Β§14.4–14.7Common Errors … Summary

Part I OSTEP pp. 134–138 Β· ~6 min read

  • segmentation fault
  • buffer overflow
  • uninitialized read
  • memory leak
  • dangling pointer
  • double free
  • garbage collector
  • mmap
  • calloc
  • realloc

Every specimen below compiles without a peep. That’s the lesson hiding under the lesson: compiling a C program is necessary for correctness, and nowhere near sufficient. (Memory management is such a reliable source of pain that newer languages hired a garbage collector to free unreferenced memory automatically β€” though, as specimen #4 shows, even that isn’t a full pardon.)

1 β€” Forgetting to allocate

char *src = "hello";
char *dst;              // oops! unallocated
strcpy(dst, src);       // segfault and die

strcpy expects dst to point at real memory; it points at garbage. The likely outcome is a segmentation fault :

Segmentation fault (n.) β€” a fancy term for YOU DID SOMETHING WRONG WITH MEMORY YOU FOOLISH PROGRAMMER AND I AM ANGRY. (Why β€œsegmentation”? Chapter 16 explains β€” if that isn’t incentive to read on, what is?)

The fix: malloc(strlen(src) + 1) for dst first β€” or just use strdup() and make your life easier.

2 β€” Not allocating enough: the buffer overflow

char *src = "hello";
char *dst = (char *) malloc(strlen(src)); // too small!
strcpy(dst, src);       // "works" …

One byte short (the terminator). This buffer overflow will often run seemingly correctly: maybe the overwritten byte belonged to nothing important, maybe malloc padded the allocation anyway. Sometimes it tramples a live variable; sometimes it crashes; and this exact species of bug is the source of many security vulnerabilities. The lesson, again: ran once β‰  correct.

3 β€” Forgetting to initialize

malloc hands you bytes of unknown value. Read them before writing β€” an uninitialized read β€” and you get whatever was there: zero if you’re lucky, something random and harmful if you’re not.

4 β€” Forgetting to free: the memory leak

The memory leak β€” for a long-running program (a web server, a database, the OS itself) slow leakage ends in exhaustion and restart. GC languages don’t save you if you still hold a reference: no collector frees reachable memory.

Aside: Why no memory is leaked once your process exits

There are two levels of memory management. Level one: the OS hands memory to processes and takes all of it back at exit β€” code, stack, and heap pages alike, whatever state your allocator was in. Level two: the malloc library managing your heap within the process. So a short-lived program that never calls free() β€œworks” β€” nothing is truly lost. Bad habit anyway, says the book: aim to free every byte you allocate. And spare a thought for the one program whose leaks nobody cleans up at exit… the kernel. Those folks have the toughest job of all.

5 β€” Freeing before you’re done: the dangling pointer

Free memory, keep using the old pointer β€” a dangling pointer . The next malloc may recycle that region; your subsequent use crashes the program or silently overwrites someone else’s valid data.

6 β€” Freeing repeatedly: the double free

free(x);
...
free(x);  // undefined behavior

The double free β€” the allocator’s bookkeeping gets scrambled; crashes are the common outcome. (You met one in the wild already: chapter 10’s List_Pop race manufactured a double free with two threads.)

7 β€” Calling free() incorrectly

free() accepts exactly one thing: a pointer previously returned by malloc(). Pass anything else β€” a stack address, a pointer into the middle of an allocation β€” and the invalid free does bad things, reliably.

Tip: It compiled (or ran) β‰  it is correct

Many events can conspire to make broken code appear to work β€” until something changes and it stops. The classic student response, β€œbut it worked before!”, is usually followed by blaming the compiler, the OS, the hardware, or (dare we say) the professor. The problem is almost always where you least want it: in your code. Debug before you blame.

Because these errors are so common, an ecosystem of tools grew to hunt them: purify and valgrind are excellent, and once you’ve used them you’ll wonder how you survived without them (the homework makes sure you find out).

14.5 Underlying OS Support

Notice: no system calls were discussed above. That’s because malloc() and free() are library calls β€” the malloc library manages space inside your virtual address space, itself built on real system calls:

your program: malloc() / free() / calloc() / realloc()library calls β€” carving up heap space the process already ownsbrk / sbrkmove the β€œbreak” β€” the end of the heapmmap()anonymous, swap-backed regions (ch. 21)⚠ never call brk/sbrk yourself β€” that’s the library’s plumbing; stick to malloc() and free()

Two levels again: the library asks the OS for wholesale memory via brk/sbrk (or mmap() ), then retails it to you one malloc at a time.

14.6 Other Calls

calloc()

Allocates and zeroes the memory before returning β€” a vaccine against specimen #3, the uninitialized read.

realloc()

Grows an existing allocation: makes a larger region, copies the old contents in, returns the new pointer β€” the workhorse behind growable arrays.

14.7 Summary

The basics of the memory-allocation API, plus its seven deadly sins. For depth: the C book (K&R) and Stevens β€” and for how these problems can be found and even corrected automatically, the Exterminator paper. The allocator’s own inner workings β€” how the library actually manages that heap β€” get a whole chapter soon (free-space management territory, chapter 17).

Homework (code): break things, then meet valgrind

Write the bugs on purpose: a NULL dereference (watch it die, then watch it under gdb, then under valgrind β€”leak-check=yes); a leak; data[100] on a 100-int array (does it even crash? is it correct?); a use-after-free; a free of mid-array. Finish with a realloc-grown vector and let valgrind referee. The point of it all: knowing your tools is what makes C survivable. Details at ostep-homework.

Check yourself

1.The buffer-overflow example (malloc(strlen(src)) then strcpy) often runs 'fine.' Why is that the scary part?

2.Why doesn't a garbage collector fully solve memory leaks?

3.A short-lived program exits without ever calling free(). What actually happens to its heap?

4.What makes a dangling pointer dangerous even when the program doesn't crash?

5.Why should you NEVER call brk or sbrk yourself?

6.Which specimen from the gallery does calloc() specifically inoculate against?

6 questions