The crime scene: a side channel that should have been harmless
The previous guide left us with an uncomfortable fact: a cache leaks information through timing, because a hit is fast and a miss is slow, and an attacker who can measure that gap can learn which addresses someone else recently touched. On its own that sounds survivable — surely the processor never touches memory it is forbidden to read. Spectre and Meltdown, disclosed together in January 2018, are the brutal demonstration that this assumption is false. They take two of the speed tricks you met climbing this ladder — the ones that make a modern CPU fast — and turn them into a pump that pushes forbidden data into the cache-timing side channel.
Recall the key trick from earlier rungs. To stay busy, a CPU does not wait politely for each instruction to finish; it runs work ahead of where it is officially sure it should be. With out-of-order execution it starts whichever operations have their inputs ready, like a kitchen starting any order whose ingredients have arrived — yet it still plates dishes in the order taken, because results are only made official by in-order commit. And with speculative execution it goes further: at a branch it bets on which fork the road takes, using the branch predictor, and races down that path before the condition is even resolved. If the bet was wrong, it throws the speculative work away. The promise has always been that this discarded work is as if it never happened. The attacks prove the promise is broken — discarded work leaves a footprint.
Meltdown: speculation runs past the privilege fence
Meltdown is the simpler and the scarier of the two, because it melts down the wall between a user program and the operating-system kernel. Recall from the protection guide that the CPU runs at different privilege levels: your application sits in user mode and cannot read kernel memory, and any attempt should raise a fault. On the affected processors, the kernel's pages were mapped into every process's address space for speed, marked merely as 'no access from user mode'. The catch: the permission check and the actual data fetch happened in parallel, and a load could speculatively read the byte and hand it onward a few cycles before the fault was delivered to retire it.
Here is the trick that exfiltrates the byte before the rollback erases it. The attacker prepares a probe array of 256 cache lines, one per possible byte value, and flushes them all out of the cache. Then, in the doomed speculative window, the code reads the secret byte and uses its value as an index into that probe array — touching probe[secret]. That single touch pulls exactly one line of the probe array into the cache. The fault then fires, the architectural results vanish, the registers look untouched. But probe[secret] is still warm. The attacker times a read of all 256 lines: 255 are slow misses, one is a fast hit, and which line is fast spells out the secret byte. The secret was laundered from a register the attacker could not read into a cache address the attacker can measure.
Meltdown core, conceptually (the asterisk * is a C dereference):
flush(probe[0..255]) // start with a cold probe array
secret = *kernel_pointer // FORBIDDEN: raises a fault...
junk = probe[secret * 256] // ...but speculation does this FIRST,
// pulling probe[secret] into cache
// fault now delivered -> architectural state rolled back
// but probe[secret] is still cached
for v in 0..255: // the reload / timing step
t = time( read probe[v*256] )
if t is FAST: secret == v // the one hit reveals the byte
Repeat one byte at a time to dump a whole region of kernel memory.Spectre: tricking a program into attacking itself
Spectre is subtler, harder to fix, and named that way because — its authors warned — it would haunt us for a long time. Where Meltdown jumps the privilege fence inside one program, Spectre stays within the victim's own permissions and abuses the branch predictor to make the victim run a speculative instruction it would never run for real. The classic variant targets a bounds check. Imagine a victim function that does the right thing: `if (i < array_len) y = array2[array1[i]];`. The check is correct. But the branch predictor is a bet on the past, and the attacker first calls this code many times with valid `i` so the predictor learns 'this branch is taken'.
Now the attacker calls it with `i` deliberately out of bounds — pointing far past array1, at a secret. The bounds check `i < array_len` is false, so the load should be skipped. But the predictor, trained to expect 'taken', speculatively runs the body anyway: it reads array1[i] — the secret byte — and uses it to index array2, pulling array2[secret] into the cache, all in the speculative window before the comparison resolves and the work is thrown away. The architectural result is correct (the body never officially ran), yet the secret has been encoded into the cache exactly as in Meltdown. The attacker then times array2 to recover it. The victim attacked itself, with its own legitimate code, on the attacker's behalf.
This is why Spectre is so much harder to kill. Meltdown is roughly one CPU bug — a load that leaked data before its permission check — and a hardware fix plus mapping the kernel out of user space (KPTI) largely closes it. Spectre is not a single bug but a class: any code path where the processor speculates across a security-relevant decision can in principle be steered. There is no kernel boundary being violated and nothing obviously 'wrong' in the victim's source. The leak lives in the gap between what the ISA promises and what the microarchitecture actually does.
Mitigations, and the price they charge
There is no single switch that makes this go away, because the cause is a performance feature we genuinely want. Mitigation is a layered defence, and almost every layer costs speed. Software can plant a speculation barrier (an instruction like `lfence`) after a sensitive bounds check, forcing the CPU to actually wait for the check before running the load — exactly the stall that out-of-order design existed to avoid. Compilers can rewrite branches into data-dependent arithmetic (so there is nothing for the predictor to mispredict), or use 'retpoline' tricks to keep indirect branches from speculating into attacker-chosen targets. Operating systems unmapped the kernel from user space and flush or partition predictor state across context switches.
Newer processors push fixes down into the hardware itself — adding the missing permission check so a forbidden load never forwards its byte speculatively, and giving software instructions to flush or isolate predictor state. Those help, but the deep tension remains: every barrier you insert, every speculation you forbid, every cache or predictor you flush on a boundary, throws away exactly the work that made the chip fast. Measured slowdowns from the various mitigations have ranged from barely noticeable for compute-bound code to double digits for system-call-heavy or context-switch-heavy workloads — the very programs that cross protection boundaries most often, which is exactly where the danger lives.
What it really means, and what it does not
Step back and see the shape of the lesson. For decades, processor architects treated speculative execution as invisible: as long as the architectural results were correct and faults eventually fired, the means seemed not to matter. Spectre and Meltdown shattered that view by showing the means leak. A correct, in-order-committed result is no longer proof that nothing was disclosed along the way, because the cache — and branch predictors, TLBs, and other shared microarchitectural state — quietly recorded the journey. The boundary that mattered was never just the ISA contract; it was the hardware that implements it.
Be honest about the limits too, because overstatement is its own kind of misinformation. These are side-channel reads, not arbitrary writes: an attacker learns secrets but does not directly corrupt memory or seize control of the program counter. The attacks are statistical and need careful timing, repeated trials, and a way to run code near the victim (a malicious process, or a script in a sandbox sharing a core). They do not magically read data across the internet, and a freshly patched system on modern hardware is substantially hardened. But the deeper point endures: the optimisations that make CPUs fast are now understood to be a genuine security surface, and 'the architecture says this can't be observed' is no longer a safe assumption — the microarchitecture gets a vote.