This technical deep dive expands on the information in the Speculative Behavior of SWAPGS and Segment Registers disclosure. Refer to the list of affected processors for more details.
In the IA-32 architecture, memory segmentation is used in the formation of physical memory addresses. Segment descriptors specify a base address (along with other attributes) for each segment, on which the rest of the physical address is built. The segment information is stored in a table in memory, and the individual segments are referenced by selectors that act as indices into this table. The architecture defines a set of segment registers (CS, SS, DS, ES, FS, and GS) that can each hold one selector value. One of these registers is either implicitly or explicitly used for every effective address memory access. More information on segmentation can be found in the Intel® 64 and IA-32 Architectures Software Developer Manual, Volume 3a, Chapter 3, Protected Mode Memory Management.
Many operating systems use the GS segment register to reference application and kernel data that is specific to a thread or processor. In such cases, the operating system maintains both user space and kernel values of GS. The
SWAPGS instruction is a privileged CPU instruction used to exchange the application and kernel values of GS.
If operating systems that use
SWAPGS to switch the contents of the GS register on kernel entry have code paths that conditionally determine whether or not to execute the instruction and then also contain memory references offset from the register, those OSes may be vulnerable to malicious actors who can cause the
SWAPGS instruction to be speculatively executed or bypassed. The CVE assigned to this vulnerability is CVE-2019-1125 (5.6 Medium CVSS:3.0/AV:L/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N/E:P/RL:O/RC:C).
We can use the example code sequence below to illustrate both of these cases:
0 test 1 je skip 2 swapgs 3 mov r10, qword ptr gs:[1000h] 4 mov rbx, [r10] 5 ... 6 skip: 7 mov rcx, qword ptr gs:[2000h] 8 mov rbx, [rcx] 9 …
If GS holds the user space value on entry (for example, if this was part of a system call) and the branch is speculatively predicted to be taken, then the speculative execution path skips the
SWAPGS instruction. The memory reference on line 7 would speculatively execute with the user space value of GS, and thus may load kernel data from an address specified by a malicious actor. The memory reference on line 8 would be based on the value loaded by line 7, which may affect the cache in a way that might be detectable by a malicious actor in user space.
In the other case, if GS holds the kernel value on entry (for example, if this code was part of an interrupt handler where interrupts could be generated from either kernel or user space) and the branch is speculatively predicted to not be taken, then the speculative execution path executes the
SWAPGS instruction, switching to the user space value of GS. The memory reference on line 3 would speculatively execute with the user space value and thus may load kernel data from an address specified by a malicious actor. The memory reference on line 4 would be based on the value loaded by line 3, which would affect the cache in a way that might be detectable by a malicious actor in user space.
Note: Real software may not have sequences that are as simple as the example above.
When converting an effective address into a linear address, the address generation hardware needs to know the base address and the limit of the applicable segment. When a segment register is written, these fields are cached within the processor in a microarchitectural structure called the segment register file.
Register renaming is a microarchitectural technique that can improve performance by allowing an operation to execute and update a register before the processor knows whether older operations will cause a jump mispredict, trap, interrupt, or exception. For at least some types of register renaming, a renamed register file will have more physical entries than the number of architectural registers that software can specify and a mapping from each architectural register to each physical entry. When software writes one of these registers, the hardware picks a new physical register file entry, writes the result into it, and causes the architectural register to point to the physical register instead of overwriting the contents of the physical register that was previously used. If that operation retires (no older operation takes a jump mispredict, trap, interrupt, or exception) then the old (unused) physical register file entry is no longer needed and can be reclaimed. However, if the operation does not retire, then the old contents of that register can be restored by mapping the architectural register back to the old physical register file entry.
This technique can also be applied to segment registers. A processor can maintain a renamed segment register file and keep updating the architectural segment register to point to the correct entry (reflecting the current architectural state) in the segment register file. This may be applicable to instructions which directly operate on segment bases, like
WRGSBASE, as well.
The processor may speculatively execute instructions that perform writes to segment registers, and thus segment bases, in the segment register file. Since these operations happen under a speculative window, the processor may allocate an entry in the segment register file and update it with a new base address. Later, the processor may determine that this speculative execution path was wrong and thus will not update the architectural state. The architectural segment register would still point to the old entry and the old base. The entry allocated in the segment register file during the speculative window may retain its speculatively written value. For lack of other terminology, we can call this entry a stale segment register entry as it is not currently being used for the architectural segment register. If the processor determines that the speculative execution path was correct, then it will update the architectural state to the newly written value. In this case, the previously used entry that held the old base would become a stale segment register entry.
If the segment register is loaded with a new selector value, a processor may perform the following operations:
On processors with segment renaming, step 5 may still occur when step 2 detects that the segment descriptor is not valid. If the descriptor is invalid, step 2 ensures the operation faults and the changes are never reflected in the architectural state. However, if dependent instructions consume the results of this segment write (for example, by reading it out through
RDFSBASE), the dependent instructions may speculatively get the value from the newly allocated segment entry in the segment register file (from step 3). Some implementations will not write the segment register file when the segment descriptor is invalid, which can result in the value in the allocated segment entry being a stale value that was written by some previous segment write operation. Malicious actors may be able to reveal this stale value using a speculative execution disclosure gadget.
The affected CPUs have two dedicated segment register file entries for each architectural segment, one of which is currently used for the segment, while the other is currently unused at any particular time. These entries are not shared between logical processors (for example, no entry is sometimes used by hyperthread 0 and sometimes used by hyperthread 1 on the same physical core), nor are they shared between different architectural segment registers (for example, there is no segment register file entry that is sometimes allocated to GS and sometimes to FS).
The values contained in a stale segment register file entry may belong to previously run software. It may be possible for a malicious actor to infer the value in the unused segment register file entry, thus inferring the segment base of that previously run software. This may apply to previously run applications, guests, kernels, or virtual machine managers (VMMs). Most software only uses non-zero segment bases for those segments used for thread local storage (FS and GS segments).
Malicious actors who are able to control victim execution (for example, through a lack of mitigations against branch target injection attacks (also known as Spectre variant 2)) may be able to induce the victim to speculatively execute a segment write with data desired by the malicious actor. This speculatively written data may go into the unused segment register file entry of the destination segment.
When the malicious code later runs on the same logical processor, the malicious actor may be able to recover this data from the unused segment register file entry using the aforementioned technique.
It is also possible for two pieces of software to intentionally pass data through the unused segment register file entry of a segment.
After assessing this issue, industry partners determined that mitigations for this issue would be implemented by the operating system. For more details about these mitigations, refer to the Microsoft* security advisory for Windows* operating systems and the latest kernel.org documentation for Linux* operating systems.
Generically, the recommended mitigation for when
SWAPGS is speculatively missed (as in the first example case above) is to add an
LFENCE or serializing instruction before the first memory reference using GS (for example, between lines 6 and 7 in the example code) on all paths that can speculatively skip the
SWAPGS instruction. This will create a speculation barrier that forces the conditional to be evaluated before the memory reference executes.
The mitigation for when an extra
SWAPGS instruction is speculatively executed when it should not be (as in the second case above) is to add an
LFENCE or serializing instruction after the
SWAPGS instruction (for example, after line 2 in the example code).
However, for optimal performance, each instance of
SWAPGS should be evaluated to determine whether an
LFENCE is actually needed. For instance, a potentially problematic code sequence might only speculatively execute without mappings of secret data, in which case the malicious actor would not have access to that data. In other cases, there may be some other serializing instruction before the memory accesses that would serve the same purpose as
LFENCE. Writes to the CR3 register used to mitigate rogue data cache load (also known as Meltdown) are serializing instructions and can be utilized for serialization in lieu of
Many channels already exist that can be used for side channels. In general, the mitigations outlined above do not prevent the side channels, but rather prevent malicious actors from controlling speculation in order to put victim data into the channel in the first place. For example, a branch target injection attack that uses segment registers as a side channel (instead of using the data caches as the original branch target injection attack did) should be mitigated by preventing branch target injection attacks in general (for example, through retpoline or indirect branch restricted speculation (IBRS)) instead of attempting to address the specific channel (in this case, segment registers).
The unused segment register file entry may contain information from previously run applications or guests. Most segment bases are zero except for segments used for thread local storage (FS and GS). Intel does not believe that these segment bases are application secrets and does not recommend mitigating this information leakage.
The segment register file entries for a segment can be overwritten with the specified data by a pair of
POP xS, or
LGS instructions that write that segment on at least Intel® microarchitecture code name Sandy Bridge and later processors. Loading a null segment selector may be lower latency than loading a segment from a descriptor table.
SWAPGS being executed after ring transitions, applications cannot observe the GS value from previous applications, but they may be able to infer the kernel GS base.
Intel recommends that you always keep your systems up to date with the latest security updates and guidance from your OS and VMM vendors.
Intel thanks the Linux kernel community and other industry partners who have contributed significantly to mitigations for speculative execution side channel methods, and recognizes the following people for their development efforts and dedication to software security: