Slide 1

Slide 1 text

Meltdown & Spectre @jorendorff Meltdown and Spectre are two new security attacks revealed January 4th. You may have heard about this. They allow an attacker to steal sensitive data, like passwords. These attacks are like nothing I've seen before.

Slide 2

Slide 2 text

Inside modern CPUs To understand how they work, you need to understand branches, instruction pipelines, and speculative execution.

Slide 3

Slide 3 text

Inside modern CPUs * Branches

Slide 4

Slide 4 text

Inside modern CPUs * Branches * Instruction pipelines

Slide 5

Slide 5 text

Inside modern CPUs * Branches * Instruction pipelines * Speculative execution

Slide 6

Slide 6 text

Branches if (the light is off) { turn the light on; } look for keys; There's a decision point in this code. At the end of the first line, the CPU has to decide whether to continue with the next instruction, and turn the light on; or jump ahead to this part where it looks for your car keys. This kind of decision point is called a branch.

Slide 7

Slide 7 text

Branches if (the light is off) { turn the light on; } look for keys;

Slide 8

Slide 8 text

Branches while (!sink.empty) { do dishes; } switch (game.mode) { case EASY_MODE: //... } return; Normal code is full of branches. They're everywhere. It's not a problem, unless you're a CPU.

Slide 9

Slide 9 text

Branches while (!sink.empty) { do dishes; } switch (game.mode) { case EASY_MODE: //... } return;

Slide 10

Slide 10 text

Branches while (!sink.empty) { do dishes; } switch (game.mode) { case EASY_MODE: //... } return;

Slide 11

Slide 11 text

Branches while (!sink.empty) { do dishes; } switch (game.mode) { case EASY_MODE: //... } return;

Slide 12

Slide 12 text

Instruction pipeline Modern CPUs have instruction pipelines. They're massive assembly lines for computation. One part of the CPU is racing ahead and fetching upcoming instructions from RAM; another part is decoding the instructions that have been fetched; another part is figuring out what data we need for those instructions; another part is prefetching that data from RAM; another part is actually executing the instructions; and another part is storing the results. This is a picture of a jet engine but it’s basically the same thing. Actual Intel CPUs have a 31-stage pipeline. It's one of the engineering wonders of the world. But branches are bad for the pipeline. Why?

Slide 13

Slide 13 text

Instruction pipeline if (the light is off) { turn the light on; } look for keys; oh no which way are we going? When you reach a branch, you can't keep racing ahead and working on what's next, because you don't know what's next! You have to wait here for the actual compute stage of the CPU to come along and tell you which branch to take.

Slide 14

Slide 14 text

This is called a stall. We're not moving forward anymore. Execution is stalled. Well, stalls suck, so CPUs are designed to try to figure out as early as possible which way a branch will go, to avoid stalling. And if they can't figure it out in advance… they guess.

Slide 15

Slide 15 text

Speculative execution if (the light is off) { turn the light on; } look for keys; The CPU will make an educated guess as to which way the branch will go, and race ahead in that direction. This is called speculative execution, because the CPU is starting to execute these instructions, and just sort of hoping they are the right ones. If so, great! We avoided a stall.

Slide 16

Slide 16 text

Speculative execution if (the light is off) { turn the light on; } look for keys; whatever, just assume it’s off. go go go!

Slide 17

Slide 17 text

Speculative execution But the guess may turn out to be wrong. Eventually the CPU figures out the branch should have gone the other way. Then what? Well, the parts of the CPU that have been racing ahead have done a bunch of erroneous work. The CPU has to discard all that work, go back to the branch, and start over, loading, decoding, and executing instructions along the branch that the program actually took. If that happens, it's as bad as a stall, but we were going to stall anyway. And CPUs are good at guessing, so at most branches, speculative execution prevents stalls.

Slide 18

Slide 18 text

Speculative execution whoa, whoa… everybody back up

Slide 19

Slide 19 text

The complete attack OK, now you know enough to understand Meltdown and Spectre. The two attacks are very similar. In five steps:

Slide 20

Slide 20 text

The complete attack 1. Run evil code First, you have to get your target to run some code for you. Which seems like it should be hard. People should be like, no, I’m not running your evil code. Who are you and what are you doing in my living room? But, they’ll visit your web site and run your JavaScript. So you’re in.

Slide 21

Slide 21 text

The complete attack 1. Run evil code 2. Trigger speculative execution Second, you have to get speculative execution to happen. You must train the CPU on your code so that the CPU expects one branch to be taken, and starts speculatively executing those instructions.

Slide 22

Slide 22 text

if (index is in bounds) { ... } Your goal is to get this to happen.

Slide 23

Slide 23 text

if (index is in bounds) { ... } whatever, just assume it’s in bounds. go go go!

Slide 24

Slide 24 text

The complete attack 1. Run evil code 2. Trigger speculative execution 3. Read the secret Now the attack. In those speculative instructions, you load a byte of data that you're not supposed to have access to.

Slide 25

Slide 25 text

if (index is in bounds) { let secretByte = array[index]; ... } whatever, just assume it’s in bounds. go go go! One way to do it is to read off the end of a typed array. For now, just accept that this is allowed. Obviously it shouldn’t be. But in theory, it's OK, because we’re only speculatively executing this code. If the read turns out to be invalid, all this gets rolled back, so it’s no big deal.

Slide 26

Slide 26 text

if (index is in bounds) { let secretByte = array[index]; ... } whatever, just assume it’s in bounds. go go go! we’re very sure the index is in bounds

Slide 27

Slide 27 text

The complete attack 1. Run evil code 2. Trigger speculative execution 3. Read the secret 4. ??? OK. So we have one byte of a secret. Now we're in a scenario from a science fiction story. You're an evil secret agent, and you've obtained the top-secret information! But the universe you're living in is a spurious universe. A mistake universe. Mere nanoseconds from now, the CPU is going to detect the mistake and roll all this back. Your temporary universe where you've got the secret is going to be rolled back out of existence. How do you get the secret out?

Slide 28

Slide 28 text

The complete attack 1. Run evil code 2. Trigger speculative execution 3. Read the secret 4. Smuggle it out It turns out there are ways to do this. There are things you can tell the CPU to do that have subtle side effects that won’t get rolled back.

Slide 29

Slide 29 text

if (index is in bounds) { let secretByte = array1[index]; g_dontCare = array2[secretByte]; } whatever, just assume it’s in bounds. go go go! For example, do a second memory access, for a location that depends on this secret data. So if the byte you’ve read is, I dunno, 65, the next thing you do is look up element number 65 in a separate array, one that you created. I know, this looks super pointless. But this is what it looks like when you’re trying to smuggle information from one timeline to another, OK? Here’s why this works. The CPU caches every piece of memory it touches, for speed, so when you do this, the CPU grabs element number 65 out of “array2”, and that gets cached.

Slide 30

Slide 30 text

if (index is in bounds) { let secretByte = array1[index]; g_dontCare = array2[secretByte]; } whatever, just assume it’s in bounds. go go go! 65

Slide 31

Slide 31 text

if (index is in bounds) { let secretByte = array1[index]; g_dontCare = array2[secretByte]; } whatever, just assume it’s in bounds. go go go! 65 read and cache array2[65]

Slide 32

Slide 32 text

if (index is in bounds) { let secretByte = array1[index]; g_dontCare = array2[secretByte]; } Eventually, the CPU detects its mistake, rolls back the universe to the branch, and executes the correct path. Your precious secret information is lost. …Or is it? You've still got `array2`, and one element of `array2` is cached in the CPU. The cache was not rolled back.

Slide 33

Slide 33 text

The complete attack 1. Run evil code 2. Trigger speculative execution 3. Read the secret 4. Smuggle it out 5. Recover it How can you recover the secret number that you lost? (discuss) Right. You precisely measure how long it takes to access each element of array2. Whichever element is fast, that’s the one you cached earlier! This is what's called a timing attack. You've just recovered one byte of the secret. Now repeat this for the next byte, and the next byte, until you've got the whole secret. Speaking of timing attacks, I think I’m about to be attacked, for running over my time, so let's wrap it up.

Slide 34

Slide 34 text

Stopping Meltdown 1. Run evil code 2. Trigger speculative execution 3. Read the secret 4. Smuggle it out 5. Recover it Meltdown attacks operating systems, using a specific CPU bug. In step 3, Meltdown reads from operating system memory that it categorically should not have access to. The CPU allows the read, speculatively, while the permission check is going on in parallel. Eventually it rolls everything back, but it's too late. …How do we fix this? Well, Intel needs to mail everyone a new CPU. But while we're waiting for that to happen, operating systems have been patched so that kernel memory isn't addressable at all in user processes. That stops Meltdown. It also makes your computer slower. So if you haven't installed updates lately, your laptop is about 5% faster than everyone else’s! Good work!

Slide 35

Slide 35 text

Stopping Meltdown 1. Run evil code 2. Trigger speculative execution 3. Read the secret 4. Smuggle it out 5. Recover it

Slide 36

Slide 36 text

Stopping Spectre 1. Run evil code 2. Trigger speculative execution 3. Read the secret 4. Smuggle it out 5. Recover it Spectre is the single-process version of this bug. It attacks browsers. With Spectre, it's easiest to block step 5. That's why all browsers are now rounding off the value of performance.now() to the nearest millisecond, and disabling something called SharedArrayBuffer. We're trying to eliminate this timing attack by eliminating timers. …It’s not good enough. We expect to see attacks that don't require precise timers, so we're going to have to somehow keep JIT code from accessing secrets even speculatively. …This makes Spectre unique and scary. We're talking about locking down code that in theory isn't even executing. That's where we are.

Slide 37

Slide 37 text

Stopping Spectre 1. Run evil code 2. Trigger speculative execution 3. Read the secret 4. Smuggle it out 5. Recover it

Slide 38

Slide 38 text

Stopping Spectre 1. Run evil code 2. Trigger speculative execution 3. Read the secret 4. Smuggle it out 5. Recover it today

Slide 39

Slide 39 text

Stopping Spectre 1. Run evil code 2. Trigger speculative execution 3. Read the secret 4. Smuggle it out 5. Recover it today

Slide 40

Slide 40 text

Stopping Spectre 1. Run evil code 2. Trigger speculative execution 3. Read the secret 4. Smuggle it out 5. Recover it today soon

Slide 41

Slide 41 text

I'm out of time. For more, read the two papers at meltdownattack.com. They're 16 pages each and very good. Thank you!