Spectre mitigations in MSVC

Update 20 April 2018 With Visual Studio 2017 version 15.7 Preview 4 we have added a new warning, C5045, that shows what patterns in your code would have caused a mitigation to be inserted. See this post for more details.

Update 4 April 2018 With Visual Studio 2017 version 15.7 Preview 3 we have two new features to announce with regards to our Spectre mitigations. First, the /Qspectre switch is now supported regardless of the selected optimization level. Second, we have provided Spectre-mitigated implementations of the Microsoft Visual C++ libraries. See below for details.

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. For a more complete explanation of these vulnerabilities, please see this TechNet post on Mitigating speculative execution side channel hardware attacks.

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.

We’re also adding Spectre-mitigated implementations of the Microsoft Visual C++ libraries. Visual Studio 2017 version 15.7 Preview 3 includes runtime libraries with mitigation enabled for a subset of the Visual C++ runtimes: VC++ start-up code, vcruntime140, msvcp140, concrt140, and vcamp140. We also include static library equivalents of those libraries. We are providing static linking support and App Local deployment only; the contents of the Visual C++ 2017 Runtime Libraries Redistributable have not been modified.

You must select these libraries for installation in the VS Installer under the Individual Components tab:

To enable Spectre mitigations for both your code and library code, simply select “Enabled” under the “Code Generation” section of the project Property Pages:

The C5045 diagnostic, added in Visual Studio 2017 version 15.7 Preview 4, shows where the compiler would insert a mitigation if the /Qspectre switch were enabled. Please see this post for more details.

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.

Current status

The following table shows the status of supported features in the versions of Visual Studio with Spectre mitigations available in the MSVC toolset:

Visual Studio Version (as of April 4, 2018) /Qspectre with optimizations /Qspectre without optimizations X86 and Amd64 Arm and Arm64 Mitigated libs C5045
VS 2015 Update 3
VS 2017 RTW 15.0 (26228.23)
VS 2017 15.5.5
VS 2017 15.6
VS 2017 15.7

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. Codebases vary greatly so we advise all developers to evaluate the impact of /Qspectre in the context of their applications and workloads.

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.