blob: 30de16c2b8da194a6130e2490ed9837d06102997 [file]
// Copyright 2026 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! This file contains utilities related to probing and mitigating architectural
//! bugs and vulnerabilities.
//!
//! In general, we cannot rely on the official means of enumerating whether a
//! vulnerability is present. For example, it might only be enumerable after
//! certain microcode updates are performed. Accordingly, if we cannot get an
//! definitive "is not vulnerable" from the official means, we fall back to
//! pessimistically assigning vulnerability on the basis of microarchitecture,
//! making implicit reference to the following documents:
//!
//! * Intel:
//! https://software.intel.com/security-software-guidance/processors-affected-transient-execution-attack-mitigation-product-cpu-model
//! Discontinued models (e.g., Core 2, Nehalem, and Westmere) are not present
//! in the table; in those cases, we assume vulnerability by default, unless
//! otherwise mentions.
//!
//! * AMD: https://www.amd.com/en/corporate/product-security
//!
//! Further pessimistically, we default to assigning vulnerability in the case
//! unknown architectures.
//!
//! For non-architectural MSRs whose fields give workarounds to a specific
//! erratum, if no further qualification is given (e.g., a field name/mnemonic),
//! we name the field "erratum_${ID}_workaround".
use super::cpuid::{
EXTENDED_AMD_FEATURES_B, EXTENDED_FEATURES_D, FEATURE_FLAGS_C, Microarchitecture, VERSION_INFO,
VendorString,
};
use super::{
ArchCapabilitiesMsr, SpeculationControlMsr, Vendor, VirtualSpeculationControlMsr, has_ibrs,
has_stibp, tsx_is_supported,
};
use bitrs::layout;
use regio::traits::{ReadReg, RwSafeReg, SafeWriteReg};
use regio::x86::{Cpuid, Msr};
use regio::{LayoutOver, RwSafe};
/// Whether the CPU is susceptible to swapgs speculation attacks:
/// https://software.intel.com/security-software-guidance/advisory-guidance/speculative-behavior-swapgs-and-segment-registers
///
/// CVE-2019-1125.
pub fn has_x86_swapgs_bug(cpuid: &impl Cpuid) -> bool {
let vendor = VendorString::from_cpuid(cpuid).vendor();
match vendor {
// All Intel CPUs seem to be affected and there is no indication that they
// intend to fix this.
Vendor::Unknown | Vendor::Intel => true,
Vendor::Amd => false,
}
}
/// Whether the CPU is susceptible to any of the Microarchitectural Data
/// Sampling (MDS) bugs.
///
/// CVE-2018-12126, CVE-2018-12127, CVE-2018-12130, CVE-2019-11091.
pub fn has_x86_mds_bugs(cpuid: &impl Cpuid, msr: &impl ReadReg<ArchCapabilitiesMsr>) -> bool {
// https://software.intel.com/security-software-guidance/resources/processors-affected-microarchitectural-data-sampling
if ArchCapabilitiesMsr::is_supported(cpuid) && msr.read().mds_no() {
return false;
}
let version_info = cpuid.read(VERSION_INFO);
let vendor = VendorString::from_cpuid(cpuid).vendor();
let march = version_info.microarchitecture(vendor);
match march {
Microarchitecture::Unknown
| Microarchitecture::IntelCore2
| Microarchitecture::IntelNehalem
| Microarchitecture::IntelWestmere
| Microarchitecture::IntelSandyBridge
| Microarchitecture::IntelIvyBridge
| Microarchitecture::IntelHaswell
| Microarchitecture::IntelBroadwell
| Microarchitecture::IntelSkylake
| Microarchitecture::IntelSkylakeServer
| Microarchitecture::IntelCannonLake
| Microarchitecture::IntelIceLake
| Microarchitecture::IntelSilvermont
| Microarchitecture::IntelAirmont => true,
Microarchitecture::IntelTigerLake
| Microarchitecture::IntelAlderLake
| Microarchitecture::IntelRaptorLake
| Microarchitecture::IntelBonnell
| Microarchitecture::IntelSaltwell
| Microarchitecture::IntelGoldmont
| Microarchitecture::IntelGoldmontPlus
| Microarchitecture::IntelTremont
| Microarchitecture::AmdFamilyBulldozer
| Microarchitecture::AmdFamilyJaguar
| Microarchitecture::AmdFamilyZen
| Microarchitecture::AmdFamilyZen3 => false,
}
}
/// Whether the CPU is susceptible to the TSX Asynchronous Abort (TAA) bug.
///
/// CVE-2019-11135.
pub fn has_x86_taa_bug(cpuid: &impl Cpuid, msr: &impl ReadReg<ArchCapabilitiesMsr>) -> bool {
// https://software.intel.com/security-software-guidance/advisory-guidance/intel-transactional-synchronization-extensions-intel-tsx-asynchronous-abort
//
// A processor is affected by TAA if both of the following are true:
// * CPU supports TSX (indicated by the HLE or RTM features);
// * CPU does not enumerate TAA_NO.
let taa_no = ArchCapabilitiesMsr::is_supported(cpuid) && msr.read().taa_no();
if !tsx_is_supported(cpuid) || taa_no {
return false;
}
let version_info = cpuid.read(VERSION_INFO);
let vendor = VendorString::from_cpuid(cpuid).vendor();
match version_info.microarchitecture(vendor) {
Microarchitecture::Unknown
| Microarchitecture::IntelHaswell
| Microarchitecture::IntelBroadwell
| Microarchitecture::IntelSkylake
| Microarchitecture::IntelSkylakeServer
| Microarchitecture::IntelCannonLake
| Microarchitecture::IntelIceLake => true,
/* Does not implement TSX. */
Microarchitecture::IntelCore2
/* Does not implement TSX. */
| Microarchitecture::IntelNehalem
/* Does not implement TSX. */
| Microarchitecture::IntelWestmere
| Microarchitecture::IntelSandyBridge
| Microarchitecture::IntelIvyBridge
| Microarchitecture::IntelTigerLake
| Microarchitecture::IntelAlderLake
| Microarchitecture::IntelRaptorLake
| Microarchitecture::IntelBonnell
| Microarchitecture::IntelSaltwell
| Microarchitecture::IntelSilvermont
| Microarchitecture::IntelAirmont
| Microarchitecture::IntelGoldmont
| Microarchitecture::IntelGoldmontPlus
| Microarchitecture::IntelTremont
| Microarchitecture::AmdFamilyBulldozer
| Microarchitecture::AmdFamilyJaguar
| Microarchitecture::AmdFamilyZen
| Microarchitecture::AmdFamilyZen3 => false,
}
}
/// Whether the CPU is susceptible to any of the MDS or TAA bugs, which are
/// closely related and similarly mitigated.
pub fn has_x86_mds_taa_bugs(cpuid: &impl Cpuid, msr: &impl ReadReg<ArchCapabilitiesMsr>) -> bool {
has_x86_mds_bugs(cpuid, msr) || has_x86_taa_bug(cpuid, msr)
}
/// Whether the MDS/TAA bugs can be mitigated, which all make use of the same
/// method (MD_CLEAR):
/// https://software.intel.com/security-software-guidance/deep-dives/deep-dive-intel-analysis-microarchitectural-data-sampling#mitigation4processors
pub fn can_mitigate_x86_mds_taa_bugs(cpuid: &impl Cpuid) -> bool {
cpuid.read(EXTENDED_FEATURES_D).md_clear()
}
/// Whether the CPU is susceptible to the Speculative Store Bypass (SSB) bug:
/// https://software.intel.com/security-software-guidance/advisory-guidance/speculative-store-bypass
///
/// CVE-2018-3639.
pub fn has_x86_ssb_bug(cpuid: &impl Cpuid, msr: &impl ReadReg<ArchCapabilitiesMsr>) -> bool {
// Check if the processor explicitly advertises that it is not affected, in
// both the Intel and AMD ways.
if ArchCapabilitiesMsr::is_supported(cpuid) && msr.read().ssb_no() {
return false;
}
if cpuid.supports(EXTENDED_AMD_FEATURES_B) && cpuid.read(EXTENDED_AMD_FEATURES_B).ssb_no() {
return false;
}
let version_info = cpuid.read(VERSION_INFO);
let vendor = VendorString::from_cpuid(cpuid).vendor();
match version_info.microarchitecture(vendor) {
Microarchitecture::Unknown
| Microarchitecture::IntelCore2
| Microarchitecture::IntelNehalem
| Microarchitecture::IntelWestmere
| Microarchitecture::IntelSandyBridge
| Microarchitecture::IntelIvyBridge
| Microarchitecture::IntelHaswell
| Microarchitecture::IntelBroadwell
| Microarchitecture::IntelSkylake
| Microarchitecture::IntelSkylakeServer
| Microarchitecture::IntelCannonLake
| Microarchitecture::IntelIceLake
| Microarchitecture::IntelTigerLake
| Microarchitecture::IntelAlderLake
| Microarchitecture::IntelRaptorLake
| Microarchitecture::IntelGoldmont
| Microarchitecture::IntelGoldmontPlus
| Microarchitecture::IntelTremont
| Microarchitecture::AmdFamilyBulldozer
| Microarchitecture::AmdFamilyJaguar
| Microarchitecture::AmdFamilyZen
| Microarchitecture::AmdFamilyZen3 => true,
Microarchitecture::IntelBonnell
| Microarchitecture::IntelSaltwell
| Microarchitecture::IntelSilvermont
| Microarchitecture::IntelAirmont => false,
}
}
/// Whether the CPU is susceptible to the Rogue Data Cache Load (Meltdown) bug:
/// https://software.intel.com/security-software-guidance/advisory-guidance/rogue-data-cache-load.
///
/// CVE-2017-5754.
pub fn has_x86_meltdown_bug(cpuid: &impl Cpuid, msr: &impl ReadReg<ArchCapabilitiesMsr>) -> bool {
// Check if the processor explicitly advertises that it is not affected.
if ArchCapabilitiesMsr::is_supported(cpuid) && msr.read().rdcl_no() {
return false;
}
let version_info = cpuid.read(VERSION_INFO);
let vendor = VendorString::from_cpuid(cpuid).vendor();
let march = version_info.microarchitecture(vendor);
match march {
Microarchitecture::Unknown
| Microarchitecture::IntelCore2
| Microarchitecture::IntelNehalem
| Microarchitecture::IntelWestmere
| Microarchitecture::IntelSandyBridge
| Microarchitecture::IntelIvyBridge
| Microarchitecture::IntelHaswell
| Microarchitecture::IntelBroadwell
| Microarchitecture::IntelSkylake
| Microarchitecture::IntelCannonLake => true,
Microarchitecture::IntelIceLake
| Microarchitecture::IntelTigerLake
| Microarchitecture::IntelAlderLake
| Microarchitecture::IntelRaptorLake
| Microarchitecture::IntelBonnell
| Microarchitecture::IntelSaltwell
| Microarchitecture::IntelSilvermont
| Microarchitecture::IntelAirmont
| Microarchitecture::IntelGoldmont
| Microarchitecture::IntelTremont
| Microarchitecture::AmdFamilyBulldozer
| Microarchitecture::AmdFamilyJaguar
| Microarchitecture::AmdFamilyZen
| Microarchitecture::AmdFamilyZen3 => false,
// Special cases from the above table.
Microarchitecture::IntelSkylakeServer => {
let info = cpuid.read(VERSION_INFO);
if info.stepping() >= 0x6 {
// Cascade Lake server+
return false;
}
true // Skylake server
}
Microarchitecture::IntelGoldmontPlus => {
let info = cpuid.read(VERSION_INFO);
if info.stepping() == 0x1 {
// First stepping was suceptable to Meltdown.
return true;
}
false
}
}
}
/// An architecturally prescribed mitigation for Spectre v2.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum SpectreV2Mitigation {
/// Enhanced/always-on IBRS (i.e., IBRS that can be enabled once without
/// automatic disabling) is preferred alone.
Ibrs,
/// IBPB and/or retpoline are recommended.
IbpbRetpoline,
/// IBPB and/or retpoline are recommended - and STIPB, though not preferred as
/// a performant mitigation, is also present and may be used.
IbpbRetpolineStibp,
}
/// Returns the preferred Spectre v2 mitigation strategy.
pub fn get_preferred_spectre_v2_mitigation(
cpuid: &impl Cpuid,
msr: &impl ReadReg<ArchCapabilitiesMsr>,
) -> SpectreV2Mitigation {
let vendor = VendorString::from_cpuid(cpuid).vendor();
match vendor {
Vendor::Unknown => {}
Vendor::Intel => {
// https://software.intel.com/security-software-guidance/advisory-guidance/branch-target-injection
//
// If enhanced IBRS are supported, it should be used for mitigation
// instead of retpoline; else retpoline
if has_ibrs(cpuid, msr, /*always_on_mode=*/ true) {
return SpectreV2Mitigation::Ibrs;
}
}
Vendor::Amd => {
// [amd/ibc]: EXTENDED USAGE MODELS.
// AMD further offers a feature bit to indicate whether IBRS is a
// preferred mitigation strategy.
if has_ibrs(cpuid, msr, /*always_on_mode=*/ true)
&& cpuid.supports(EXTENDED_AMD_FEATURES_B)
&& cpuid.read(EXTENDED_AMD_FEATURES_B).prefers_ibrs()
{
return SpectreV2Mitigation::Ibrs;
}
// [amd/ibc]: USAGE.
// Though not recommended, STIPB is still a viable mitigation strategy.
if has_stibp(cpuid, /*always_on_mode=*/ true) {
return SpectreV2Mitigation::IbpbRetpolineStibp;
}
}
}
// Retpolines comprise an architecturally agnostic, pure software solution,
// which makes it a sensible default strategy.
SpectreV2Mitigation::IbpbRetpoline
}
/// [amd/ssbd] references bits 10, 33, 54.
/// [amd/rg/17h/00h-0Fh] references bits 4, 57.
pub const AMD_LOAD_STORE_CONFIGURATION: Msr<0xc001_1020, AmdLoadStoreConfigurationMsr, RwSafe> =
Msr::new();
layout!({
/// Layout for [`AMD_LOAD_STORE_CONFIGURATION`].
pub struct AmdLoadStoreConfigurationMsr(u64);
{
let __ @ 63..58;
let erratum_1095_workaround @ 57;
let __ @ 56..55;
let ssbd_15h @ 54;
let __ @ 53..34;
let ssbd_16h @ 33;
let __ @ 32..11;
let ssbd_17h @ 10;
let __ @ 9..5;
let erratum_1033_workaround @ 4;
let __ @ 3..0;
}
});
/// [amd/rg/17h/00h-0Fh] references bit 4.
pub const AMD_C0011028: Msr<0xc001_1028, AmdC0011028Msr, RwSafe> = Msr::new();
layout!({
/// Layout for [`AMD_C0011028`].
pub struct AmdC0011028Msr(u64);
{
let __ @ 63..5;
let erratum_1049_workaround @ 4;
let __ @ 3..0;
}
});
/// [amd/rg/17h/00h-0Fh] references bit 13.
pub const AMD_C0011029: Msr<0xc001_1029, AmdC0011029Msr, RwSafe> = Msr::new();
layout!({
/// Layout for [`AMD_C0011029`].
pub struct AmdC0011029Msr(u64);
{
let __ @ 63..14;
let erratum_1021_workaround @ 13;
let __ @ 12..0;
}
});
/// [amd/rg/17h/00h-0Fh] references bit 34.
pub const AMD_C001102D: Msr<0xc001_102d, AmdC001102dMsr, RwSafe> = Msr::new();
layout!({
/// Layout for [`AMD_C001102d`].
pub struct AmdC001102dMsr(u64);
{
let __ @ 63..35;
let erratum_1091_workaround @ 34;
let __ @ 33..0;
}
});
/// Attempt to mitigate the SSB bug. Return true if the bug was successfully
/// mitigated.
pub fn mitigate_x86_ssb_bug(
cpuid: &impl Cpuid,
speculation: &impl RwSafeReg<SpeculationControlMsr>,
virtual_speculation: &impl RwSafeReg<VirtualSpeculationControlMsr>,
load_store_configuration: &impl RwSafeReg<AmdLoadStoreConfigurationMsr>,
) -> bool {
if cpuid.read(EXTENDED_FEATURES_D).ssbd() {
debug_assert!(SpeculationControlMsr::is_supported(cpuid));
speculation.modify(|value| *value.set_ssbd(true));
return true;
}
if cpuid.supports(EXTENDED_AMD_FEATURES_B) {
let amd_features = cpuid.read(EXTENDED_AMD_FEATURES_B);
if amd_features.ssbd() {
debug_assert!(SpeculationControlMsr::is_supported(cpuid));
speculation.modify(|value| *value.set_ssbd(true));
return true;
}
if amd_features.virt_ssbd() {
debug_assert!(VirtualSpeculationControlMsr::is_supported(cpuid));
virtual_speculation.modify(|value| *value.set_ssbd(true));
return true;
}
}
// [amd/ssbd]: NON-ARCHITECTURAL MSRS.
//
// There are non-architectural mechanisms to disable SSB for AMD families
// 0x15-0x17.
let version_info = cpuid.read(VERSION_INFO);
let vendor = VendorString::from_cpuid(cpuid).vendor();
match version_info.microarchitecture(vendor) {
Microarchitecture::AmdFamilyBulldozer => {
load_store_configuration.modify(|value| *value.set_ssbd_15h(true));
true
}
Microarchitecture::AmdFamilyJaguar => {
load_store_configuration.modify(|value| *value.set_ssbd_16h(true));
true
}
Microarchitecture::AmdFamilyZen => {
load_store_configuration.modify(|value| *value.set_ssbd_17h(true));
true
}
_ => false,
}
}
pub fn can_mitigate_x86_ssb_bug(cpuid: &impl Cpuid) -> bool {
// With a null I/O provider, we can make the requisite checks without
// actually committing the writes.
struct NullMsr;
impl<Layout: LayoutOver<u64>> ReadReg<Layout> for NullMsr {
fn read(&self) -> Layout {
Layout::from(0u64)
}
}
impl<Layout> SafeWriteReg<Layout> for NullMsr {
fn write(&self, _value: Layout) {}
}
mitigate_x86_ssb_bug(cpuid, &NullMsr, &NullMsr, &NullMsr)
}
/// Applies workarounds to processor-specific errata.
pub fn apply_x86_errata_workarounds(
cpuid: &impl Cpuid,
c0011028: &impl RwSafeReg<AmdC0011028Msr>,
c0011029: &impl RwSafeReg<AmdC0011029Msr>,
c001102d: &impl RwSafeReg<AmdC001102dMsr>,
load_store: &impl RwSafeReg<AmdLoadStoreConfigurationMsr>,
) {
if cpuid.read(FEATURE_FLAGS_C).hypervisor() {
return;
}
let vendor = VendorString::from_cpuid(cpuid).vendor();
match vendor {
Vendor::Unknown => {}
Vendor::Intel => {}
Vendor::Amd => {
let info = cpuid.read(VERSION_INFO);
#[expect(clippy::single_match)]
match info.family() {
0x17 => {
#[expect(clippy::single_match)]
match info.model() {
// [amd/rg/17h/00h-0Fh].
0x00..=0x0f => {
// ZP-B1 refers to (model, stepping) == (1, 1); some of the errata are
// detailed as only applying to that CPU.
let zp_b1 = info.model() == 1 && info.stepping() == 1;
// 1021: Load Operation May Receive Stale Data From Older Store
// Operation.
c0011029.modify(|value| *value.set_erratum_1021_workaround(true));
let mut lscfg = load_store.read();
// 1033: A Lock Operation May Cause the System to Hang.
if zp_b1 {
lscfg.set_erratum_1033_workaround(true);
}
// 1095: Potential Violation of Read Ordering In Lock Operation in SMT
// Mode.
if true {
// TODO(https://fxbug.dev/42113091): Do not apply if SMT is
// disabled.
lscfg.set_erratum_1095_workaround(true);
}
load_store.write(lscfg);
// 1049: FCMOV Instruction May Not Execute Correctly.
c0011028.modify(|value| *value.set_erratum_1049_workaround(true));
// 1091: 4K Address Boundary Crossing Load Operation May Receive Stale
// Data.
c001102d.modify(|value| *value.set_erratum_1091_workaround(true));
}
_ => {}
}
}
_ => {}
}
}
}
}