#039: Speculative Exploitation

You might have heard about a small problem concerning pretty much all computers worldwide.

There are actually two different problems, dubbed Meltdown and Spectre. To understand what problem they exploit, you first need to know a few things about modern computers. Specifically, branch prediction and virtual memory.

Branch Prediction

Back in the days of yore, processors were fairly simply beasts. When you gave them a program to run, they would load an instruction from memory, execute it, then write the results back to memory. They pretty much worked like cooking from a recipe, doing each step, one after the other, eventually arriving at a result.

However, processor designers soon realized this was a bit inefficient. During the time an instruction was loaded from memory, or the results were written back to memory, the processor wouldn’t be doing anything. So, they added a feature called pipelining, where the processor would load the next instruction while it was executing the current one. This works great if you’re executing instructions one after the other, but sometimes, the processor wouldn’t know which instruction came next, because it depended on the result of the current instruction. This is a branch in the program’s execution, because the “path” of the code executed can go down one of two different ways from this point. So, processors would have to wait until such a branching instruction was executed before they would know which one to load next.

In response, processor designers came up with branch prediction: Imagine you’re responsible for handling a railway switch (branching point in the program’s execution) on a train line. Every time a train (the program running its code) comes, you stop it, ask the train conductor which way they want to go (the result of the branching instruction), you set the switch, and the train starts up again, going down the correct track. This takes quite some time, because each train has to first come to a full stop, before starting up again.

After a while, you notice that 80% of the trains go down track A, and not B. So you come up with a new scheme: Instead of flagging down trains, you just let the switch set to track A, and let all the trains run through without stopping them. Only when the train conductor notices they’re on the wrong track, they stop the train, back up, tell you to set the switch the other way, and continue. This change speeds up the majority of the trains, at the cost of making the wrong prediction fairly costly. But the tradeoff is worth it.

Eventually, processor designers figured, why predict which way a branch is going to go? Why not just do both sides, and once the processor knows which branch is the correct one, discard the other one? This is where it gets a bit hard to demonstrate, because to reuse the analogy from above, there’s still a switch, but whenever a train comes along, it goes down both tracks simultaneously, and once the conductor figures out the correct path, the other side just… disappears. It’s hard to image this happening, and sounds more like something out of quantum physics, but it works. This technique is called speculative execution, and is another reason why modern processors are so fast.

Virtual Memory

Your computer contains many, many bytes of memory in its RAM. Each one of those bytes has an address. In the old days, the operating system’s kernel and a user’s program would simply share all the memory. Each program had to take care that it would only use the memory addresses allocated to it by the kernel. If it didn’t, and suddenly wrote data to addresses it shouldn’t have, it could corrupt other programs, or even the kernel, causing all kinds of problems. So eventually, processor designers came up with something called virtual memory.

With virtual memory, as far as the program is concerned, it has all the memory to itself. The kernel sets up the virtual memory handling, letting the processor know how to translate a virtual memory address into a real one, and all processes (and the kernel) can pretend as if all the memory is theirs to use.

However, this too introduces a small performance penalty whenever an address needs to be translated from virtual to real memory address, so processor designers included a special cache on the processor to make these translations faster. Additionally, operating system designers use a trick to make better use of the cache — processes don’t actually get the entire memory for themselves, as the kernel reserves part of the addresses (virtual or not) for itself. This way, the processor can make better use of the special cache, at the cost of the program not having access to the entire memory.

Meltdown

Meltdown exploits the fact that (not only) Intel processors allow speculative execution of code that calls into the kernel. While the processor has special protections built in to prevent processes from unauthorized access to kernel memory if it turns out the speculative execution got ahead of itself, and did something it shouldn’t have, doing so does change the special cache for translating virtual to real memory addresses — and malicious code can then use a timing attack, where the code measures how fast certain addresses are translated, to figure out how kernel memory looks like.

The good news about Meltdown is that it can be patched in software, by not having the kernel reserve a part of the memory for itself. Even though it incurs a small performance penalty since the cache can’t be used as effectively any more, it protects against this kind of attack.

Spectre

Spectre is more tricky, and virtually every modern processor is affected: By using similar tricks as Meltdown based on speculative execution and branch prediction in combination with a timing attack, malicious code running on a computer can eventually figure out the memory contents of any process or even the kernel itself, accessing secret keys or passwords. What’s especially problematic is that this includes JavaScript, so you could be attacked with Spectre by simply opening a web page.

Here, there isn’t really good news: Because this attack exploits one of the fundamental techniques used in high performance processors, there isn’t really much anyone can do. Browser vendors are introducing mitigations to make exploitation of Spectre via JavaScript hard to impossible, but unlike most other exploits, Spectre is actually an entire new class of exploits. This will likely result in an arms race between hackers, who will uncover new ways of using Spectre to read memory they shouldn’t be able to, and browser as well as operating system vendors including new safeguards to make those specific exploits harder. Actually solving the problem will require entirely new hardware, and cannot be done via software.