Speculative Execution Explained: Speed vs Security
Speculative execution lets a CPU guess ahead and run instructions before it knows they're needed, buying speed at the cost of the timing side channels behind Spectre and Meltdown.
Speculative execution is a CPU performance technique where the processor guesses the outcome of a branch or the result of a slow operation and starts executing instructions based on that guess, before it actually knows whether the guess is correct. If the guess is right, the CPU has already done useful work and saved time. If it’s wrong, the CPU discards the speculative results and starts over — but the guess-and-discard cycle, done well, is far faster on average than waiting to be certain before every instruction.
Why CPUs guess at all
Modern processors are pipelined: many instructions are in flight simultaneously, at different stages of being decoded and executed, rather than running strictly one after another. This works well for straight-line code, but branches — if statements, loop conditions, function calls through a pointer — break the pipeline’s flow. The CPU can’t know which instructions come next until the branch condition is evaluated, and evaluating it might itself take many cycles, especially if it depends on a value still being fetched from memory.
Stalling the pipeline until every branch resolves would waste enormous amounts of otherwise idle execution capacity. So instead, the CPU predicts. See branch prediction and out-of-order execution for the mechanics of how that prediction is made — pattern history in a branch predictor, informed by how the same branch resolved in the recent past. Speculative execution is what the CPU does with that prediction: it doesn’t just predict, it actually starts running the predicted path, retiring the results only if the prediction turns out correct.
What happens when the guess is wrong
When a speculatively executed branch turns out to have been mispredicted, the CPU has to unwind: discard the speculative results, flush the pipeline of instructions that were executing down the wrong path, and restart from the correct branch. This is called a pipeline flush, and it’s expensive relative to a correctly predicted branch — but modern branch predictors are right often enough (well above 90% for typical code) that the average-case speedup from speculating outweighs the cost of the occasional flush.
Crucially, “discarding the results” in the architectural sense — the registers and memory the program can see — doesn’t mean the speculation left no trace. That gap between architectural state and microarchitectural state is exactly what created a new class of vulnerability.
The security problem: timing side channels
Speculatively executed instructions can still touch the CPU cache even though their results are never committed to visible program state. If a speculative instruction reads memory it shouldn’t have permission to read — because the permission check itself hadn’t completed before the CPU started speculating — that memory access can still change what’s sitting in the cache. And cache state is measurable: a subsequent, architecturally valid instruction can time how long it takes to access different memory locations. A cache hit is fast; a cache miss is slow. That timing difference leaks information about what the speculative instruction touched, even though the speculative instruction’s actual result was discarded.
This is the mechanism behind Spectre and Meltdown, the two vulnerability classes disclosed in early 2018 that put speculative execution security on the map. Spectre tricks a victim process into speculatively executing code that accesses memory it shouldn’t, using the attacker’s own carefully crafted inputs to manipulate the branch predictor. Meltdown exploited a more specific flaw in some processors that allowed speculative execution to read privileged kernel memory from user space entirely, before the permission check caught up. Both rely on the same underlying pattern: speculation leaves a measurable trace in the cache even when its results never touch program-visible state, and a timing attack is enough to read that trace out.
How it’s mitigated
Fixing this class of vulnerability without eliminating speculative execution — which would erase a large fraction of modern CPU performance — took multiple layers:
- Hardware changes. Newer CPU designs add barriers that prevent certain speculative paths from executing across privilege boundaries, or that block speculative results from leaving a cache trace at all in sensitive contexts.
- Software mitigations. Operating systems and compilers added specific barrier instructions (like retpolines and fencing instructions) that block the specific speculative patterns these attacks rely on, inserted at points where untrusted code could otherwise manipulate a victim’s branch predictor.
- Isolation boundaries. Stricter separation between privilege levels and between tenants on shared hardware reduces how much an attacker’s speculative probing can actually reach.
None of these approaches eliminate speculative execution itself — that would mean giving up a large share of the performance gains it provides. They narrow the attack surface: making it harder to influence what a victim speculates on, and harder to read out the cache-timing signal afterward.
Speculative execution vs simple pipelining
| Simple pipelining | Speculative execution | |
|---|---|---|
| Handles branches by | Stalling until resolved | Guessing and executing ahead |
| Wrong-guess cost | N/A (no guess made) | Pipeline flush, discarded work |
| Performance | Lower on branch-heavy code | Higher, given accurate prediction |
| Security exposure | None from this mechanism | Cache-timing side channels (Spectre, Meltdown class) |
The takeaway
Speculative execution is a trade CPUs make deliberately: guess ahead on likely-correct paths, and the average-case speedup outweighs the cost of occasionally guessing wrong. That trade held up fine as a pure performance question for decades — until it became clear that discarded speculative work isn’t perfectly invisible. It still moves data through the cache, and cache timing is measurable from software. The CPU architectures shipping today still speculate aggressively; they just do it with more mitigations around privilege boundaries than they did before 2018, because the alternative — turning speculation off — was never a realistic option for performance-sensitive hardware.
Keep reading
Chisato · · 4 min read What Is Virtual Memory? Paging and Address Translation
Virtual memory gives every process its own private address space, mapped to physical RAM by the OS and CPU — enabling isolation, swapping, and overcommit.
Chisato · · 5 min read What Is Memory Interleaving?
Memory interleaving spreads consecutive addresses across multiple memory banks so the system can access them in parallel instead of one at a time.
Chisato · · 5 min read Big-Endian vs Little-Endian: Byte Order Explained
Endianness decides whether a multi-byte number's most or least significant byte is stored first in memory. Why it matters and how to spot it.