The previous lectures show how memory corruption gives attackers code execution. Mitigations are the runtime, compiler, and OS countermeasures that turn each exploitation step into a research problem. They rarely fix the underlying bug — they make the bug harder to weaponize.
The pattern is always the same: an exploit needs a reliable address, a writable location for shellcode, or an unprotected control-flow edge. Each mitigation removes one of those affordances. Attackers respond with new techniques (ROP defeated DEP, infoleaks defeated ASLR), and defenses evolve to close the new gaps. This is the arms race, and it never settles.
Stack Canaries
A random "canary" value is placed between local buffers and the saved return address at every function prologue. The epilogue checks it. If a buffer overflow rewrites the return address, it also rewrote the canary — the check fails and the process aborts before the corrupted return is used.
DEP / NX / W^X
Memory pages are marked either writable or executable, never both. Stack, heap, and data segments lose their exec bit; only the code segment keeps it. Shellcode written into a buffer can no longer be jumped to — the CPU faults on instruction fetch from a non-executable page.
ASLR / PIE
Address Space Layout Randomization picks a different base address every time a program loads its stack, heap, libraries, and (with PIE) the main binary. Hard-coded addresses in shellcode — the address of system(), of a buffer, of a ROP gadget — no longer work. The exploit must first leak an address.
Control-Flow Integrity (CFI)
Indirect calls and returns are restricted to a set of legitimate targets determined at compile time. Even with full memory corruption, the CPU refuses to call a function whose signature doesn't match the call site, or to return to an address that isn't on a verified list.
Heap Hardening
Modern allocators reject double-frees, verify metadata, separate metadata from user data, and place freed blocks on a quarantine before reuse. Hardened allocators (PartitionAlloc, jemalloc, mimalloc, scudo) make heap exploitation noticeably harder.
Memory-Safe Languages
The most effective mitigation is to remove the bug class entirely. Rust, Swift, Go, C#, Java, Python, and JavaScript provide memory safety as a language guarantee: no use-after-free, no buffer overflow, no double-free, no uninitialized read. Microsoft, Google, and the NSA have all publicly recommended migration from C/C++ for new security-sensitive code.
| Exploit Technique | Canary | DEP/NX | ASLR/PIE | CFI | Safe Lang |
|---|---|---|---|---|---|
| Inject + jump shellcode | stops | stops | partial | stops | stops |
| ret2libc / ROP | partial | no | stops (without leak) | stops | stops |
| Use-after-free + vtable | no | no | partial | stops | stops |
| Format-string read (%s) | no | no | no | no | stops |
| Format-string write (%n) | partial | no | partial | stops | stops |
| Race / TOCTOU | no | no | no | no | partial |
| Data-only attacks | no | no | no | no | stops |