blob: 50a2dc7061ac820d1a82889858f0199b45572f7d [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#ifndef ZIRCON_KERNEL_LIB_ARCH_INCLUDE_LIB_ARCH_X86_BUG_H_
#define ZIRCON_KERNEL_LIB_ARCH_INCLUDE_LIB_ARCH_X86_BUG_H_
#include <lib/arch/x86/cpuid.h>
#include <lib/arch/x86/extension.h>
#include <lib/arch/x86/feature.h>
#include <lib/arch/x86/speculation.h>
// 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".
namespace arch {
namespace internal {
// A trivial MSR I/O provider that can be used in instances in which we do not
// wish MSR writes to take effect but still wish to observe the result (e.g., a
// dry-run context of sorts).
struct NullMsrIo {
template <typename IntType>
void Write(IntType value, uint32_t msr) const {}
template <typename IntType>
IntType Read(uint32_t msr) const {
return 0;
}
};
} // namespace internal
// [amd/ssbd] references bits 10, 33, 54.
// [amd/rg/17h/00h-0Fh] references bits 4, 57.
//
// MSRC001_1020.
struct AmdLoadStoreConfigurationMsr
: public X86MsrBase<AmdLoadStoreConfigurationMsr, X86Msr::MSRC001_1020> {
// Bits [63:58] are reserved/unknown.
DEF_BIT(57, erratum_1095_workaround);
// Bits [56:55] are reserved/unknown.
DEF_BIT(54, ssbd_15h);
// Bits [53:34] are reserved/unknown.
DEF_BIT(33, ssbd_16h);
// Bits [32:11] are reserved/unknown.
DEF_BIT(10, ssbd_17h);
// Bits [9:5] are reserved/unknown.
DEF_BIT(4, erratum_1033_workaround);
// Bits [3:0] are reserved/unknown.
};
// [amd/rg/17h/00h-0Fh] references bit 4.
//
// MSRC001_1028.
struct AmdC0011028Msr : public X86MsrBase<AmdC0011028Msr, X86Msr::MSRC001_1028> {
// Bits [63:5] are reserved/unknown.
DEF_BIT(4, erratum_1049_workaround);
// Bits [3:0] are reserved/unknown.
};
// [amd/rg/17h/00h-0Fh] references bit 13.
//
// MSRC001_1029.
struct AmdC0011029Msr : public X86MsrBase<AmdC0011029Msr, X86Msr::MSRC001_1029> {
// Bits [63:14] are reserved/unknown.
DEF_BIT(13, erratum_1021_workaround);
// Bits [12:0] are reserved/unknown.
};
// [amd/rg/17h/00h-0Fh] references bit 34.
//
// MSRC001_102D.
struct AmdC001102dMsr : public X86MsrBase<AmdC001102dMsr, X86Msr::MSRC001_102D> {
// Bits [63:35] are reserved/unknown.
DEF_BIT(34, erratum_1091_workaround);
// Bits [33:0] are reserved/unknown.
};
// 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.
template <typename CpuidIoProvider>
inline bool HasX86SwapgsBug(CpuidIoProvider&& cpuid) {
switch (arch::GetVendor(cpuid)) {
case arch::Vendor::kUnknown:
// All Intel CPUs seem to be affected and there is no indication that they
// intend to fix this.
case arch::Vendor::kIntel:
return true;
case arch::Vendor::kAmd:
return false;
}
return 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.
template <typename CpuidIoProvider, typename MsrIoProvider>
inline bool HasX86MdsBugs(CpuidIoProvider&& cpuid, MsrIoProvider&& msr) {
// https://software.intel.com/security-software-guidance/resources/processors-affected-microarchitectural-data-sampling
if (ArchCapabilitiesMsr::IsSupported(cpuid) &&
ArchCapabilitiesMsr::Get().ReadFrom(&msr).mds_no()) {
return false;
}
switch (GetMicroarchitecture(cpuid)) {
case Microarchitecture::kUnknown:
case Microarchitecture::kIntelCore2:
case Microarchitecture::kIntelNehalem:
case Microarchitecture::kIntelWestmere:
case Microarchitecture::kIntelSandyBridge:
case Microarchitecture::kIntelIvyBridge:
case Microarchitecture::kIntelHaswell:
case Microarchitecture::kIntelBroadwell:
case Microarchitecture::kIntelSkylake:
case Microarchitecture::kIntelSkylakeServer:
case Microarchitecture::kIntelCannonLake:
case Microarchitecture::kIntelIceLake:
case Microarchitecture::kIntelSilvermont:
case Microarchitecture::kIntelAirmont:
return true;
case Microarchitecture::kIntelTigerLake:
case Microarchitecture::kIntelBonnell:
case Microarchitecture::kIntelGoldmont:
case Microarchitecture::kIntelGoldmontPlus:
case Microarchitecture::kIntelTremont:
case Microarchitecture::kAmdFamilyBulldozer:
case Microarchitecture::kAmdFamilyJaguar:
case Microarchitecture::kAmdFamilyZen:
case Microarchitecture::kAmdFamilyZen3:
return false;
}
return true;
}
// Whether the CPU is susceptible to the TSX Asynchronous Abort (TAA) bug.
//
// CVE-2019-11135.
template <typename CpuidIoProvider, typename MsrIoProvider>
inline bool HasX86TaaBug(CpuidIoProvider&& cpuid, MsrIoProvider&& msr) {
// 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.
const bool taa_no =
ArchCapabilitiesMsr::IsSupported(cpuid) && ArchCapabilitiesMsr::Get().ReadFrom(&msr).taa_no();
if (!TsxIsSupported(cpuid) || taa_no) {
return false;
}
switch (GetMicroarchitecture(cpuid)) {
case Microarchitecture::kUnknown:
case Microarchitecture::kIntelHaswell:
case Microarchitecture::kIntelBroadwell:
case Microarchitecture::kIntelSkylake:
case Microarchitecture::kIntelSkylakeServer:
case Microarchitecture::kIntelCannonLake:
case Microarchitecture::kIntelIceLake:
return true;
case Microarchitecture::kIntelCore2: // Does not implement TSX.
case Microarchitecture::kIntelNehalem: // Does not implement TSX.
case Microarchitecture::kIntelWestmere: // Does not implement TSX.
case Microarchitecture::kIntelSandyBridge:
case Microarchitecture::kIntelIvyBridge:
case Microarchitecture::kIntelTigerLake:
case Microarchitecture::kIntelBonnell:
case Microarchitecture::kIntelSilvermont:
case Microarchitecture::kIntelAirmont:
case Microarchitecture::kIntelGoldmont:
case Microarchitecture::kIntelGoldmontPlus:
case Microarchitecture::kIntelTremont:
case Microarchitecture::kAmdFamilyBulldozer:
case Microarchitecture::kAmdFamilyJaguar:
case Microarchitecture::kAmdFamilyZen:
case Microarchitecture::kAmdFamilyZen3:
return false;
}
return true;
}
// Whether the CPU is susceptible to any of the MDS or TAA bugs, which are
// closely related and similarly mitigated.
template <typename CpuidIoProvider, typename MsrIoProvider>
inline bool HasX86MdsTaaBugs(CpuidIoProvider&& cpuid, MsrIoProvider&& msr) {
return HasX86MdsBugs(cpuid, msr) || HasX86TaaBug(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
template <typename CpuidIoProvider>
inline bool CanMitigateX86MdsTaaBugs(CpuidIoProvider&& cpuid) {
return cpuid.template Read<CpuidExtendedFeatureFlagsD>().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.
template <typename CpuidIoProvider, typename MsrIoProvider>
inline bool HasX86SsbBug(CpuidIoProvider&& cpuid, MsrIoProvider&& msr) {
// Check if the processor explicitly advertises that it is not affected, in
// both the Intel and AMD ways.
if (ArchCapabilitiesMsr::IsSupported(cpuid) &&
ArchCapabilitiesMsr::Get().ReadFrom(&msr).ssb_no()) {
return false;
}
if (CpuidSupports<CpuidExtendedAmdFeatureFlagsB>(cpuid) &&
cpuid.template Read<CpuidExtendedAmdFeatureFlagsB>().ssb_no()) {
return false;
}
switch (GetMicroarchitecture(cpuid)) {
case Microarchitecture::kUnknown:
case Microarchitecture::kIntelCore2:
case Microarchitecture::kIntelNehalem:
case Microarchitecture::kIntelWestmere:
case Microarchitecture::kIntelSandyBridge:
case Microarchitecture::kIntelIvyBridge:
case Microarchitecture::kIntelHaswell:
case Microarchitecture::kIntelBroadwell:
case Microarchitecture::kIntelSkylake:
case Microarchitecture::kIntelSkylakeServer:
case Microarchitecture::kIntelCannonLake:
case Microarchitecture::kIntelIceLake:
case Microarchitecture::kIntelTigerLake:
case Microarchitecture::kIntelBonnell:
case Microarchitecture::kIntelGoldmont:
case Microarchitecture::kIntelGoldmontPlus:
case Microarchitecture::kIntelTremont:
case Microarchitecture::kAmdFamilyBulldozer:
case Microarchitecture::kAmdFamilyJaguar:
case Microarchitecture::kAmdFamilyZen:
case Microarchitecture::kAmdFamilyZen3:
return true;
case Microarchitecture::kIntelSilvermont:
case Microarchitecture::kIntelAirmont:
break;
}
return false;
}
// Attempt to mitigate the SSB bug. Return true if the bug was successfully
// mitigated.
template <typename CpuidIoProvider, typename MsrIoProvider>
inline bool MitigateX86SsbBug(CpuidIoProvider&& cpuid, MsrIoProvider&& msr) {
if (cpuid.template Read<CpuidExtendedFeatureFlagsD>().ssbd()) {
ZX_DEBUG_ASSERT(SpeculationControlMsr::IsSupported(cpuid));
SpeculationControlMsr::Get().ReadFrom(&msr).set_ssbd(1).WriteTo(&msr);
return true;
}
if (CpuidSupports<CpuidExtendedAmdFeatureFlagsB>(cpuid)) {
const auto amd_features = cpuid.template Read<CpuidExtendedAmdFeatureFlagsB>();
if (amd_features.ssbd()) {
ZX_DEBUG_ASSERT(SpeculationControlMsr::IsSupported(cpuid));
SpeculationControlMsr::Get().ReadFrom(&msr).set_ssbd(1).WriteTo(&msr);
return true;
}
if (amd_features.virt_ssbd()) {
ZX_DEBUG_ASSERT(AmdVirtualSpeculationControlMsr::IsSupported(cpuid));
AmdVirtualSpeculationControlMsr::Get().ReadFrom(&msr).set_ssbd(1).WriteTo(&msr);
return true;
}
}
// [amd/ssbd]: NON-ARCHITECTURAL MSRS.
//
// There are non-architectural mechanisms to disable SSB for AMD families
// 0x15-0x17.
switch (GetMicroarchitecture(cpuid)) {
case arch::Microarchitecture::kAmdFamilyBulldozer:
AmdLoadStoreConfigurationMsr::Get().ReadFrom(&msr).set_ssbd_15h(1).WriteTo(&msr);
return true;
case arch::Microarchitecture::kAmdFamilyJaguar:
AmdLoadStoreConfigurationMsr::Get().ReadFrom(&msr).set_ssbd_16h(1).WriteTo(&msr);
return true;
case arch::Microarchitecture::kAmdFamilyZen:
AmdLoadStoreConfigurationMsr::Get().ReadFrom(&msr).set_ssbd_17h(1).WriteTo(&msr);
return true;
default:
break;
}
return false;
}
template <typename CpuidIoProvider>
inline bool CanMitigateX86SsbBug(CpuidIoProvider&& cpuid) {
// With a null I/O provider, we can make the requisite checks without
// actually committing the writes.
return MitigateX86SsbBug(cpuid, internal::NullMsrIo{});
}
// 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.
template <typename CpuidIoProvider, typename MsrIoProvider>
inline bool HasX86MeltdownBug(CpuidIoProvider&& cpuid, MsrIoProvider&& msr) {
// Check if the processor explicitly advertises that it is not affected.
if (ArchCapabilitiesMsr::IsSupported(cpuid) &&
ArchCapabilitiesMsr::Get().ReadFrom(&msr).rdcl_no()) {
return false;
}
switch (GetMicroarchitecture(cpuid)) {
case Microarchitecture::kUnknown:
case Microarchitecture::kIntelCore2:
case Microarchitecture::kIntelNehalem:
case Microarchitecture::kIntelWestmere:
case Microarchitecture::kIntelSandyBridge:
case Microarchitecture::kIntelIvyBridge:
case Microarchitecture::kIntelHaswell:
case Microarchitecture::kIntelBroadwell:
case Microarchitecture::kIntelSkylake:
case Microarchitecture::kIntelCannonLake:
case Microarchitecture::kIntelBonnell:
case Microarchitecture::kIntelSilvermont:
case Microarchitecture::kIntelAirmont:
return true;
case Microarchitecture::kIntelSkylakeServer:
case Microarchitecture::kIntelIceLake:
case Microarchitecture::kIntelTigerLake:
case Microarchitecture::kIntelGoldmont:
case Microarchitecture::kIntelGoldmontPlus:
case Microarchitecture::kIntelTremont:
case Microarchitecture::kAmdFamilyBulldozer:
case Microarchitecture::kAmdFamilyJaguar:
case Microarchitecture::kAmdFamilyZen:
case Microarchitecture::kAmdFamilyZen3:
break;
}
return false;
}
// Whether the CPU is susceptible to the L1 Terminal Fault (L1TF) bug:
// https://software.intel.com/security-software-guidance/advisory-guidance/l1-terminal-fault.
//
// CVE-2018-3615, CVE-2018-3620, CVE-2018-3646.
template <typename CpuidIoProvider, typename MsrIoProvider>
inline bool HasX86L1tfBug(CpuidIoProvider&& cpuid, MsrIoProvider&& msr) {
// Advertisement of RDCL_NO also implies non-susceptibility to L1TF:
// https://software.intel.com/security-software-guidance/deep-dives/deep-dive-intel-analysis-l1-terminal-fault.
if (ArchCapabilitiesMsr::IsSupported(cpuid) &&
ArchCapabilitiesMsr::Get().ReadFrom(&msr).rdcl_no()) {
return false;
}
switch (GetMicroarchitecture(cpuid)) {
case Microarchitecture::kUnknown:
case Microarchitecture::kIntelCore2:
case Microarchitecture::kIntelNehalem:
case Microarchitecture::kIntelWestmere:
case Microarchitecture::kIntelSandyBridge:
case Microarchitecture::kIntelIvyBridge:
case Microarchitecture::kIntelHaswell:
case Microarchitecture::kIntelBroadwell:
case Microarchitecture::kIntelSkylake:
case Microarchitecture::kIntelCannonLake:
case Microarchitecture::kIntelIceLake:
case Microarchitecture::kIntelBonnell:
return true;
case Microarchitecture::kIntelSkylakeServer:
case Microarchitecture::kIntelTigerLake:
case Microarchitecture::kIntelSilvermont:
case Microarchitecture::kIntelAirmont:
case Microarchitecture::kIntelGoldmont:
case Microarchitecture::kIntelGoldmontPlus:
case Microarchitecture::kIntelTremont:
case Microarchitecture::kAmdFamilyBulldozer:
case Microarchitecture::kAmdFamilyJaguar:
case Microarchitecture::kAmdFamilyZen:
case Microarchitecture::kAmdFamilyZen3:
break;
}
return false;
}
// An architecturally prescribed mitigation for Spectre v2.
enum class SpectreV2Mitigation {
// Enhanced/always-on IBRS (i.e., IBRS that can be enabled once without
// automatic disabling) is preferred alone.
kIbrs,
// IBPB and/or retpoline are recommended.
kIbpbRetpoline,
// IBPB and/or retpoline are recommended - and STIPB, though not preferred as
// a performant mitigation, is also present and may be used.
kIbpbRetpolineStibp,
};
// Returns the preferred Spectre v2 mitigation strategy.
template <typename CpuidIoProvider, typename MsrIoProvider>
inline SpectreV2Mitigation GetPreferredSpectreV2Mitigation(CpuidIoProvider&& cpuid,
MsrIoProvider&& msr) {
switch (GetVendor(cpuid)) {
case Vendor::kUnknown:
break;
case Vendor::kIntel:
// 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 (HasIbrs(cpuid, msr, /*always_on_mode=*/true)) {
return SpectreV2Mitigation::kIbrs;
}
break;
case Vendor::kAmd: {
// [amd/ibc]: EXTENDED USAGE MODELS.
// AMD further offers a feature bit to indicate whether IBRS is a
// preffered mitigation strategy.
if (HasIbrs(cpuid, msr, /*always_on_mode=*/true) &&
CpuidSupports<CpuidExtendedAmdFeatureFlagsB>(cpuid) &&
cpuid.template Read<CpuidExtendedAmdFeatureFlagsB>().prefers_ibrs()) {
return SpectreV2Mitigation::kIbrs;
}
// [amd/ibc]: USAGE.
// Though not recommended, STIPB is still a viable mitigation strategy.
if (HasStibp(cpuid, /*always_on_mode=*/true)) {
return SpectreV2Mitigation::kIbpbRetpolineStibp;
}
break;
}
}
// Retpolines comprise an architecturally agnostic, pure software solution,
// which makes it a sensible default strategy.
return SpectreV2Mitigation::kIbpbRetpoline;
}
// Applies workarounds to processor-specific errata.
template <typename CpuidIoProvider, typename MsrIoProvider>
inline void ApplyX86ErrataWorkarounds(CpuidIoProvider&& cpuid, MsrIoProvider&& msr) {
if (cpuid.template Read<CpuidFeatureFlagsC>().hypervisor()) {
return;
}
switch (arch::GetVendor(cpuid)) {
case Vendor::kUnknown:
break;
case Vendor::kIntel:
break;
case Vendor::kAmd: {
const auto info = cpuid.template Read<CpuidVersionInfo>();
switch (info.family()) {
case 0x17: {
switch (info.model()) {
// [amd/rg/17h/00h-0Fh].
case 0x00 ... 0x0f: {
// ZP-B1 refers to (model, stepping) == (1, 1); some of the errata
// are detailed as only applying to that CPU.
const bool zp_b1 = info.model() == 1 && info.stepping() == 1;
// 1021: Load Operation May Receive Stale Data From Older Store Operation.
AmdC0011029Msr::Get().ReadFrom(&msr).set_erratum_1021_workaround(1).WriteTo(&msr);
auto lscfg = AmdLoadStoreConfigurationMsr::Get().ReadFrom(&msr);
// 1033: A Lock Operation May Cause the System to Hang.
if (zp_b1) {
lscfg.set_erratum_1033_workaround(1);
}
// 1095: Potential Violation of Read Ordering In Lock Operation in SMT Mode.
if (true) { // TODO(fxbug.dev/37450): Do not apply if SMT is disabled.
lscfg.set_erratum_1095_workaround(1);
}
lscfg.WriteTo(&msr);
// 1049: FCMOV Instruction May Not Execute Correctly.
AmdC0011028Msr::Get().ReadFrom(&msr).set_erratum_1049_workaround(1).WriteTo(&msr);
// 1091: 4K Address Boundary Crossing Load Operation May Receive Stale Data.
AmdC001102dMsr::Get().ReadFrom(&msr).set_erratum_1091_workaround(1).WriteTo(&msr);
break;
}
}
break;
}
}
break;
}
}
}
} // namespace arch
#endif // ZIRCON_KERNEL_LIB_ARCH_INCLUDE_LIB_ARCH_X86_BUG_H_