hackweb
notes on hacking and technology
IT EN
main menu
user menu
you are not logged in

Spectre: reading memory the CPU never really read

May 20, 2026 · 3 min read · #hardware #side-channel #cpu

Modern CPUs do not wait. When they hit a conditional branch — an if — they do not stop to compute the condition: the branch predictor guesses the outcome and the CPU starts executing the predicted path speculatively, ahead of time. If it guessed right, it saved time. If it guessed wrong, it throws the results away and restarts on the correct branch. Architecturally it is as if nothing happened.

The problem, disclosed in 2018, is that almost. Instructions that were speculated and then discarded leave a side effect the rollback does not clean up: the state of the cache. Spectre reads that state and recovers the data the speculation touched.

The vulnerable gadget

The canonical case, Spectre variant 1 (bounds check bypass):

if (x < array1_size) {
    y = array2[array1[x] * 4096];
}

The check x < array1_size looks like it guards the access. But if array1_size is not in cache, the CPU takes hundreds of cycles to load it. While waiting, the predictor — trained by us with many valid x — assumes the branch is taken, and speculates. With a malicious, out-of-bounds x, during the speculation window the CPU actually reads array1[x], one byte of arbitrary memory, and uses it as an index into array2.

The secret enters the cache

The trick is the multiply by 4096: the size of a page. The secret byte b = array1[x] determines which page of array2 gets pulled into cache by the speculative read. Every possible value of b (0–255) touches a different, distant page, so no prefetcher muddies the water.

Then the CPU realises x was out of bounds, rolls everything back, and y is never architecturally written. But array2[b*4096] stayed in cache.

Flush + Reload: reading the trace

Before the attack you evict all 256 candidate pages from the cache (clflush). After the speculation, you time how long it takes to read the first word of each:

for (i = 0; i < 256; i++) {
    addr = &array2[i * 4096];
    t0 = rdtscp();
    tmp = *addr;              // read
    dt = rdtscp() - t0;       // timing measurement
    if (dt < CACHE_HIT_THRESHOLD)
        results[i]++;         // this page was cached -> b == i
}

The page that reads in ~50 cycles instead of ~300 is the one the speculation pulled into cache: its index is the secret byte. Repeat the measurement to beat the noise, advance x by one byte, and read memory at will — one byte at a time, typically at a few KB/s.

Why it is so serious

Spectre does not exploit a software bug: it exploits the correct behaviour of the microarchitecture. The gadget code is flawless by any static analysis. And crucially it crosses boundaries we assumed were solid:

  • Browser sandbox. The original version read the browser process's memory from JavaScript, training the predictor with JS code. The answer was to take high-resolution timers away from sites (performance.now() coarsened, SharedArrayBuffer disabled for years) and to introduce site isolation into separate processes.
  • Kernel from user space and guest → host in virtualization, when the right gadget exists in privileged code.

The mitigations and their price

There is no single software patch because the root is in the silicon. It is fought on several fronts, each of which costs cycles:

  • Speculation barriers. On x86 an lfence after the bounds check stops the CPU speculating past it. The compiler can insert them, but they serialize execution: slow if overused.
  • Index clamping. Instead of trusting the branch, you mask the index so it stays in bounds even under speculation (array_index_nospec in the Linux kernel).
  • Retpoline for variant 2 (branch target injection): it replaces indirect calls with a construct that traps speculation in a harmless loop instead of letting it jump to a poisoned target.

The total bill, across Spectre, Meltdown and their descendants (MDS, L1TF, Retbleed), was a measurable performance loss on real workloads — in some cases in the double-digit percentages — paid by every datacenter on the planet. It is the definitive case study that the CPU abstraction is a useful lie: underneath, timing and the cache tell everything.


« back to home

latest posts
 
your IP address:
216.73.216.108
visitor #0
MOTD:
Every abstraction leaks somewhere.
Here we look at where.
topics