Skip to main content

libarch/x86/
bug.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! This file contains utilities related to probing and mitigating architectural
6//! bugs and vulnerabilities.
7//!
8//! In general, we cannot rely on the official means of enumerating whether a
9//! vulnerability is present. For example, it might only be enumerable after
10//! certain microcode updates are performed. Accordingly, if we cannot get an
11//! definitive "is not vulnerable" from the official means, we fall back to
12//! pessimistically assigning vulnerability on the basis of microarchitecture,
13//! making implicit reference to the following documents:
14//!
15//! * Intel:
16//!   https://software.intel.com/security-software-guidance/processors-affected-transient-execution-attack-mitigation-product-cpu-model
17//!   Discontinued models (e.g., Core 2, Nehalem, and Westmere) are not present
18//!   in the table; in those cases, we assume vulnerability by default, unless
19//!   otherwise mentions.
20//!
21//! * AMD: https://www.amd.com/en/corporate/product-security
22//!
23//! Further pessimistically, we default to assigning vulnerability in the case
24//! unknown architectures.
25//!
26//! For non-architectural MSRs whose fields give workarounds to a specific
27//! erratum, if no further qualification is given (e.g., a field name/mnemonic),
28//! we name the field "erratum_${ID}_workaround".
29
30use super::cpuid::{
31    EXTENDED_AMD_FEATURES_B, EXTENDED_FEATURES_D, FEATURE_FLAGS_C, Microarchitecture, VERSION_INFO,
32    VendorString,
33};
34use super::{
35    ArchCapabilitiesMsr, SpeculationControlMsr, Vendor, VirtualSpeculationControlMsr, has_ibrs,
36    has_stibp, tsx_is_supported,
37};
38use bitrs::layout;
39use regio::traits::{ReadReg, RwSafeReg, SafeWriteReg};
40use regio::x86::{Cpuid, Msr};
41use regio::{LayoutOver, RwSafe};
42
43/// Whether the CPU is susceptible to swapgs speculation attacks:
44/// https://software.intel.com/security-software-guidance/advisory-guidance/speculative-behavior-swapgs-and-segment-registers
45///
46/// CVE-2019-1125.
47pub fn has_x86_swapgs_bug(cpuid: &impl Cpuid) -> bool {
48    let vendor = VendorString::from_cpuid(cpuid).vendor();
49    match vendor {
50        // All Intel CPUs seem to be affected and there is no indication that they
51        // intend to fix this.
52        Vendor::Unknown | Vendor::Intel => true,
53        Vendor::Amd => false,
54    }
55}
56
57/// Whether the CPU is susceptible to any of the Microarchitectural Data
58/// Sampling (MDS) bugs.
59///
60/// CVE-2018-12126, CVE-2018-12127, CVE-2018-12130, CVE-2019-11091.
61pub fn has_x86_mds_bugs(cpuid: &impl Cpuid, msr: &impl ReadReg<ArchCapabilitiesMsr>) -> bool {
62    // https://software.intel.com/security-software-guidance/resources/processors-affected-microarchitectural-data-sampling
63    if ArchCapabilitiesMsr::is_supported(cpuid) && msr.read().mds_no() {
64        return false;
65    }
66    let version_info = cpuid.read(VERSION_INFO);
67    let vendor = VendorString::from_cpuid(cpuid).vendor();
68    let march = version_info.microarchitecture(vendor);
69    match march {
70        Microarchitecture::Unknown
71        | Microarchitecture::IntelCore2
72        | Microarchitecture::IntelNehalem
73        | Microarchitecture::IntelWestmere
74        | Microarchitecture::IntelSandyBridge
75        | Microarchitecture::IntelIvyBridge
76        | Microarchitecture::IntelHaswell
77        | Microarchitecture::IntelBroadwell
78        | Microarchitecture::IntelSkylake
79        | Microarchitecture::IntelSkylakeServer
80        | Microarchitecture::IntelCannonLake
81        | Microarchitecture::IntelIceLake
82        | Microarchitecture::IntelSilvermont
83        | Microarchitecture::IntelAirmont => true,
84
85        Microarchitecture::IntelTigerLake
86        | Microarchitecture::IntelAlderLake
87        | Microarchitecture::IntelRaptorLake
88        | Microarchitecture::IntelBonnell
89        | Microarchitecture::IntelSaltwell
90        | Microarchitecture::IntelGoldmont
91        | Microarchitecture::IntelGoldmontPlus
92        | Microarchitecture::IntelTremont
93        | Microarchitecture::AmdFamilyBulldozer
94        | Microarchitecture::AmdFamilyJaguar
95        | Microarchitecture::AmdFamilyZen
96        | Microarchitecture::AmdFamilyZen3 => false,
97    }
98}
99
100/// Whether the CPU is susceptible to the TSX Asynchronous Abort (TAA) bug.
101///
102/// CVE-2019-11135.
103pub fn has_x86_taa_bug(cpuid: &impl Cpuid, msr: &impl ReadReg<ArchCapabilitiesMsr>) -> bool {
104    // https://software.intel.com/security-software-guidance/advisory-guidance/intel-transactional-synchronization-extensions-intel-tsx-asynchronous-abort
105    //
106    // A processor is affected by TAA if both of the following are true:
107    // * CPU supports TSX (indicated by the HLE or RTM features);
108    // * CPU does not enumerate TAA_NO.
109    let taa_no = ArchCapabilitiesMsr::is_supported(cpuid) && msr.read().taa_no();
110    if !tsx_is_supported(cpuid) || taa_no {
111        return false;
112    }
113    let version_info = cpuid.read(VERSION_INFO);
114    let vendor = VendorString::from_cpuid(cpuid).vendor();
115    match version_info.microarchitecture(vendor) {
116        Microarchitecture::Unknown
117        | Microarchitecture::IntelHaswell
118        | Microarchitecture::IntelBroadwell
119        | Microarchitecture::IntelSkylake
120        | Microarchitecture::IntelSkylakeServer
121        | Microarchitecture::IntelCannonLake
122        | Microarchitecture::IntelIceLake => true,
123
124        /* Does not implement TSX. */
125        Microarchitecture::IntelCore2
126        /* Does not implement TSX. */
127        | Microarchitecture::IntelNehalem
128        /* Does not implement TSX. */
129        | Microarchitecture::IntelWestmere
130        | Microarchitecture::IntelSandyBridge
131        | Microarchitecture::IntelIvyBridge
132        | Microarchitecture::IntelTigerLake
133        | Microarchitecture::IntelAlderLake
134        | Microarchitecture::IntelRaptorLake
135        | Microarchitecture::IntelBonnell
136        | Microarchitecture::IntelSaltwell
137        | Microarchitecture::IntelSilvermont
138        | Microarchitecture::IntelAirmont
139        | Microarchitecture::IntelGoldmont
140        | Microarchitecture::IntelGoldmontPlus
141        | Microarchitecture::IntelTremont
142        | Microarchitecture::AmdFamilyBulldozer
143        | Microarchitecture::AmdFamilyJaguar
144        | Microarchitecture::AmdFamilyZen
145        | Microarchitecture::AmdFamilyZen3 => false,
146    }
147}
148
149/// Whether the CPU is susceptible to any of the MDS or TAA bugs, which are
150/// closely related and similarly mitigated.
151pub fn has_x86_mds_taa_bugs(cpuid: &impl Cpuid, msr: &impl ReadReg<ArchCapabilitiesMsr>) -> bool {
152    has_x86_mds_bugs(cpuid, msr) || has_x86_taa_bug(cpuid, msr)
153}
154
155/// Whether the MDS/TAA bugs can be mitigated, which all make use of the same
156/// method (MD_CLEAR):
157/// https://software.intel.com/security-software-guidance/deep-dives/deep-dive-intel-analysis-microarchitectural-data-sampling#mitigation4processors
158pub fn can_mitigate_x86_mds_taa_bugs(cpuid: &impl Cpuid) -> bool {
159    cpuid.read(EXTENDED_FEATURES_D).md_clear()
160}
161
162/// Whether the CPU is susceptible to the Speculative Store Bypass (SSB) bug:
163/// https://software.intel.com/security-software-guidance/advisory-guidance/speculative-store-bypass
164///
165/// CVE-2018-3639.
166pub fn has_x86_ssb_bug(cpuid: &impl Cpuid, msr: &impl ReadReg<ArchCapabilitiesMsr>) -> bool {
167    // Check if the processor explicitly advertises that it is not affected, in
168    // both the Intel and AMD ways.
169    if ArchCapabilitiesMsr::is_supported(cpuid) && msr.read().ssb_no() {
170        return false;
171    }
172    if cpuid.supports(EXTENDED_AMD_FEATURES_B) && cpuid.read(EXTENDED_AMD_FEATURES_B).ssb_no() {
173        return false;
174    }
175    let version_info = cpuid.read(VERSION_INFO);
176    let vendor = VendorString::from_cpuid(cpuid).vendor();
177    match version_info.microarchitecture(vendor) {
178        Microarchitecture::Unknown
179        | Microarchitecture::IntelCore2
180        | Microarchitecture::IntelNehalem
181        | Microarchitecture::IntelWestmere
182        | Microarchitecture::IntelSandyBridge
183        | Microarchitecture::IntelIvyBridge
184        | Microarchitecture::IntelHaswell
185        | Microarchitecture::IntelBroadwell
186        | Microarchitecture::IntelSkylake
187        | Microarchitecture::IntelSkylakeServer
188        | Microarchitecture::IntelCannonLake
189        | Microarchitecture::IntelIceLake
190        | Microarchitecture::IntelTigerLake
191        | Microarchitecture::IntelAlderLake
192        | Microarchitecture::IntelRaptorLake
193        | Microarchitecture::IntelGoldmont
194        | Microarchitecture::IntelGoldmontPlus
195        | Microarchitecture::IntelTremont
196        | Microarchitecture::AmdFamilyBulldozer
197        | Microarchitecture::AmdFamilyJaguar
198        | Microarchitecture::AmdFamilyZen
199        | Microarchitecture::AmdFamilyZen3 => true,
200        Microarchitecture::IntelBonnell
201        | Microarchitecture::IntelSaltwell
202        | Microarchitecture::IntelSilvermont
203        | Microarchitecture::IntelAirmont => false,
204    }
205}
206
207/// Whether the CPU is susceptible to the Rogue Data Cache Load (Meltdown) bug:
208/// https://software.intel.com/security-software-guidance/advisory-guidance/rogue-data-cache-load.
209///
210/// CVE-2017-5754.
211pub fn has_x86_meltdown_bug(cpuid: &impl Cpuid, msr: &impl ReadReg<ArchCapabilitiesMsr>) -> bool {
212    // Check if the processor explicitly advertises that it is not affected.
213    if ArchCapabilitiesMsr::is_supported(cpuid) && msr.read().rdcl_no() {
214        return false;
215    }
216    let version_info = cpuid.read(VERSION_INFO);
217    let vendor = VendorString::from_cpuid(cpuid).vendor();
218    let march = version_info.microarchitecture(vendor);
219    match march {
220        Microarchitecture::Unknown
221        | Microarchitecture::IntelCore2
222        | Microarchitecture::IntelNehalem
223        | Microarchitecture::IntelWestmere
224        | Microarchitecture::IntelSandyBridge
225        | Microarchitecture::IntelIvyBridge
226        | Microarchitecture::IntelHaswell
227        | Microarchitecture::IntelBroadwell
228        | Microarchitecture::IntelSkylake
229        | Microarchitecture::IntelCannonLake => true,
230        Microarchitecture::IntelIceLake
231        | Microarchitecture::IntelTigerLake
232        | Microarchitecture::IntelAlderLake
233        | Microarchitecture::IntelRaptorLake
234        | Microarchitecture::IntelBonnell
235        | Microarchitecture::IntelSaltwell
236        | Microarchitecture::IntelSilvermont
237        | Microarchitecture::IntelAirmont
238        | Microarchitecture::IntelGoldmont
239        | Microarchitecture::IntelTremont
240        | Microarchitecture::AmdFamilyBulldozer
241        | Microarchitecture::AmdFamilyJaguar
242        | Microarchitecture::AmdFamilyZen
243        | Microarchitecture::AmdFamilyZen3 => false,
244        // Special cases from the above table.
245        Microarchitecture::IntelSkylakeServer => {
246            let info = cpuid.read(VERSION_INFO);
247            if info.stepping() >= 0x6 {
248                // Cascade Lake server+
249                return false;
250            }
251            true // Skylake server
252        }
253        Microarchitecture::IntelGoldmontPlus => {
254            let info = cpuid.read(VERSION_INFO);
255            if info.stepping() == 0x1 {
256                // First stepping was suceptable to Meltdown.
257                return true;
258            }
259            false
260        }
261    }
262}
263
264/// An architecturally prescribed mitigation for Spectre v2.
265#[derive(Clone, Copy, Debug, Eq, PartialEq)]
266pub enum SpectreV2Mitigation {
267    /// Enhanced/always-on IBRS (i.e., IBRS that can be enabled once without
268    /// automatic disabling) is preferred alone.
269    Ibrs,
270    /// IBPB and/or retpoline are recommended.
271    IbpbRetpoline,
272    /// IBPB and/or retpoline are recommended - and STIPB, though not preferred as
273    /// a performant mitigation, is also present and may be used.
274    IbpbRetpolineStibp,
275}
276
277/// Returns the preferred Spectre v2 mitigation strategy.
278pub fn get_preferred_spectre_v2_mitigation(
279    cpuid: &impl Cpuid,
280    msr: &impl ReadReg<ArchCapabilitiesMsr>,
281) -> SpectreV2Mitigation {
282    let vendor = VendorString::from_cpuid(cpuid).vendor();
283    match vendor {
284        Vendor::Unknown => {}
285        Vendor::Intel => {
286            // https://software.intel.com/security-software-guidance/advisory-guidance/branch-target-injection
287            //
288            // If enhanced IBRS are supported, it should be used for mitigation
289            // instead of retpoline; else retpoline
290            if has_ibrs(cpuid, msr, /*always_on_mode=*/ true) {
291                return SpectreV2Mitigation::Ibrs;
292            }
293        }
294        Vendor::Amd => {
295            // [amd/ibc]: EXTENDED USAGE MODELS.
296            // AMD further offers a feature bit to indicate whether IBRS is a
297            // preferred mitigation strategy.
298            if has_ibrs(cpuid, msr, /*always_on_mode=*/ true)
299                && cpuid.supports(EXTENDED_AMD_FEATURES_B)
300                && cpuid.read(EXTENDED_AMD_FEATURES_B).prefers_ibrs()
301            {
302                return SpectreV2Mitigation::Ibrs;
303            }
304            // [amd/ibc]: USAGE.
305            // Though not recommended, STIPB is still a viable mitigation strategy.
306            if has_stibp(cpuid, /*always_on_mode=*/ true) {
307                return SpectreV2Mitigation::IbpbRetpolineStibp;
308            }
309        }
310    }
311    // Retpolines comprise an architecturally agnostic, pure software solution,
312    // which makes it a sensible default strategy.
313    SpectreV2Mitigation::IbpbRetpoline
314}
315
316/// [amd/ssbd] references bits 10, 33, 54.
317/// [amd/rg/17h/00h-0Fh] references bits 4, 57.
318pub const AMD_LOAD_STORE_CONFIGURATION: Msr<0xc001_1020, AmdLoadStoreConfigurationMsr, RwSafe> =
319    Msr::new();
320
321layout!({
322    /// Layout for [`AMD_LOAD_STORE_CONFIGURATION`].
323    pub struct AmdLoadStoreConfigurationMsr(u64);
324    {
325        let __ @ 63..58;
326        let erratum_1095_workaround @ 57;
327        let __ @ 56..55;
328        let ssbd_15h @ 54;
329        let __ @ 53..34;
330        let ssbd_16h @ 33;
331        let __ @ 32..11;
332        let ssbd_17h @ 10;
333        let __ @ 9..5;
334        let erratum_1033_workaround @ 4;
335        let __ @ 3..0;
336    }
337});
338
339/// [amd/rg/17h/00h-0Fh] references bit 4.
340pub const AMD_C0011028: Msr<0xc001_1028, AmdC0011028Msr, RwSafe> = Msr::new();
341
342layout!({
343    /// Layout for [`AMD_C0011028`].
344    pub struct AmdC0011028Msr(u64);
345    {
346        let __ @ 63..5;
347        let erratum_1049_workaround @ 4;
348        let __ @ 3..0;
349    }
350});
351
352/// [amd/rg/17h/00h-0Fh] references bit 13.
353pub const AMD_C0011029: Msr<0xc001_1029, AmdC0011029Msr, RwSafe> = Msr::new();
354
355layout!({
356    /// Layout for [`AMD_C0011029`].
357    pub struct AmdC0011029Msr(u64);
358    {
359        let __ @ 63..14;
360        let erratum_1021_workaround @ 13;
361        let __ @ 12..0;
362    }
363});
364
365/// [amd/rg/17h/00h-0Fh] references bit 34.
366pub const AMD_C001102D: Msr<0xc001_102d, AmdC001102dMsr, RwSafe> = Msr::new();
367
368layout!({
369    /// Layout for [`AMD_C001102d`].
370    pub struct AmdC001102dMsr(u64);
371    {
372        let __ @ 63..35;
373        let erratum_1091_workaround @ 34;
374        let __ @ 33..0;
375    }
376});
377
378/// Attempt to mitigate the SSB bug. Return true if the bug was successfully
379/// mitigated.
380pub fn mitigate_x86_ssb_bug(
381    cpuid: &impl Cpuid,
382    speculation: &impl RwSafeReg<SpeculationControlMsr>,
383    virtual_speculation: &impl RwSafeReg<VirtualSpeculationControlMsr>,
384    load_store_configuration: &impl RwSafeReg<AmdLoadStoreConfigurationMsr>,
385) -> bool {
386    if cpuid.read(EXTENDED_FEATURES_D).ssbd() {
387        debug_assert!(SpeculationControlMsr::is_supported(cpuid));
388        speculation.modify(|value| *value.set_ssbd(true));
389        return true;
390    }
391    if cpuid.supports(EXTENDED_AMD_FEATURES_B) {
392        let amd_features = cpuid.read(EXTENDED_AMD_FEATURES_B);
393        if amd_features.ssbd() {
394            debug_assert!(SpeculationControlMsr::is_supported(cpuid));
395            speculation.modify(|value| *value.set_ssbd(true));
396            return true;
397        }
398
399        if amd_features.virt_ssbd() {
400            debug_assert!(VirtualSpeculationControlMsr::is_supported(cpuid));
401            virtual_speculation.modify(|value| *value.set_ssbd(true));
402            return true;
403        }
404    }
405
406    // [amd/ssbd]: NON-ARCHITECTURAL MSRS.
407    //
408    // There are non-architectural mechanisms to disable SSB for AMD families
409    // 0x15-0x17.
410    let version_info = cpuid.read(VERSION_INFO);
411    let vendor = VendorString::from_cpuid(cpuid).vendor();
412    match version_info.microarchitecture(vendor) {
413        Microarchitecture::AmdFamilyBulldozer => {
414            load_store_configuration.modify(|value| *value.set_ssbd_15h(true));
415            true
416        }
417        Microarchitecture::AmdFamilyJaguar => {
418            load_store_configuration.modify(|value| *value.set_ssbd_16h(true));
419            true
420        }
421        Microarchitecture::AmdFamilyZen => {
422            load_store_configuration.modify(|value| *value.set_ssbd_17h(true));
423            true
424        }
425        _ => false,
426    }
427}
428
429pub fn can_mitigate_x86_ssb_bug(cpuid: &impl Cpuid) -> bool {
430    // With a null I/O provider, we can make the requisite checks without
431    // actually committing the writes.
432
433    struct NullMsr;
434
435    impl<Layout: LayoutOver<u64>> ReadReg<Layout> for NullMsr {
436        fn read(&self) -> Layout {
437            Layout::from(0u64)
438        }
439    }
440
441    impl<Layout> SafeWriteReg<Layout> for NullMsr {
442        fn write(&self, _value: Layout) {}
443    }
444
445    mitigate_x86_ssb_bug(cpuid, &NullMsr, &NullMsr, &NullMsr)
446}
447
448/// Applies workarounds to processor-specific errata.
449pub fn apply_x86_errata_workarounds(
450    cpuid: &impl Cpuid,
451    c0011028: &impl RwSafeReg<AmdC0011028Msr>,
452    c0011029: &impl RwSafeReg<AmdC0011029Msr>,
453    c001102d: &impl RwSafeReg<AmdC001102dMsr>,
454    load_store: &impl RwSafeReg<AmdLoadStoreConfigurationMsr>,
455) {
456    if cpuid.read(FEATURE_FLAGS_C).hypervisor() {
457        return;
458    }
459
460    let vendor = VendorString::from_cpuid(cpuid).vendor();
461    match vendor {
462        Vendor::Unknown => {}
463        Vendor::Intel => {}
464        Vendor::Amd => {
465            let info = cpuid.read(VERSION_INFO);
466            #[expect(clippy::single_match)]
467            match info.family() {
468                0x17 => {
469                    #[expect(clippy::single_match)]
470                    match info.model() {
471                        // [amd/rg/17h/00h-0Fh].
472                        0x00..=0x0f => {
473                            // ZP-B1 refers to (model, stepping) == (1, 1); some of the errata are
474                            // detailed as only applying to that CPU.
475                            let zp_b1 = info.model() == 1 && info.stepping() == 1;
476                            // 1021: Load Operation May Receive Stale Data From Older Store
477                            //       Operation.
478                            c0011029.modify(|value| *value.set_erratum_1021_workaround(true));
479
480                            let mut lscfg = load_store.read();
481                            // 1033: A Lock Operation May Cause the System to Hang.
482                            if zp_b1 {
483                                lscfg.set_erratum_1033_workaround(true);
484                            }
485                            // 1095: Potential Violation of Read Ordering In Lock Operation in SMT
486                            //       Mode.
487                            if true {
488                                // TODO(https://fxbug.dev/42113091): Do not apply if SMT is
489                                // disabled.
490                                lscfg.set_erratum_1095_workaround(true);
491                            }
492                            load_store.write(lscfg);
493
494                            // 1049: FCMOV Instruction May Not Execute Correctly.
495                            c0011028.modify(|value| *value.set_erratum_1049_workaround(true));
496
497                            // 1091: 4K Address Boundary Crossing Load Operation May Receive Stale
498                            //       Data.
499                            c001102d.modify(|value| *value.set_erratum_1091_workaround(true));
500                        }
501                        _ => {}
502                    }
503                }
504                _ => {}
505            }
506        }
507    }
508}