V8: one year with Specter

Original author: Ben L. Titzer, Jaroslav Sevcik
  • Transfer
On January 3, 2018, Google Project Zero and others revealed the first three of a new class of vulnerabilities that affect speculative execution processors. They were called Specter (1 and 2) and Meltdown . Using speculative CPU execution mechanisms , an attacker can temporarily bypass both explicit and implicit software security checks that prevent programs from reading inaccessible data in memory. While speculative execution was designed as a part of the microarchitecture, invisible at the architectural level, carefully designed programs could read inaccessible information in a speculative block and reveal it through side channels, such as the execution time of a program fragment.

When it was shown that Specter attacks are possible using JavaScript, the V8 team took part in solving the problem. We formed an emergency response team and worked closely with other Google teams, our other browser partners, and hardware partners. Together with them, we proactively conducted both offensive research (designing attack modules to prove the concept) and defensive (mitigating potential attacks).

The Specter attack consists of two parts:

  • Leak of otherwise inaccessible data into the latent state of the CPU . All known Specter attacks use speculation to transfer bits of inaccessible data to CPU caches.
  • Retrieving a hidden state to restore inaccessible data. For this, an attacker needs a watch of sufficient accuracy. (Surprisingly low accuracy, especially with methods such as edge thresholding - comparing with a threshold along a selected outline).

Theoretically, it would be enough to block any of the two components of the attack. Since we don’t know how to completely block any of them, we developed and deployed mitigations that significantly reduce the amount of information leaking into CPU caches and mitigations that make it difficult to recover a hidden state.

High precision timers


Tiny state changes that remain after speculative execution produce correspondingly tiny, almost impossibly tiny, temporal differences - of the order of a billionth of a second. To directly detect individual such differences, the attacker needs a high-precision timer. Processors offer such timers, but the web platform does not set them. The most accurate timer on the web platform performance.now()had a resolution of several microseconds, which was initially considered unsuitable for this purpose. However, two years ago, a research group specializing in microarchitecture attacks published an articleabout timers on the web platform. They concluded that simultaneous mutable shared memory and various resolution recovery methods allow the creation of timers of even higher resolution, down to the nanosecond. Such timers are accurate enough to detect individual hits and misses of the L1 cache. It is he who is usually used to capture information in Specter attacks.

Timer protection


To disrupt the ability to detect small differences in time, browser developers have chosen a multilateral approach. In all browsers, the resolution was reduced performance.now()(in Chrome from 5 microseconds to 100) and random jitter was introduced to prevent restoration of resolution. After consultations between the developers of all browsers, together we decided to take an unprecedented step: immediately and retroactively disable the SharedArrayBufferAPI in all browsers to prevent the creation of a nanosecond timer.

Gain


At the beginning of our offensive research, it became clear that timer mitigations alone are not enough. One of the reasons is that an attacker can simply run his code repeatedly so that the cumulative time difference is much more than one hit or cache miss. We were able to construct reliable "gadgets" that use many cache lines at a time, up to the entire cache capacity, which gives a time difference of up to 600 microseconds. Later, we discovered arbitrary amplification methods that are not limited by cache capacity. Such amplification methods are based on repeated attempts to read secret data.

JIT Protection


To read inaccessible data using Specter, an attacker forces the CPU to speculatively execute code that reads normally inaccessible data and puts them in the cache. Protection can be considered from two sides:

  1. Prevent speculative code execution.
  2. Prevention of reading inaccessible data from the speculative pipeline.

We experimented with the first option, inserting recommended instructions for preventing speculation, such as LFENCEfrom Intel, on each critical conditional branch and using retpolins for indirect branches. Unfortunately, such heavy mitigations significantly reduce productivity (2-3x slowdown on the Octane benchmark). Instead, we took the second approach by inserting mitigation sequences that prevent sensitive data from being read due to improper speculation. Let me illustrate the technique with the following code snippet:

if (condition) {
  return a[i];
}

For simplicity, we assume that the condition is 0or 1. The code above is vulnerable if the CPU speculatively reads from a[i]when it iis out of range, gaining access to normally inaccessible data. An important observation is that in this case, speculation tries to read a[i]when the condition is equal 0. Our mitigation rewrites this program so that it behaves exactly the same as the original program, but does not allow any speculatively loaded data to leak.

We reserve one CPU register, which we call “poison”, to keep track of whether code is executing in a misinterpreted branch. The poison register is supported in all branches and calls of the generated code, so any incorrectly interpreted branch causes the poison register to become 0. Then we measure all memory accesses so that they unconditionally mask the result of all downloads with the current value of the poison register. This does not prevent the processor from predicting (or misinterpreting) the branches, but it destroys the information (potentially outside the limits) of the loaded values ​​due to incorrectly interpreted branches. The tool code is shown below ( athis is an array of numbers).

let poison = 1;
// …
if (condition) {
  poison *= condition;
  return a[i] * poison;
}

Additional code does not affect the normal (defined by the architecture) behavior of the program. It only affects the micro-architectural state when working on a CPU with speculative execution. If you instrument a program at the source code level, advanced optimizations in modern compilers can remove such instrumentation. In V8, we prevent the compiler from removing mitigations by inserting them at a very late stage of compilation.

We also use this poisoning technique to prevent leaks from indirect branches in the interpreter's bytecode loop and in the sequence of JavaScript function calls. In the interpreter we set the poison to0if the bytecode handler (i.e., a machine code sequence that interprets a single bytecode) does not match the current bytecode. For JavaScript calls, we pass the target function as a parameter (in the register) and set the poison at 0the beginning of each function if the incoming target function does not match the current function. With this softening, we see a slowdown of less than 20% in the Octane benchmark.

The mitigation for WebAssembly is simpler, since the main security check is to ensure that memory access is within boundaries. For 32-bit platforms, in addition to the usual bounds checks, we fill all the memory to the next power of two and unconditionally mask any upper bits of the user memory index. 64-bit platforms do not need such mitigation, since the implementation uses virtual memory protection for border checks. We experimented with compiling switch / case statements into binary search code instead of using a potentially vulnerable indirect branch, but it is too expensive for some workloads. Indirect calls are protected by retpolins.

Software Protection - Unreliable


Fortunately or unfortunately, our offensive research progressed much faster than defensive, and we quickly found it impossible to programmatically mitigate all possible leaks during Specter attacks. There are several reasons for this. First, engineering efforts to combat Specter are disproportionate to the level of threat. In V8, we encounter many other security risks that are much worse, from reading directly outside borders due to common bugs (which is faster and easier than Specter), writing outside borders (this is impossible with Specter and worse) and potential remote code execution (impossible with Specter and much, much worse). Secondly, the increasingly complex mitigation measures that we developed and implemented carried significant challenges, which is a technical duty and can actually increase the attack surface and performance overhead. Thirdly, testing and maintaining mitigation of microarchitectural leaks is even more difficult than designing the gadgets themselves for an attack, since it is difficult to be sure that mitigations continue to work the way they were designed. At least once, important mitigations were effectively undone by later compiler optimizations. Fourth, we found that effectively mitigating some Specter options, especially option 4, is simply not possible in the software, even after the heroic efforts of our Apple partners to deal with the problem in their JIT compiler. Testing and maintaining mitigation of microarchitectural leaks is even more difficult than designing the gadgets themselves for an attack, since it is difficult to be sure that mitigations continue to work the way they were designed. At least once, important mitigations were effectively undone by later compiler optimizations. Fourth, we found that effectively mitigating some Specter options, especially option 4, is simply not possible in the software, even after the heroic efforts of our Apple partners to deal with the problem in their JIT compiler. Testing and maintaining mitigation of microarchitectural leaks is even more difficult than designing the gadgets themselves for an attack, since it is difficult to be sure that mitigations continue to work the way they were designed. At least once, important mitigations were effectively undone by later compiler optimizations. Fourth, we found that effectively mitigating some Specter options, especially option 4, is simply not possible in the software, even after the heroic efforts of our Apple partners to deal with the problem in their JIT compiler.

Site Isolation


Our research led to the conclusion: in principle, untrusted code can read the entire address space of a process using Specter and side channels. Software mitigations reduce the effectiveness of many potential gadgets, but are not effective or comprehensive. The only effective measure is to move sensitive data outside the process address space. Fortunately, Chrome has been trying for many years to separate sites into different processes in order to reduce the attack surface due to common vulnerabilities. These investments paid off, and by May 2018 we brought to the stage of readiness and expanded the isolation of sites on the maximum number of platforms. Thus, the Chrome security model no longer assumes language privacy during the render process.

Specter has come a long way and emphasized the merits of developer collaboration in industry and academia. So far, white hats are ahead of black ones. We still do not know about a single real attack, with the exception of curious experimenters and professional researchers developing gadgets to prove the concept. New variants of these vulnerabilities continue to appear and this will continue for some time. We continue to monitor these threats and take them seriously.

Like many programmers, we also thought that safe languages ​​provide the right border for abstraction, preventing well-typed programs from reading arbitrary memory. It is sad that this turned out to be a mistake - this guarantee does not correspond to today's equipment. Of course, we still believe that safe languages ​​have more engineering advantages, and the future lies with them, but ... on today's equipment they leak a little.

Interested readers can delve deeper into the topic and get more detailed information in our scientific article .

Also popular now: