1 of 19

Module: Speculative Execution

Spectre

Robert Wasinger

Arizona State University

2 of 19

Spectre

Leverages speculative execution by training the

CPU branch predictor to speculate incorrectly

3 of 19

Past results may indicate future performance

The CPU branch predictor uses past execution to make predictions

  • Individual branch history at specific virtual addresses
  • Global branch history of all branching on core

By repeatedly branching in one direction, the predictor can be biased

4 of 19

CPU Branch Predictors

mov rdi, [rdi]

mov rax, [rax]

cmp rax, rdi

jz

1

0

010110

Branch History

5 of 19

CPU Branch Predictors

Branch Predictor

mov rdi, [rdi]

mov rax, [rax]

cmp rax, rdi

jz

State Machine 1

State Machine 2

State Machine 3

State Machine 4

State Machine 5

1

0

010110

Branch History

Prediction:Jump!

6 of 19

CPU Branch Predictors - 2-bit counter

Consistently

Taken

Probably

Taken

Probably

Not Taken

Consistently

Not Taken

Not Taken

Taken

Not Taken

Taken

Not Taken

Taken

7 of 19

How to Train Your Branch Predictor

Training is physical core specific

  • In the same process, possibly different context (kernel vs userland)
  • Across address-spaces (cross process)

In many cases, training does not take many runs to start showing results!

8 of 19

Spectre Attacks

Two variants: V1 and V2

V1: Takes advantage of conditional branches

V2: Takes advantage of indirect branches

Unlike meltdown,

the spectre exploit occurs inside the victim process.

9 of 19

Spectre V1

if (x < array1_size) {

y = array2[array1[x] * 4096];

}

This block of code never executes if x > array1_size.

Spectre V1 relies on the fact the CPU can speculate it does.

10 of 19

Spectre V1 - Training

if (x < array1_size) {

y = array2[array1[x] * 4096];

}

Repeatedly passing the check trains the branch predictor to enter this block

array1_size = 10

x = 1

In Memory

array1[0] = 0

array1[1] = 1

array1[2] = 2

.

.

.

private_var: “secret value”

11 of 19

Spectre V1 - Exploiting

if (x < array1_size) {

y = array2[array1[x] * 4096];

}

Speculatively, array1 will be indexed out of bounds by a transient instruction. This allows arbitrary memory access while speculating.

array1_size = 10

x = 1000

In Memory

array1[0] = 0

array1[1] = 1

array1[2] = 2

.

.

.

private_var: “secret value”

12 of 19

Spectre V1 - Exploiting

if (x < array1_size) {

y = array2[“s” * 4096];

}

Speculatively, array1 will be indexed out of bounds by a transient instruction. This allows arbitrary memory access while speculating.

array1_size = 10

x = 1000

In Memory

array1[0] = 0

array1[1] = 1

array1[2] = 2

.

.

.

private_var: “secret value”

13 of 19

Spectre V1

if (x < array1_size) {

y = array2[array1[x] * 4096];

}

If array1_size takes a long time to access, the CPU can speculate:

  • When the CPU speculates, it predicts a branch
  • Incorrectly predicting results in transient instructions being executed
  • Transient instructions can influence the cache

14 of 19

Spectre V1

We’ve seen the pattern inside the conditional block before…

It leaks information via the cache that can be read via flush & reload!

y = array2[array1[x] * 4096];

DEMO

15 of 19

Spectre Attacks

Two variants: V1 and V2

V1: Takes advantage of conditional branches

V2: Takes advantage of indirect branches

16 of 19

Spectre V2

This example jumps to an address defined in memory at 0xdeadbeef

mov rdi, [rdi]

mov rsi, 0xdeadbeef

call QWORD PTR [rsi]

The CPU will attempt to predict where to jump and continue execution.

Training this predictor can cause arbitrary instructions to be executed as transient instructions!

Could be any “slow” instruction that encourages speculation

17 of 19

Spectre V2

The attacker influences branch predictor by calling:

  • Valid addresses in a different context
  • Different virtual addresses that access the same physical address
  • The branch predictor will predict jumping to these new locations

Executing again in a different context may trigger jumping to a spectre gadget

18 of 19

Spectre Gadgets

Instructions that when speculatively executed, influence microarchitecture state

Spectre Gadget

y = array2[array1[x] * 4096];

Influencing rbx or rdx changes what address will be speculatively accessed

Here, the data at rbx+rdx can be leaked via a cache side-channel

mov rdi, [rbx+rdx]

mov dl,byte ptr [rdi]

19 of 19

Spectre Variations and Beyond

Many other variations have been theorized or confirmed

All follow a similar pattern

  • Encode secret data in microarchitecture (CPU cache)
  • Retrieve embedded data via side-channel (Flush and Reload)