Spectre Mitigations in Microsoft's C/C++ Compiler
paulkocher.com
paulkocher.com
The fact that this isn't a full mitigation is mentioned in the original post from Microsoft, but it's more of a footnote. I think it would be better if it was spelled out more clearly so people don't think they're safe when they're not.
From https://blogs.msdn.microsoft.com/vcblog/2018/01/15/spectre-m...
> It is important to note that there are limits to the analysis that MSVC and compilers in general can perform when attempting to identify instances of variant 1. As such, there is no guarantee that all possible instances of variant 1 will be instrumented under /Qspectre.
Kind of reminds me of https://digitizor.com/internet-explorer-9-caught-cheating-in... (sorry for the non-primary source, the original blog post has since been taken down).
We--as an industry--are learning as we go with Spectre. There's a lot of data that went into the decision to release this switch with its current implementation. And this isn't the last iteration: note in Paul Kocher's writeup that we've asked for feedback as to whether anyone would use a switch that was sound (for known variants) but incurred very large performance regressions.
As evidence that this is an industry-wide issue, I ask that you reread Paul Kocher's post. Also, see Chandler Carruth's tweet this morning about this topic: https://twitter.com/chandlerc1024/status/963995521705627648. Chandler is the guy driving Spectre in LLVM.
Lastly, understand that Microsoft has a lot of customers who rely on our technologies. For their benefit it's often better to say less than to say more, especially when talking about security vulnerabilities.
Hi from one compiler engineer to another!
> Chandler is the guy driving Spectre in LLVM.
I actually sit about 25ft away from him; we talked about his tweet before lunch today. :)
> there is not an automatic fix available for Spectre. The /Qspectre switch offers help in mitigation. It doesn't offer, nor does it claim to offer, complete protection.
When I read this sentence and the footnote in the VCBlog post my takeaway is that /Qspectre offers incomplete protection that is nonetheless useful for a nontrivially broad class of applications. That is, I understand "incomplete mitigation" to be a stronger statement than "there exists a program in which the spectre attack is mitigated".
But when I read Paul's post, what I understand is that the level of protection offered is not useful for applications that do not look extremely similar to the original Spectre PoC.
I wonder if you think I'm being unfair in my reading of either of these documents?
I'm also genuinely curious how telling customers less about a security fix might be better for them than telling them more.
That is, compile an array access a[i] into an array of size N as a[i&M] where M is one less than the next power of two larger than N.
The logical and is very fast; it can be applied everywhere without a big performance hit. And this removes almost all the attack surface, as the real vulnerability is when the attacker forces speculative access to go way outside the array to unrelated memory that probably even belongs to another process.
Edit: actually there is an even better masking approach the linux kernel is using -- see https://lwn.net/Articles/744287/
You can only read from a process that you have managed to compromise, and spectre is a new category of exploit vectors not an exploit in and of itself.
So you need a process that takes external inputs (you need some way to influence it), and then you need to figure out how that input can resolve to a viable exploit via spectre. If your input validation code is spectre-free (LFENCE, whatever) then you're probably good to go. You don't need everything in your app to be spectre free. In the blog's example, for instance, he just assumes that somehow an attacker can influence the input X. But if there's no way for an attacker to do that because X never comes from any externally-influenced thing then you didn't need any LFENCEs on it.
Ensuring that all your external-touching code is completely spectre-free is non-trivial, of course, but it's not like you can just arbitrarily exploit anything you want, and you don't need every array access to be LFENCE'd or mask'd.
I suppose if you mitigate the branch predictor poisoning (retpoline perhaps?) then this is not a concern any more.
But consider the example function in the article:
void victim_function_v01(size_t x) {
if (x < array1_size) {
temp &= array2[array1[x] * 512];
}
}
If you can't control x as the attacker you can't really get this to do anything useful no matter which way you manage to get the if to predict. Simply forcing the if to speculate one way or the other does not result in arbitrary memory reads. You need to force the if and control x.Assuming you've mitigated spectre v2, right?
If you are vulnerable to v2, I can take any other indirect branch in your program (which may appear after some "y" I can control as an input) and have it speculatively branch from there to this "temp &= ..." code, leaking the value of "y".
If you are not vulnerable to spectre v2, then I agree, the paths are much more limited and tied to speculative execution that is related to attacker-controlled values.
I'm mentioning this because (at least to my understanding) in Spectre variant 2 the entire address space of the victim process can be used to find the "gadget" i.e. an usable target for the indirect branch. This means that making only your input validation code "spectre-free" is not good enough for variant 2. (This is why e.g. OpenSSH recently started using the (Spectre variant 2!) retpoline compiler flags of GCC/LLVM if available. See this thread for details: https://lists.mindrot.org/pipermail/openssh-unix-dev/2018-Fe...)
Doesn't that still permit speculatively getting an out-of-bound element, where the problem gets worse with larger arrays?
The following LWN article describes a more sophisticated masking approach by Alexei Starovoitov:
https://lwn.net/Articles/744287/
Of course, the same question still applies: why not use this cheaper masking approach. Are there known problems with it?
At first, I thought I had the wrong version of the compiler, since I wasn't seeing any LFENCEs. Finally, I tried compiling my example code from the Appendix of the Spectre paper, and saw LFENCEs appearing. Still, even small variations in the source code resulted in unprotected code being emitted, with no warning to developers.
Theres some explanation for why this is the case, but it's not looking all that good for Microsoft's implementation, IMO.
"Speculation barriers are only an effective defense if they are applied to all vulnerable code patterns in a process" Which they are not, since that would be a huge performance hit.
Hmm, I don't understand this statement. My (simplistic) understanding of this attack is that speculative execution produces detectable side-effects that result in memory being leaked from another address space that the attacker isn't supposed to be able to read. Surely if Spectre is used to leak the contents of something useless then that's not going to help the attacker compromise the application. Spectre is read-only, right? Am I missing something?
Or was that meltdown?
DRM technologies like "white box crypto," seem to be designed for the same use case. Is encoding sensitive operations in an application using these techs not a viable medium term mitigation as well?
[0]: https://bugs.chromium.org/p/chromium/issues/detail?id=798964
https://developer.arm.com/support/security-update
ans mips to spectre:
https://www.mips.com/blog/mips-response-on-speculative-execu...
Hint: The fact that ARM ranges from "completely immune" to "affected by the less-common Meltdown" gives away the answer.
At the technical level, these are subtle bugs that affect most of the industry (with the Meltdown bug affecting a more restricted set of chips). It's a mess we'll be paying for for years to come, but not one that Intel is especially culpable for.
Scary times.