Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mitigate spectre with GCC and Clang (or LLVM in general)

Microsoft added a convenient /Qspectre to their MSVC compiler (although that seems to only be (trying to) mitigate Spectre v1 at the moment) that they will be updating over time. This is quite nice from a users perspective, just enable that flag and you get the latest and greatest mitigation they have in store.

With LLVM and GCC it looks a little bit different. I don't think their mitigations are officially released yet.

LLVM is supposed to get a -mretpoline compiler flag that mitigates Spectre v2 via return trampolines for indirect calls.

GCC on the other hand has patches that add three compiler options for mitigating Spectre v2:

  • -mindirect-branch that can be set to thunk. The way I understand it, this creates retpolines for every indirect call.
  • -mfunction-return that can be set to thunk. I imagine this uses these retpolines for every single function return, which might be necessary for Skylake because these processors can also predict returns?
  • -mindirect-branch-register This uses a register instead of a stack to store the address of the indirect call?

So I'm quite confused. What compiler option mitigates what and in what scenarios are they necessary for a user space application?

Is it a good idea to generally turn them on? Will they also generate these retpolines if compiled for a processor architecture that doesn't even have any speculative execution (like microprocessors)?

What about Spectre v1?

UPDATE:

Let me ask more precise questions:

  • Is my understanding of what the compiler options do correct?
  • Are the GCC options applied everywhere or only on processors with speculative execution?
  • Are the LLVM options applied everywhere or only on processors with speculative execution?
  • What exactly do these options mitigate (do they fully mitigate spectre v2)?
  • Let's consider everything else that I asked to be additional "bonus" questions" that are good to know but not essential to my question.
like image 386
FSMaxB Avatar asked Jan 24 '18 17:01

FSMaxB


1 Answers

Is my understanding of what the compiler options do correct?

I think so.

Are the GCC options applied everywhere or only on processors with speculative execution?

The options are x86 specific so far. I did not check whether there are equivalent ARM patches, but in any case these options will depend on the processor architecture at least partially.

I only had a look at some of the patches, but it does not look like additional runtime checks are imposed. Rather the instruction selection is changed to prevent the backend from emitting unwanted indirect jumps at all if the options are set.

So the option is not applied to completely unrelated architectures, but no attempt is made to discover whether the particular CPU is vulnerable at runtime. Note that most better known x86 processors sold since ca. the Pentium Pro do use speculative execution.

What exactly do these options mitigate (do they fully mitigate spectre v2)?

Mostly they mitigate branch target injection (Spectre v2), by ensuring that no attacker controlled speculation occurs at the respective indirect call.

Retpolines achieve this by using return instructions to jump to the target address, which uses a different branch predictor that basically remembers where the latest call came from. This is manipulated by the generated code which executes a call instruction before the return instruction, that ensures that the speculated execution will hit a dead end by putting an mfence instruction after the call. See this answer for more details.

Branch Target Injection is a problem, because as explained in the Spectre Paper under "5.1 Discussion", there may be lots of code mapped for the attacker to exploit. The discussion talks about Windows, but I could imagine that there must be some code from shared libraries mapped in Linux as well. If that code's addresses are more randomized exploitation may be harder, but I would not count on that without checking. In particular the Linux kernel contains lots of code that could be used by an attacker with the power of injecting arbitrary branch targets.

-mfunction-return

At the first glance it seems stupid to protect a return by replacing it with a more complicated return, but according to this message by David Woodhouse the option was requested by Linux kernel developers, because upon underflow of the remembered return addresses (basically a hidden stack following the call stack), some CPUs again pull in the global branch predictor, which may be manipulated by an attacker. So your explanation was correct. According to the same message this flag was not used right away by the Linux kernel. I assume the performance impact would be significant, since returns are much more common than other indirect branches, and the specialized return prediction will achieve excellent hit rates in practice.

-mindirect-branch-register

I am not quite sure, what -mindirect-branch-register mitigates. Apparently it is needed in the test suite together with the other options, but I could not find an explanation yet. Also Xen uses this option together with -mindirect-branch=thunk-extern, which means they write the thunk code themselves and do not let the compiler generate it. Contrary to my initial guess this has nothing to do with potential speculation while the address is loaded, as this is intended to be used with retpolines which prevent that speculation. In some discussions it was mentioned that the version of thunks that take the target address on the stack rather than in a register initially had conflicts with Intel Control-flow Enforcement Technology (CET). I assume since the retpolines are using returns in an unusual way, CET prevented the jumps. However, according to this discussion, these problems seem to have been resolved, and on machines which support CET, other mitigations (IBRS_ALL) are available, which allows the retpoline thunks to be replaced by indirect jumps again. I assume this option will not help a lot by itself.

Update: Spectre Variant 1

Recently Chandler Carruth proposed how to mitigate Spectre V1 in LLVM. This is still work-in-progress not yet landed in a release (as of March 2018). The basic idea is to mask out vulnerable addresses or loaded values on mis-speculated paths, before they can be used with side effects.

like image 173
PaulR Avatar answered Nov 17 '22 17:11

PaulR