Microsoft is aware of a new publicly disclosed class of vulnerabilities, called “speculative execution side-channel attacks,” that affect many operating systems and modern processors, including processors from Intel, AMD, and ARM. On the MSVC team, we’ve reviewed information in detail and conducted extensive tests, which showed the performance impact of the new /Qspectre
switch to be negligible. This post is intended as a follow-up to Terry Myerson’s recent Windows System post with a focus on the assessment for MSVC. If you haven’t had a chance to read Terry’s post you should take a moment to read it before reading this one.
The Spectre and Meltdown vulnerabilities
The security researchers that discovered these vulnerabilities identified three variants that could enable speculative execution side-channel attacks. The following table from Terry’s blog provides the decoder ring for each of these variants:
Exploited Vulnerability | CVE | Exploit Name | Public Vulnerability Name |
---|---|---|---|
Spectre | 2017-5753 | Variant 1 | Bounds Check Bypass |
Spectre | 2017-5715 | Variant 2 | Branch Target Injection |
Meltdown | 2017-5754 | Variant 3 | Rogue Data Cache Load |
The mitigations for variant 2 and variant 3 are outside the scope of this post but are explained in Terry’s post. In this post, we’ll provide an overview of variant 1 and describe the steps that we’ve taken with the MSVC compiler to provide mitigation assistance.
What actions do developers need to take?
If you are a developer whose code operates on data that crosses a trust boundary then you should consider downloading an updated version of the MSVC compiler, recompiling your code with the /Qspectre
switch enabled, and redeploying your code to your customers as soon as possible. Examples of code that operates on data that crosses a trust boundary include code that loads untrusted input that can affect execution such as remote procedure calls, parsing untrusted input for files, and other local inter-process communication (IPC) interfaces. Standard sandboxing techniques may not be sufficient: you should investigate your sandboxing carefully before deciding that your code does not cross a trust boundary.
In current versions of the MSVC compiler, the /Qspectre switch only works on optimized code. You should make sure to compile your code with any of the optimization switches (e.g., /O2
or /O1
but NOT /Od
) to have the mitigation applied. Similarly, inspect any code that uses #pragma optimize([stg], off). Work is ongoing now to make the /Qspectre
mitigation work on unoptimized code.
The MSVC team is evaluating the Microsoft Visual C++ Redistributables to make certain that any necessary mitigations are applied.
What versions of MSVC support the /Qspectre switch?
All versions of Visual Studio 2017 version 15.5 and all Previews of Visual Studio version 15.6 already include an undocumented switch, /d2guardspecload
, that is currently equivalent to /Qspectre
. You can use /d2guardspecload
to apply the same mitigations to your code. Please update to using /Qspectre
as soon as you get a compiler that supports the switch as the /Qspectre
switch will be maintained with new mitigations going forward.
The /Qspectre
switch will be available in MSVC toolsets included in all future releases of Visual Studio (including Previews). We will also release updates to some existing versions of Visual Studio to include support for /Qspectre
. Releases of Visual Studio and Previews are announced on the Visual Studio Blog; update notifications are included in the Notification Hub. Visual Studio updates that include support for /Qspectre
will be announced on the Visual C++ Team Blog and the @visualc Twitter feed.
We initially plan to include support for /Qspectre
in the following:
- Visual Studio 2017 version 15.6 Preview 4
- An upcoming servicing update to Visual Studio 2017 version 15.5
- A servicing update to Visual Studio 2017 “RTW”
- A servicing update to Visual Studio 2015 Update 3
If you’re using an older version of MSVC we strongly encourage you to upgrade to a more recent compiler for this and other security improvements that have been developed in the last few years. Additionally, you’ll benefit from increased conformance, code quality, and faster compile times as well as many productivity improvements in Visual Studio.
What’s the performance impact?
Our tests show the performance impact of /Qspectre
to be negligible. We have built all of Windows with /Qspectre
enabled and did not notice any performance regressions of concern. Performance gains from speculative execution are lost where the mitigation is applied but the mitigation was needed in a relatively small number of instances across the large codebases that we recompiled. We advise all developers to evaluate the impact in the context of their applications and workloads as codebases vary greatly.
If you know that a particular block of your code is performance-critical (say, in a tight loop) and does not need the mitigation applied, you can selectively disable the mitigation with __declspec(spectre(nomitigation))
. Note that the __declspec
is not available in compilers that only support the /d2guardspecload
switch.
Understanding variant 1
Variant 1 represents a new vulnerability class that software developers did not previously realize they needed to defend against. To better understand the issue, it’s helpful to consider the following example code:
if (untrusted_index < array1_length) { unsigned char value = array1[untrusted_index]; unsigned char value2 = array2[value * 64]; }
In the above example, the code performs an array-bounds check to ensure that untrusted_index
is less than the length of array1
. This is needed to ensure that the program does not read beyond the bounds of the array. While this appears to be sound as written, it does not take into account microarchitectural behaviors of the CPU involving speculative execution. In short, it is possible that the CPU may mispredict the conditional branch when untrusted_index
is greater than or equal to length
. This can cause the CPU to speculatively execute the body of the if
statement. As a consequence of this, the CPU may perform a speculative out-of-bounds read of array1
and then use the value loaded from array1
as an index into array2
. This can create observable side effects in the CPU cache that reveal information about the value that has been read out-of-bounds. While the CPU will eventually recognize that it mispredicted the conditional branch and discard the speculatively executed state, it does not discard the residual side effects in the cache which will remain. This is why variant 1 exposes a speculative execution side-channel.
For a deeper explanation of variant 1, we encourage you to read the excellent research by Google Project Zero and the authors of the Spectre paper.
Mitigating variant 1
Software changes are required to mitigate variant 1 on all currently affected CPUs. This can be accomplished by employing instructions that act as a speculation barrier. For Intel and similar processors (including AMD) the recommended instruction is LFENCE
. ARM recommends a conditional move (ARM) or conditional selection instruction (AArch64) on some architectures and the use of a new instruction known as CSDB
on others. These instructions ensure that speculative execution down an unsafe path cannot proceed beyond the barrier. However, applying this guidance correctly requires developers to determine the appropriate places to make use of these instructions such as by identifying instances of variant 1.
In order to help developers mitigate this new issue, the MSVC compiler has been updated with support for the /Qspectre
switch which will automatically insert one of these speculation barriers when the compiler detects instances of variant 1. In this case the compiler detects that a range-checked integer is used as an index to load a value that is used to compute the address of a subsequent load. If you compile the example above with and without /Qspectre
, you will see the following code generation difference on x86:
Without /Qspectre |
With /Qspectre |
---|---|
?example@@YAEHHPAH0@Z PROC mov ecx, DWORD PTR _index$[esp-4] cmp ecx, DWORD PTR _length$[esp-4] jge SHORT $LN4@example mov eax, DWORD PTR _array$[esp-4] ; no lfence here mov dl, BYTE PTR [eax+ecx*4] mov eax, DWORD PTR _array2$[esp-4] movzx ecx, dl shl ecx, 8 mov al, BYTE PTR [ecx+eax] $LN4@example: |
?example@@YAEHHPAH0@Z PROC mov ecx, DWORD PTR _index$[esp-4] cmp ecx, DWORD PTR _length$[esp-4] jge SHORT $LN4@example mov eax, DWORD PTR _array$[esp-4] lfence mov dl, BYTE PTR [eax+ecx*4] mov eax, DWORD PTR _array2$[esp-4] movzx ecx, dl shl ecx, 8 mov al, BYTE PTR [ecx+eax] $LN4@example: |
As the above shows, the compiled code under /Qspectre
now contains the explicit speculation barrier instruction on line 6 which will prevent speculation from going down the unsafe path, thus mitigating the issue. (For clarity, the left hand side includes a comment, introduced with a ;
in assembly.)
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
.
References
For more details please see the official Microsoft Security Advisory ADV180002, Guidance to mitigate speculative execution side-channel vulnerabilities. Guidance is also available from Intel, Speculative Execution Side Channel Mitigations, and ARM, Cache Speculation Side-channels. We’ll update this blog post as other official guidance is published.
In closing
We on the MSVC team are committed to the continuous improvement and security of your Windows software which is why we have taken steps to enable developers to help mitigate variant 1 under the new /Qspectre
flag.
We encourage you to recompile and redeploy your vulnerable software as soon as possible. Continue watching this blog and the @visualc Twitter feed for updates on this topic.
If you have any questions, please feel free to ask us below. You can also send us your comments through e-mail at visualcpp@microsoft.com, through Twitter @visualc, or Facebook at Microsoft Visual Cpp. Thank you.