Introduction

Every time your code reaches an if statement, the CPU faces a choice: wait until the condition resolves (stalling for several clock cycles), or guess which branch will be taken and start executing it right away. Modern processors chose to guess — and almost always get it right.

This technique, speculative execution, is one of the biggest reasons that today's processors run many times faster than a naive design would. The branch predictor learns your code's patterns over thousands of iterations and predicts with over 95% accuracy. When the guess is correct, the work is already done. When it is wrong, the processor squashes everything it computed speculatively, rolls back the architectural state, and takes the correct path instead. No harm done — or so it seemed.

In January 2018, researchers from Google Project Zero and several universities disclosed Spectre and Meltdown, two families of attacks that exploit exactly what happens during that squash. The architectural state is rolled back perfectly — but the microarchitectural state, most importantly the cache, is not. The discarded speculative work leaves a footprint in the cache that an attacker can measure, one memory access at a time. Data that was never supposed to be readable can be extracted byte by byte.

The gap between what the architecture promises and what the microarchitecture actually does turned out to be one of the most consequential security flaws in hardware history.

Try It: Branch Training

This demo simulates the core of a Spectre-style attack in three steps. First, a branch is trained so the predictor expects the "safe" path. Then one out-of-bounds access is fired speculatively. Finally, the cache timing reveals which memory line was touched — exposing the secret without ever reading it directly.

<!-- {{c_html_intro}} -->
<div class="panel" id="panel-train">
  <h3>{{phase_train_title}}</h3>
  <p class="desc">{{phase_train_desc}}</p>
  <div class="progress-row">
    <span class="label">{{label_iteration}}</span>
    <div class="bar-wrap"><div class="bar" id="bar-train"></div></div>
    <span class="count" id="count-train">0 / {{train_total}}</span>
  </div>
  <div class="branch-vis" id="branch-vis">
    <span class="branch-true" id="bvis-true">{{branch_safe}}</span>
    <span class="branch-sep">|</span>
    <span class="branch-false" id="bvis-false">{{branch_unsafe}}</span>
  </div>
  <button id="btn-train" type="button">{{btn_start_training}}</button>
</div>

<div class="panel" id="panel-attack" style="display:none">
  <h3>{{phase_attack_title}}</h3>
  <p class="desc">{{phase_attack_desc}}</p>
  <div class="secret-row">
    <span class="label">{{label_secret}}</span>
    <span class="secret-box" id="secret-display">?</span>
    <span class="label small">{{label_secret_hint}}</span>
  </div>
  <button id="btn-attack" type="button">{{btn_fire_attack}}</button>
</div>

<div class="panel" id="panel-probe" style="display:none">
  <h3>{{phase_probe_title}}</h3>
  <p class="desc">{{phase_probe_desc}}</p>
  <div id="probe-grid" class="probe-grid"></div>
  <div class="status" id="status-probe"></div>
  <button id="btn-reset" type="button" class="ghost">{{btn_reset}}</button>
</div>
/* {{c_css_intro}} */
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: system-ui, sans-serif; color: #222; background: transparent; }
.panel { background: #f4f7fa; border: 1px solid #d0dae4; border-radius: 10px;
         padding: 1rem 1.1rem; margin-bottom: .8rem; }
h3 { font-size: 1rem; font-weight: 700; margin-bottom: .4rem; color: #1d3557; }
.desc { font-size: .88rem; color: #444; line-height: 1.5; margin-bottom: .8rem; }
/* {{c_css_progress}} */
.progress-row { display: flex; align-items: center; gap: .6rem; margin-bottom: .6rem; }
.label { font-size: .82rem; color: #555; white-space: nowrap; }
.label.small { font-size: .78rem; color: #777; }
.bar-wrap { flex: 1; height: 10px; background: #d5dfe8; border-radius: 5px; overflow: hidden; }
.bar { height: 100%; width: 0; background: #457b9d; border-radius: 5px; transition: width .12s; }
.count { font-size: .82rem; color: #555; white-space: nowrap; min-width: 3rem; text-align: right; }
/* {{c_css_branch_vis}} */
.branch-vis { display: flex; align-items: center; gap: .5rem; margin-bottom: .8rem;
              font-size: .85rem; font-weight: 600; }
.branch-true { color: #0a7d33; padding: .25rem .55rem; border-radius: 6px; background: #d4f0dc; }
.branch-false { color: #888; padding: .25rem .55rem; border-radius: 6px; background: #e8eef3; }
.branch-true.active { background: #0a7d33; color: #fff; }
.branch-false.active { background: #c92f3c; color: #fff; }
.branch-sep { color: #aaa; font-weight: 400; }
/* {{c_css_secret}} */
.secret-row { display: flex; align-items: center; gap: .7rem; margin-bottom: .8rem; }
.secret-box { font: 700 1.4rem ui-monospace, monospace; background: #1d3557; color: #fff;
              padding: .25rem .6rem; border-radius: 6px; letter-spacing: .05em; min-width: 2.5rem;
              text-align: center; }
/* {{c_css_probe}} */
.probe-grid { display: grid; grid-template-columns: repeat(8, 1fr); gap: 4px; margin-bottom: .7rem; }
.probe-slot { height: 36px; border-radius: 6px; background: #d5dfe8; position: relative;
              display: flex; align-items: flex-end; justify-content: center;
              padding-bottom: 3px; font-size: .7rem; color: #555; transition: background .15s; }
.probe-slot.hot { background: #e63946; color: #fff; }
.probe-slot .slot-label { position: absolute; bottom: 4px; }
/* {{c_css_status}} */
.status { font-size: .95rem; font-weight: 600; min-height: 1.4em; margin-bottom: .6rem; }
.status.ok { color: #0a7d33; }
.status.bad { color: #c92f3c; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem;
         border: 1px solid #1d3557; background: #1d3557; color: #fff;
         border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
// Code not found

Notice how the secret leaks through timing alone. The architectural check (index < array.length) was satisfied every time during training, so the predictor is confident. On the malicious access it predicts "safe," speculatively reads array[secret_offset], and populates a cache line — before the CPU notices the index is out of bounds and squashes the result. The squash removes the value from registers but the cache line stays warm. Measuring which probe slot is fast reveals the secret.

The Real Complexity

What makes Spectre and Meltdown so difficult to fully close?

  • The ISA contract says nothing about time. The instruction-set architecture promises which values registers hold after execution. It says nothing about how long memory accesses take. Cache timing is entirely outside the contract — yet it is observable by any unprivileged code.
  • Speculation is deep. A modern out-of-order core may have hundreds of instructions in flight simultaneously. Disabling speculation entirely would erase most of the performance gains of the last 30 years.
  • There are many variants. Spectre variant 1 exploits bounds-check bypass; variant 2 exploits branch-target injection; Meltdown exploits transient reads of kernel memory. New variants (SpectreV4, MDS, TAA, …) keep appearing because the root cause is the class of microarchitectural leakage, not one specific gadget.
  • Software mitigations are costly. Retpoline, LFENCE barriers, index masking, and site isolation add 5–30% overhead depending on workload. Microcode patches for some variants cost even more on certain hardware generations.
  • Hardware fixes are partial. Intel's eIBRS and AMD's IBRS help with branch-target injection but do not address all variants. A truly speculation-free design would need a fundamental rethink of the pipeline.

Spectre is not a single bug to patch — it is a new class of vulnerability that follows from the design of every high-performance processor built in the last 25 years. See P vs NP for another case where a seemingly innocuous gap has enormous consequences.

Where It Matters

Because the attack leaks through timing rather than memory reads, it affects almost every computing environment:

  • Cloud virtual machines: a tenant VM and a co-located VM may share a physical core and its branch predictor. Spectre variant 2 lets the attacker train the predictor of the hypervisor, potentially reading hypervisor memory from guest code.
  • Web browsers: JavaScript running in a tab can measure timer-based side channels. Browsers responded by reducing SharedArrayBuffer precision and disabling high-resolution timers — restoring some later only after site isolation landed.
  • Operating system kernels: Meltdown let unprivileged user code read arbitrary kernel memory on Intel hardware before KPTI (kernel page-table isolation) was deployed, at a cost of up to 30% on syscall-heavy workloads.
  • Smartphones and embedded devices: ARM processors with speculative execution (used in virtually every smartphone) are affected by multiple Spectre variants. Patching is slower to reach end devices.
  • Cryptographic implementations: even constant-time code can leak if the CPU speculatively branches on secret-dependent conditions. Side-channel-safe code now has to reason about speculative paths, not just the nominal execution.

The halting problem showed limits on what can be decided algorithmically; Spectre shows limits on what isolation guarantees hardware can actually provide.

Conclusion

Speculative execution is a masterpiece of engineering: by guessing ahead and discarding wrong guesses, processors deliver performance that would otherwise require a far more expensive design. For decades, this worked invisibly and safely — because the discarded work left no trace anyone could see.

Spectre and Meltdown showed that the invisible traces were never truly invisible. The cache remembers what speculation touched, and timing can read the cache. The architectural contract is satisfied, but the microarchitectural reality leaks secrets that the contract says should be private.

The lesson is not that speculation is wrong — it is that every abstraction layer has a shadow, and that shadow can be measured. When performance and security both depend on the same hardware, the gap between what the specification promises and what the silicon does becomes an attack surface. Understanding that gap is now a required skill for anyone building systems that must keep secrets.

Share this article

Pick a channel — or use your device's native share sheet.

Comments

Loading comments...

https://www.kipuhub.com/en/article/speculative-execution/Content licensed under CC BY-NC 4.0.