This has been a very interesting New Year. So far, 2018 has brought us two of the biggest bugs to hit CPUs in the past 12 years: Meltdown and Spectre. Intel is struggling to get their Meltdown bug under control while Amazon and Microsoft brutally await its impact on their cloud services. Both Meltdown and Spectre are serious issues, but Meltdown is easier to exploit and gives attackers more valuable information. Also, Meltdown is only a vulnerability for Intel CPUs while Spectre affects all CPUs. Another interesting thing to note is that Spectre could be exposed through Javascript (or any other JIT compiler) while Meltdown can be exposed by almost any locally installed software.
Speculative Execution:
Meltdown and Spectre are results of this interesting mechanism of speculative execution. Speculative execution is an optimization technique that practically every CPU uses to save resources and increase performance output and efficiency. Speculative execution is mostly used to reduce the cost of conditional statements. For example, say I have the following:
if(x > y){
x = z;
}
With speculative execution, the CPU will execute this conditional even before its determined whether the conditional is true. In other words, before we even check if X is truly greater than Y, we will execute this statement and hold onto the results. If indeed it is true, then great, we just saved time. Otherwise, we disregard the results.
It’s import to point out that the CPU only does this pre-execution if it has strong reason to believe that X will be greater than Y. Therefore, if at other points in the program we compared X to Y or similar values and it was true, then we (as the CPU) can safely assume that it will be true later on.
Spectre Javascript Vulnerability:
As I mentioned earlier, Spectre could be exposed through Javascript or any other JIT compiled languaged. However, please don’t freak out and start disabling Javascript as some other clickbait news articles have suggested. For one, your browsing experience on most websites will be terrible if not impractical without Javascript. Secondly, the Spectre javascript vulnerability is very complicated to actually pull off.
Here is some sample code from a well-explained research paper of the vulnerability.
if (x < array1_size)
y = array2[array1[x] * 256];
Suppose that X is some malicious value that resolves to a memory address K, and also suppose that you have flushed the CPU Cache and that array1.size and array2 are not in memory but K is. Finally, you will need the CPU to perform its speculative operation so that it thinks x will likely be less than array1_size. This could be accomplished by running X in some loop and changing the value on its last iteration.
The result is that you can now read that address K. Now keep in mind, your program will already need to have the privilege to read that address anyway (unlike the Meltdown bug) so if we are talking about Javascript in a web browser, you are essentially bypassing Chrome’s sandbox and getting access to other memory in Chrome.
Again, this is not impossible to do but it’s not a simple copy and paste script. As described in the article, it was necessary to compile the JS to machine code and manipulate a couple of things to get the desired result.
if (index < simpleByteArray.length) {
index = simpleByteArray[index | 0];
index = (((index * TABLE1_STRIDE)|0) & (TABLE1_BYTES-1))|0;
localJunk ^= probeTable[index|0]|0;
}
The above lines are the full program with constants defined in their article. The following is the generated machine code with the changes to get the necessary values loaded into memory.
1 cmpl r15,[rbp-0xe0] ; Compare index (r15) against simpleByteArray.length
2 jnc 0x24dd099bb870 ; If index >= length, branch to instruction after movq below
3 REX.W leaq rsi,[r12+rdx*1] ; Set rsi=r12+rdx=addr of first byte in simpleByteArray
4 movzxbl rsi,[rsi+r15*1] ; Read byte from address rsi+r15 (= base address+index)
5 shll rsi, 12 ; Multiply rsi by 4096 by shifting left 12 bits}\%\
6 andl rsi,0x1ffffff ; AND reassures JIT that next operation is in-bounds
7 movzxbl rsi,[rsi+r8*1] ; Read from probeTable
8 xorl rsi,rdi ; XOR the read result onto localJunk
9 REX.W movq rdi,rsi ; Copy localJunk into rdi
Meltdown
So this is the big one that kicked it all off. Meltdown is also a result of speculative execution but comes from a trivial abuse in the system. Let’s refer back to the same conditional as above:
if(x > y){
x = z;
}
Now suppose this conditional execute, on an Intel CPU this could be very bad. Let’s say the line x = z is actually access to the special L1 D-cache that contains privilege data like cryptographic keys. Since this data is privileged, it’s only accessible to programs running in the Kernel space. But wait, we just trigged the CPU into thinking that we are most likely a privilege program and therefore it just takes a few extra steps to get access to this data. This is very important when the OS is context switching and working with memory between programs that should not be shared or accessible by other programs.
This vulnerability does not exist on AMD CPUs because they check the security flags upon processes accessing the cache. Albeit a slower but more secure option.