Files
spectre-meltdown-checker/src/libs/400_hw_check.sh
T
Stéphane Lesimple cb48e1ace8 fix: mmio: don't report "Intel never assessed this CPU" when the MSR is unreadable
When IA32_ARCH_CAPABILITIES (0x10a) can't be read from userspace (no msr
module, or kernel lockdown under Secure Boot), the FBSDP_NO/PSDP_NO/SBDR_SSDP_NO
bits were left at 0 ("explicitly not immune") instead of -1 ("unknown"). For a
recent CPU not in any kernel model list (e.g. Arrow Lake), this wrongly flipped
the MMIO Stale Data verdict into the "out of servicing period, Intel never
assessed this CPU" bucket.

(cherry picked from commit 23ea5427b5)
2026-08-08 17:23:47 +00:00

1597 lines
61 KiB
Bash

# vim: set ts=4 sw=4 sts=4 et:
# ENTRYPOINT
# we can't do anything useful under WSL
if uname -a | grep -qE -- '-Microsoft #[0-9]+-Microsoft '; then
pr_warn "This script doesn't work under Windows Subsystem for Linux"
pr_warn "You should use the official Microsoft tool instead."
pr_warn "It can be found under https://aka.ms/SpeculationControlPS"
exit 1
fi
# or other UNIX-ish OSes non-Linux non-supported-BSDs
if [ "$g_os" = Darwin ] || [ "$g_os" = VMkernel ]; then
pr_warn "You're running under the $g_os OS, but this script"
pr_warn "only works under Linux and some BSD systems, sorry."
pr_warn "Please read the README and FAQ for more information."
exit 1
fi
# check for mode selection inconsistency
if [ "$g_mode" = hw-only ]; then
if [ "$opt_cve_all" = 0 ]; then
show_usage
echo "$0: error: incompatible modes specified, --hw-only vs --variant" >&2
exit 255
else
opt_cve_all=0
opt_cve_list=''
fi
fi
# coreos mode
if [ "$opt_coreos" = 1 ]; then
if ! is_coreos; then
pr_warn "CoreOS mode asked, but we're not under CoreOS!"
exit 255
fi
pr_warn "CoreOS mode, starting an ephemeral toolbox to launch the script"
load_msr
load_cpuid
mount_debugfs
toolbox --ephemeral --bind-ro "$CPU_DEV_BASE:$CPU_DEV_BASE" -- sh -c "dnf install -y binutils which && /media/root$PWD/$0 $* --coreos-within-toolbox"
g_exitcode=$?
exit $g_exitcode
else
if is_coreos; then
pr_warn "You seem to be running CoreOS, you might want to use the --coreos option for better results"
pr_warn
fi
fi
# if we're under a BSD, try to mount linprocfs for "$g_procfs/cpuinfo"
g_procfs=/proc
if echo "$g_os" | grep -q BSD; then
pr_debug "We're under BSD, check if we have g_procfs"
g_procfs=$(mount | awk '/^linprocfs/ { print $3; exit; }')
if [ -z "$g_procfs" ]; then
pr_debug "we don't, try to mount it"
g_procfs=/proc
[ -d /compat/linux/proc ] && g_procfs=/compat/linux/proc
test -d $g_procfs || mkdir $g_procfs
if mount -t linprocfs linprocfs $g_procfs 2>/dev/null; then
g_mounted_procfs=1
pr_debug "g_procfs just mounted at $g_procfs"
else
g_procfs=''
fi
else
pr_debug "We do: $g_procfs"
fi
fi
# define a few vars we might reference later without these being inited
g_mockme=''
g_mocked=0
g_specex_knob_dir=/dev/no_valid_path
# if /tmp doesn't exist and TMPDIR is not set, try to set it to a sane default for Android
if [ -z "${TMPDIR:-}" ] && ! [ -d "/tmp" ] && [ -d "/data/local/tmp" ]; then
TMPDIR=/data/local/tmp
export TMPDIR
fi
parse_cpu_details
get_cmdline
if [ "$opt_cpu" != all ] && [ "$opt_cpu" -gt "$g_max_core_id" ]; then
echo "$0: error: --cpu can't be higher than $g_max_core_id, got $opt_cpu" >&2
exit 255
fi
if has_runtime; then
pr_info "Checking for vulnerabilities on current system"
# try to find the image of the current running kernel
if [ -n "$opt_kernel" ]; then
# specified by user on cmdline, with --live, don't override
:
# first, look for the BOOT_IMAGE hint in the kernel cmdline
elif echo "$g_kernel_cmdline" | grep -q 'BOOT_IMAGE='; then
opt_kernel=$(echo "$g_kernel_cmdline" | grep -Eo 'BOOT_IMAGE=[^ ]+' | cut -d= -f2)
pr_debug "found opt_kernel=$opt_kernel in $g_procfs/cmdline"
# if the boot partition is within a btrfs subvolume, strip the subvolume name
# if /boot is a separate subvolume, the remainder of the code in this section should handle it
if echo "$opt_kernel" | grep -q "^/@"; then opt_kernel=$(echo "$opt_kernel" | sed "s:/@[^/]*::"); fi
# if we have a dedicated /boot partition, our bootloader might have just called it /
# so try to prepend /boot and see if we find anything
[ -e "/boot/$opt_kernel" ] && opt_kernel="/boot/$opt_kernel"
# special case for CoreOS if we're inside the toolbox
[ -e "/media/root/boot/$opt_kernel" ] && opt_kernel="/media/root/boot/$opt_kernel"
pr_debug "opt_kernel is now $opt_kernel"
# else, the full path is already there (most probably /boot/something)
fi
# if we didn't find a kernel, default to guessing
if [ ! -e "$opt_kernel" ]; then
# Fedora:
[ -e "/lib/modules/$(uname -r)/vmlinuz" ] && opt_kernel="/lib/modules/$(uname -r)/vmlinuz"
# Slackware:
[ -e "/boot/vmlinuz" ] && opt_kernel="/boot/vmlinuz"
# Arch aarch64:
[ -e "/boot/Image" ] && opt_kernel="/boot/Image"
# Arch armv5/armv7:
[ -e "/boot/zImage" ] && opt_kernel="/boot/zImage"
# Arch arm7:
[ -e "/boot/kernel7.img" ] && opt_kernel="/boot/kernel7.img"
# Linux-Libre:
[ -e "/boot/vmlinuz-linux-libre" ] && opt_kernel="/boot/vmlinuz-linux-libre"
# pine64
[ -e "/boot/pine64/Image" ] && opt_kernel="/boot/pine64/Image"
# generic:
[ -e "/boot/vmlinuz-$(uname -r)" ] && opt_kernel="/boot/vmlinuz-$(uname -r)"
[ -e "/boot/kernel-$(uname -r)" ] && opt_kernel="/boot/kernel-$(uname -r)"
[ -e "/boot/bzImage-$(uname -r)" ] && opt_kernel="/boot/bzImage-$(uname -r)"
# Gentoo:
[ -e "/boot/kernel-genkernel-$(uname -m)-$(uname -r)" ] && opt_kernel="/boot/kernel-genkernel-$(uname -m)-$(uname -r)"
# NixOS:
[ -e "/run/booted-system/kernel" ] && opt_kernel="/run/booted-system/kernel"
# Guix System:
[ -e "/run/booted-system/kernel/bzImage" ] && opt_kernel="/run/booted-system/kernel/bzImage"
# systemd kernel-install:
[ -e "/etc/machine-id" ] && [ -e "/boot/$(cat /etc/machine-id)/$(uname -r)/linux" ] && opt_kernel="/boot/$(cat /etc/machine-id)/$(uname -r)/linux"
# Clear Linux:
g_str_uname=$(uname -r)
g_clear_linux_kernel="/lib/kernel/org.clearlinux.${g_str_uname##*.}.${g_str_uname%.*}"
[ -e "$g_clear_linux_kernel" ] && opt_kernel=$g_clear_linux_kernel
# Custom Arch seems to have the kernel path in its cmdline in the form "\directory\kernelimage",
# with actual \'s instead of /'s:
g_custom_arch_kernel=$(echo "$g_kernel_cmdline" | grep -Eo "(^|\s)\\\\[\\\\a-zA-Z0-9_.-]+" | tr "\\\\" "/" | tr -d '[:space:]')
if [ -n "$g_custom_arch_kernel" ] && [ -e "$g_custom_arch_kernel" ]; then
opt_kernel="$g_custom_arch_kernel"
fi
# FreeBSD:
[ -e "/boot/kernel/kernel" ] && opt_kernel="/boot/kernel/kernel"
fi
# system.map
if [ -n "$opt_map" ]; then
# specified by user on cmdline, with --live, don't override
:
elif [ -e "$g_procfs/kallsyms" ]; then
opt_map="$g_procfs/kallsyms"
elif [ -e "/lib/modules/$(uname -r)/System.map" ]; then
opt_map="/lib/modules/$(uname -r)/System.map"
elif [ -e "/boot/System.map-$(uname -r)" ]; then
opt_map="/boot/System.map-$(uname -r)"
elif [ -e "/lib/kernel/System.map-$(uname -r)" ]; then
opt_map="/lib/kernel/System.map-$(uname -r)"
fi
# config
if [ -n "$opt_config" ]; then
# specified by user on cmdline, with --live, don't override
:
elif [ -e "$g_procfs/config.gz" ]; then
g_dumped_config="$(mktemp -t smc-config-XXXXXX)"
gunzip -c "$g_procfs/config.gz" >"$g_dumped_config"
# g_dumped_config will be deleted at the end of the script
opt_config="$g_dumped_config"
elif [ -e "/lib/modules/$(uname -r)/config" ]; then
opt_config="/lib/modules/$(uname -r)/config"
elif [ -e "/boot/config-$(uname -r)" ]; then
opt_config="/boot/config-$(uname -r)"
elif [ -e "/etc/kernels/kernel-config-$(uname -m)-$(uname -r)" ]; then
opt_config="/etc/kernels/kernel-config-$(uname -m)-$(uname -r)"
elif [ -e "/lib/kernel/config-$(uname -r)" ]; then
opt_config="/lib/kernel/config-$(uname -r)"
fi
else
pr_info "Checking for vulnerabilities against specified kernel"
fi
if [ -n "$opt_kernel" ]; then
pr_verbose "Will use kernel image \033[35m$opt_kernel\033[0m"
else
pr_verbose "Will use no kernel image (accuracy might be reduced)"
g_bad_accuracy=1
fi
if [ "$g_os" = Linux ]; then
if [ -n "$opt_config" ] && ! grep -q '^CONFIG_' "$opt_config"; then
# given file is invalid!
pr_warn "The kernel config file seems invalid, was expecting a plain-text file, ignoring it!"
opt_config=''
fi
if [ -n "${g_dumped_config:-}" ] && [ -n "$opt_config" ]; then
pr_verbose "Will use kconfig \033[35m$g_procfs/config.gz (decompressed)\033[0m"
elif [ -n "$opt_config" ]; then
pr_verbose "Will use kconfig \033[35m$opt_config\033[0m"
else
pr_verbose "Will use no kconfig (accuracy might be reduced)"
g_bad_accuracy=1
fi
if [ -n "$opt_map" ]; then
pr_verbose "Will use System.map file \033[35m$opt_map\033[0m"
else
pr_verbose "Will use no System.map file (accuracy might be reduced)"
g_bad_accuracy=1
fi
: "${g_bad_accuracy:=0}"
fi
if [ -e "$opt_kernel" ]; then
if ! command -v "${opt_arch_prefix}readelf" >/dev/null 2>&1; then
pr_debug "readelf not found"
g_kernel_err="missing '${opt_arch_prefix}readelf' tool, please install it, usually it's in the 'binutils' package"
elif [ "$opt_sysfs_only" = 1 ] || [ "$g_mode" = hw-only ]; then
g_kernel_err='kernel image decompression skipped'
else
extract_kernel "$opt_kernel"
fi
else
pr_debug "no opt_kernel defined"
g_kernel_err="couldn't find your kernel image in /boot, if you used netboot, this is normal"
fi
if [ -z "$g_kernel" ] || [ ! -r "$g_kernel" ]; then
[ -z "$g_kernel_err" ] && g_kernel_err="couldn't extract your kernel from $opt_kernel"
else
# vanilla kernels have with ^Linux version
# also try harder with some kernels (such as Red Hat) that don't have ^Linux version before their version string
# and check for FreeBSD
g_kernel_version=$("${opt_arch_prefix}strings" "$g_kernel" 2>/dev/null | grep -E \
-e '^Linux version ' \
-e '^[[:alnum:]][^[:space:]]+ \([^[:space:]]+\) #[0-9]+ .+ (19|20)[0-9][0-9]$' \
-e '^FreeBSD [0-9]' | grep -v 'ABI compat' | head -n1)
if [ -z "$g_kernel_version" ]; then
# try even harder with some kernels (such as ARM) that split the release (uname -r) and version (uname -v) in 2 adjacent strings
g_kernel_version=$("${opt_arch_prefix}strings" "$g_kernel" 2>/dev/null | grep -E -B1 '^#[0-9]+ .+ (19|20)[0-9][0-9]$' | tr "\n" " ")
fi
if [ -n "$g_kernel_version" ]; then
# in live mode, check if the img we found is the correct one
if has_runtime; then
pr_verbose "Kernel image is \033[35m$g_kernel_version"
if ! echo "$g_kernel_version" | grep -qF "$(uname -r)"; then
pr_warn "Possible discrepancy between your running kernel '$(uname -r)' and the image '$g_kernel_version' we found ($opt_kernel), results might be incorrect"
fi
else
pr_verbose "Kernel image is \033[35m$g_kernel_version"
fi
else
pr_verbose "Kernel image version is unknown"
fi
fi
pr_info
# end of header stuff
# now we define some util functions and the check_*() funcs, as
# the user can choose to execute only some of those
# Check a sysfs/procfs file for a vulnerability mitigation status
# Args: $1=file_path $2=regex(optional) $3=mode(optional)
# Sets: ret_sys_interface_check_fullmsg
# Returns: 0 if file matched, 1 otherwise
sys_interface_check() {
local file regex mode mockvarname
file="$1"
regex="${2:-}"
mode="${3:-}"
msg=''
ret_sys_interface_check_fullmsg=''
if has_runtime && [ "$opt_no_sysfs" = 0 ] && [ -r "$file" ]; then
:
else
g_mockme=$(printf "%b\n%b" "$g_mockme" "SMC_MOCK_SYSFS_$(basename "$file")_RET=1")
return 1
fi
mockvarname="SMC_MOCK_SYSFS_$(basename "$file")_RET"
# shellcheck disable=SC2086,SC1083
if [ -n "$(eval echo \${$mockvarname:-})" ]; then
pr_debug "sysfs: MOCKING enabled for $file func returns $(eval echo \$$mockvarname)"
g_mocked=1
return "$(eval echo \$$mockvarname)"
fi
[ -n "$regex" ] || regex='.*'
mockvarname="SMC_MOCK_SYSFS_$(basename "$file")"
# shellcheck disable=SC2086,SC1083
if [ -n "$(eval echo \${$mockvarname:-})" ]; then
ret_sys_interface_check_fullmsg="$(eval echo \$$mockvarname)"
msg=$(echo "$ret_sys_interface_check_fullmsg" | grep -Eo "$regex")
pr_debug "sysfs: MOCKING enabled for $file, will return $ret_sys_interface_check_fullmsg"
g_mocked=1
else
ret_sys_interface_check_fullmsg=$(cat "$file")
msg=$(grep -Eo "$regex" "$file")
g_mockme=$(printf "%b\n%b" "$g_mockme" "SMC_MOCK_SYSFS_$(basename "$file")='$ret_sys_interface_check_fullmsg'")
fi
if [ "$mode" = silent ]; then
# capture sysfs message for JSON even in silent mode
# shellcheck disable=SC2034
g_json_cve_sysfs_msg="$ret_sys_interface_check_fullmsg"
return 0
elif [ "$mode" = quiet ]; then
pr_info "* Information from the /sys interface: $ret_sys_interface_check_fullmsg"
# shellcheck disable=SC2034
g_json_cve_sysfs_msg="$ret_sys_interface_check_fullmsg"
return 0
fi
pr_info_nol "* Mitigated according to the /sys interface: "
if echo "$msg" | grep -qi '^not affected'; then
# Not affected
ret_sys_interface_check_status=OK
pstatus green YES "$ret_sys_interface_check_fullmsg"
elif echo "$msg" | grep -qEi '^(kvm: )?mitigation'; then
# Mitigation: PTI
ret_sys_interface_check_status=OK
pstatus green YES "$ret_sys_interface_check_fullmsg"
elif echo "$msg" | grep -qi '^vulnerable'; then
# Vulnerable
ret_sys_interface_check_status=VULN
pstatus yellow NO "$ret_sys_interface_check_fullmsg"
else
ret_sys_interface_check_status=UNK
pstatus yellow UNKNOWN "$ret_sys_interface_check_fullmsg"
fi
# capture for JSON full output (read by _emit_json_full via pvulnstatus)
# shellcheck disable=SC2034
g_json_cve_sysfs_status="$ret_sys_interface_check_status"
# shellcheck disable=SC2034
g_json_cve_sysfs_msg="$ret_sys_interface_check_fullmsg"
pr_debug "sys_interface_check: $file=$msg (re=$regex)"
return 0
}
# Display kernel image, config, and System.map availability
check_kernel_info() {
local config_display
pr_info "\033[1;34mKernel information\033[0m"
if has_runtime; then
pr_info "* Kernel is \033[35m$g_os $(uname -r) $(uname -v) $(uname -m)\033[0m"
elif [ -n "$g_kernel_version" ]; then
pr_info "* Kernel is \033[35m$g_kernel_version\033[0m"
else
pr_info "* Kernel is \033[35munknown\033[0m"
fi
if [ -n "$opt_kernel" ] && [ -e "$opt_kernel" ]; then
pr_info "* Kernel image found at \033[35m$opt_kernel\033[0m"
else
pr_info "* Kernel image NOT found"
fi
if [ -n "$opt_config" ]; then
if [ -n "${g_dumped_config:-}" ]; then
config_display="$g_procfs/config.gz"
else
config_display="$opt_config"
fi
pr_info "* Kernel config found at \033[35m$config_display\033[0m"
else
pr_info "* Kernel config NOT found"
fi
if [ -n "$opt_map" ]; then
pr_info "* Kernel System.map found at \033[35m$opt_map\033[0m"
else
pr_info "* Kernel System.map NOT found"
fi
if [ "${g_bad_accuracy:-0}" = 1 ]; then
pr_warn "We're missing some kernel info, accuracy might be reduced"
fi
}
# Collapse a whitespace-separated list to its unique values, preserving first-seen order.
# Used to prettify the per-core ARM lists for display (e.g. "0x41 0x41 0x41 0x41" -> "0x41").
_uniq_list() {
echo "$1" | awk '{ for (i = 1; i <= NF; i++) if (!seen[$i]++) printf "%s%s", (n++ ? " " : ""), $i }'
}
# Display hardware-level CPU mitigation support (microcode features, ARCH_CAPABILITIES, etc.)
check_cpu() {
local capabilities ret spec_ctrl_msr codename ucode_str
if is_arm_cpu; then
pr_info "* CPU details"
pr_info " * Vendor: $cpu_vendor"
pr_info " * Model name: $cpu_friendly_name"
if [ -n "${cpu_impl_list:-}" ]; then
pr_info " * Implementer(s): $(_uniq_list "$cpu_impl_list")"
fi
if [ -n "${cpu_part_list:-}" ]; then
pr_info " * Part(s): $(_uniq_list "$cpu_part_list")"
fi
if [ -n "${cpu_arch_list:-}" ]; then
pr_info " * Architecture(s): $(_uniq_list "$cpu_arch_list")"
fi
if has_runtime; then
pr_info_nol " * Running as VM guest: "
if is_running_as_guest; then
pstatus yellow YES "$g_is_guest_vm_reason"
else
pstatus green NO
fi
fi
# ARM exposes no userspace-readable CPUID/MSR to query SSBD support directly.
# The ARMv8.5 SSBS ("Speculative Store Bypass Safe") hardware bit, when present,
# surfaces as the 'ssbs' hwcap in /proc/cpuinfo. We use it *only* as a positive
# confirmation of SSB mitigation capability (Variant 4 / CVE-2018-3639): its
# absence proves nothing, because the kernel deliberately hides the hwcap on some
# cores (e.g. the erratum-3194386 SSBS self-sync workaround), so we must never
# infer immunity from a missing 'ssbs'.
if has_runtime; then
pr_info_nol " * CPU indicates SSBS (Speculative Store Bypass Safe) capability: "
if grep '^Features' "$g_procfs/cpuinfo" | grep -qw ssbs; then
cap_ssbd='ARM SSBS (cpuinfo)'
pstatus green YES "$cap_ssbd"
else
pstatus blue UNKNOWN "not exposed (the kernel may hide it; cannot conclude)"
fi
fi
return
fi
if ! uname -m | grep -qwE 'x86_64|i[3-6]86|amd64'; then
return
fi
pr_info "* CPU details"
pr_info " * Vendor: $cpu_vendor"
pr_info " * Model name: $cpu_friendly_name"
pr_info " * Family: $(printf '0x%02x' "$cpu_family") Model: $(printf '0x%02x' "$cpu_model") Stepping: $(printf '0x%02x' "$cpu_stepping")"
if [ -n "$cpu_ucode" ]; then
ucode_str=$(printf '0x%x' "$cpu_ucode")
else
ucode_str="N/A"
fi
pr_info " * Microcode: $ucode_str"
pr_info " * CPUID: $(printf '0x%08x' "$cpu_cpuid")"
if is_intel; then
pr_info " * Platform ID: $(printf '0x%02x' "$cpu_platformid")"
if [ "$cpu_hybrid" = 1 ]; then
pr_info " * Hybrid CPU: YES"
else
pr_info " * Hybrid CPU: NO"
fi
codename=$(get_intel_codename)
if [ -n "$codename" ]; then
pr_info " * Codename: $codename"
fi
fi
if has_runtime; then
pr_info_nol " * Running as VM guest: "
if is_running_as_guest; then
pstatus yellow YES "$g_is_guest_vm_reason"
else
pstatus green NO
fi
fi
pr_info "* Hardware support (CPU microcode) for mitigation techniques"
pr_info " * Indirect Branch Restricted Speculation (IBRS)"
pr_info_nol " * SPEC_CTRL MSR is available: "
read_msr $MSR_IA32_SPEC_CTRL
ret=$?
if [ $ret = $READ_MSR_RET_OK ]; then
spec_ctrl_msr=1
pstatus green YES
elif [ $ret = $READ_MSR_RET_KO ]; then
spec_ctrl_msr=0
pstatus yellow NO
else
spec_ctrl_msr=-1
pstatus yellow UNKNOWN "$ret_read_msr_msg"
fi
pr_info_nol " * CPU indicates IBRS capability: "
# from kernel src: { X86_FEATURE_SPEC_CTRL, CPUID_EDX,26, 0x00000007, 0 },
# amd: https://developer.amd.com/wp-content/resources/Architecture_Guidelines_Update_Indirect_Branch_Control.pdf
# amd: 8000_0008 EBX[14]=1
cap_ibrs=''
if is_intel; then
read_cpuid 0x7 0x0 $EDX 26 1 1
ret=$?
if [ $ret = $READ_CPUID_RET_OK ]; then
pstatus green YES "SPEC_CTRL feature bit"
cap_spec_ctrl=1
cap_ibrs='SPEC_CTRL'
fi
elif is_amd || is_hygon; then
read_cpuid 0x80000008 0x0 $EBX 14 1 1
ret=$?
if [ $ret = $READ_CPUID_RET_OK ]; then
pstatus green YES "IBRS_SUPPORT feature bit"
cap_ibrs='IBRS_SUPPORT'
fi
else
ret=invalid
pstatus yellow NO "unknown CPU"
fi
if [ -z "$cap_ibrs" ] && [ $ret = $READ_CPUID_RET_ERR ] && has_runtime; then
# CPUID device unavailable (e.g. in a VM): fall back to /proc/cpuinfo
if grep ^flags "$g_procfs/cpuinfo" | grep -qw ibrs; then
cap_ibrs='IBRS (cpuinfo)'
cap_spec_ctrl=1
pstatus green YES "ibrs flag in $g_procfs/cpuinfo"
ret=$READ_CPUID_RET_OK
fi
fi
if [ $ret = $READ_CPUID_RET_KO ]; then
pstatus yellow NO
elif [ $ret = $READ_CPUID_RET_ERR ]; then
pstatus yellow UNKNOWN "$ret_read_cpuid_msg"
cap_spec_ctrl=-1
fi
if is_amd || is_hygon; then
pr_info_nol " * CPU indicates preferring IBRS always-on: "
# amd or hygon
read_cpuid 0x80000008 0x0 $EBX 16 1 1
ret=$?
if [ $ret = $READ_CPUID_RET_OK ]; then
pstatus green YES
elif [ $ret = $READ_CPUID_RET_KO ]; then
pstatus yellow NO
else
pstatus yellow UNKNOWN "$ret_read_cpuid_msg"
fi
pr_info_nol " * CPU indicates preferring IBRS over retpoline: "
# amd or hygon
read_cpuid 0x80000008 0x0 $EBX 18 1 1
ret=$?
if [ $ret = $READ_CPUID_RET_OK ]; then
pstatus green YES
elif [ $ret = $READ_CPUID_RET_KO ]; then
pstatus yellow NO
else
pstatus yellow UNKNOWN "$ret_read_cpuid_msg"
fi
fi
# IBPB
pr_info " * Indirect Branch Prediction Barrier (IBPB)"
if [ "$opt_allow_msr_write" = 1 ]; then
pr_info_nol " * PRED_CMD MSR is available: "
# the new MSR 'PRED_CTRL' is at offset 0x49, write-only
write_msr 0x49
ret=$?
if [ $ret = $WRITE_MSR_RET_OK ]; then
pstatus green YES
elif [ $ret = $WRITE_MSR_RET_KO ]; then
pstatus yellow NO
else
pstatus yellow UNKNOWN "$ret_write_msr_msg"
fi
fi
pr_info_nol " * CPU indicates IBPB capability: "
# CPUID EAX=0x80000008, ECX=0x00 return EBX[12] indicates support for just IBPB.
if [ "$cap_spec_ctrl" = 1 ]; then
# spec_ctrl implies ibpb
cap_ibpb='SPEC_CTRL'
pstatus green YES "SPEC_CTRL feature bit"
elif is_intel; then
if [ "$cap_spec_ctrl" = -1 ]; then
pstatus yellow UNKNOWN "is cpuid kernel module available?"
else
pstatus yellow NO
fi
elif is_amd || is_hygon; then
read_cpuid 0x80000008 0x0 $EBX 12 1 1
ret=$?
if [ $ret = $READ_CPUID_RET_OK ]; then
cap_ibpb='IBPB_SUPPORT'
pstatus green YES "IBPB_SUPPORT feature bit"
elif [ $ret = $READ_CPUID_RET_ERR ] && has_runtime && grep ^flags "$g_procfs/cpuinfo" | grep -qw ibpb; then
# CPUID device unavailable (e.g. in a VM): fall back to /proc/cpuinfo
cap_ibpb='IBPB (cpuinfo)'
pstatus green YES "ibpb flag in $g_procfs/cpuinfo"
elif [ $ret = $READ_CPUID_RET_KO ]; then
pstatus yellow NO
else
pstatus yellow UNKNOWN "$ret_read_cpuid_msg"
fi
fi
# IBPB_RET: CPUID EAX=0x80000008, ECX=0x00 return EBX[30] indicates IBPB also flushes
# return predictions (Zen4+). Without this bit, IBPB alone does not clear the return
# predictor, requiring an additional RSB fill (kernel X86_BUG_IBPB_NO_RET fix).
cap_ibpb_ret=''
if is_amd || is_hygon; then
pr_info_nol " * CPU indicates IBPB flushes return predictions: "
read_cpuid 0x80000008 0x0 $EBX 30 1 1
ret=$?
if [ $ret = $READ_CPUID_RET_OK ]; then
cap_ibpb_ret=1
pstatus green YES "IBPB_RET feature bit"
elif [ $ret = $READ_CPUID_RET_KO ]; then
cap_ibpb_ret=0
pstatus yellow NO
else
cap_ibpb_ret=-1
pstatus yellow UNKNOWN "$ret_read_cpuid_msg"
fi
fi
# STIBP
pr_info " * Single Thread Indirect Branch Predictors (STIBP)"
pr_info_nol " * SPEC_CTRL MSR is available: "
if [ "$spec_ctrl_msr" = 1 ]; then
pstatus green YES
elif [ "$spec_ctrl_msr" = 0 ]; then
pstatus yellow NO
else
pstatus yellow UNKNOWN "$ret_read_msr_msg"
fi
pr_info_nol " * CPU indicates STIBP capability: "
# intel: A processor supports STIBP if it enumerates CPUID (EAX=7H,ECX=0):EDX[27] as 1
# amd: 8000_0008 EBX[15]=1
cap_stibp=''
if is_intel; then
read_cpuid 0x7 0x0 $EDX 27 1 1
ret=$?
if [ $ret = $READ_CPUID_RET_OK ]; then
pstatus green YES "Intel STIBP feature bit"
cap_stibp='Intel STIBP'
fi
elif is_amd; then
read_cpuid 0x80000008 0x0 $EBX 15 1 1
ret=$?
if [ $ret = $READ_CPUID_RET_OK ]; then
pstatus green YES "AMD STIBP feature bit"
cap_stibp='AMD STIBP'
fi
elif is_hygon; then
read_cpuid 0x80000008 0x0 $EBX 15 1 1
ret=$?
if [ $ret = $READ_CPUID_RET_OK ]; then
pstatus green YES "HYGON STIBP feature bit"
cap_stibp='HYGON STIBP'
fi
else
ret=invalid
pstatus yellow UNKNOWN "unknown CPU"
fi
if [ -z "$cap_stibp" ] && [ $ret = $READ_CPUID_RET_ERR ] && has_runtime; then
# CPUID device unavailable (e.g. in a VM): fall back to /proc/cpuinfo
if grep ^flags "$g_procfs/cpuinfo" | grep -qw stibp; then
cap_stibp='STIBP (cpuinfo)'
pstatus green YES "stibp flag in $g_procfs/cpuinfo"
ret=$READ_CPUID_RET_OK
fi
fi
if [ $ret = $READ_CPUID_RET_KO ]; then
pstatus yellow NO
elif [ $ret = $READ_CPUID_RET_ERR ]; then
pstatus yellow UNKNOWN "$ret_read_cpuid_msg"
fi
if is_amd || is_hygon; then
pr_info_nol " * CPU indicates preferring STIBP always-on: "
read_cpuid 0x80000008 0x0 $EBX 17 1 1
ret=$?
if [ $ret = $READ_CPUID_RET_OK ]; then
pstatus green YES
elif [ $ret = $READ_CPUID_RET_KO ]; then
pstatus yellow NO
else
pstatus yellow UNKNOWN "$ret_read_cpuid_msg"
fi
fi
# variant 4
if is_intel; then
pr_info " * Speculative Store Bypass Disable (SSBD)"
pr_info_nol " * CPU indicates SSBD capability: "
read_cpuid 0x7 0x0 $EDX 31 1 1
ret24=$?
ret25=$ret24
if [ $ret24 = $READ_CPUID_RET_OK ]; then
cap_ssbd='Intel SSBD'
fi
elif is_amd; then
pr_info " * Speculative Store Bypass Disable (SSBD)"
pr_info_nol " * CPU indicates SSBD capability: "
read_cpuid 0x80000008 0x0 $EBX 24 1 1
ret24=$?
read_cpuid 0x80000008 0x0 $EBX 25 1 1
ret25=$?
if [ $ret24 = $READ_CPUID_RET_OK ]; then
cap_ssbd='AMD SSBD in SPEC_CTRL'
#cpuid_ssbd_spec_ctrl=1
elif [ $ret25 = $READ_CPUID_RET_OK ]; then
cap_ssbd='AMD SSBD in VIRT_SPEC_CTRL'
#cpuid_ssbd_virt_spec_ctrl=1
elif [ "$cpu_family" -ge 21 ] && [ "$cpu_family" -le 23 ]; then
cap_ssbd='AMD non-architectural MSR'
fi
elif is_hygon; then
pr_info " * Speculative Store Bypass Disable (SSBD)"
pr_info_nol " * CPU indicates SSBD capability: "
read_cpuid 0x80000008 0x0 $EBX 24 1 1
ret24=$?
read_cpuid 0x80000008 0x0 $EBX 25 1 1
ret25=$?
if [ $ret24 = $READ_CPUID_RET_OK ]; then
cap_ssbd='HYGON SSBD in SPEC_CTRL'
#hygon cpuid_ssbd_spec_ctrl=1
elif [ $ret25 = $READ_CPUID_RET_OK ]; then
cap_ssbd='HYGON SSBD in VIRT_SPEC_CTRL'
#hygon cpuid_ssbd_virt_spec_ctrl=1
elif [ "$cpu_family" -ge 24 ]; then
cap_ssbd='HYGON non-architectural MSR'
fi
fi
if [ -z "$cap_ssbd" ] && [ "$ret24" = $READ_CPUID_RET_ERR ] && [ "$ret25" = $READ_CPUID_RET_ERR ] && has_runtime; then
# CPUID device unavailable (e.g. in a VM): fall back to /proc/cpuinfo
if grep ^flags "$g_procfs/cpuinfo" | grep -qw ssbd; then
cap_ssbd='SSBD (cpuinfo)'
elif grep ^flags "$g_procfs/cpuinfo" | grep -qw virt_ssbd; then
cap_ssbd='SSBD in VIRT_SPEC_CTRL (cpuinfo)'
fi
fi
if [ -n "${cap_ssbd:=}" ]; then
pstatus green YES "$cap_ssbd"
elif [ "$ret24" = $READ_CPUID_RET_ERR ] && [ "$ret25" = $READ_CPUID_RET_ERR ]; then
pstatus yellow UNKNOWN "$ret_read_cpuid_msg"
else
pstatus yellow NO
fi
cap_amd_ssb_no=0
cap_hygon_ssb_no=0
if is_amd; then
# similar to SSB_NO for intel
read_cpuid 0x80000008 0x0 $EBX 26 1 1
ret=$?
if [ $ret = $READ_CPUID_RET_OK ]; then
cap_amd_ssb_no=1
elif [ $ret = $READ_CPUID_RET_ERR ]; then
cap_amd_ssb_no=-1
fi
elif is_hygon; then
# indicate when speculative store bypass disable is no longer needed to prevent speculative loads bypassing older stores
read_cpuid 0x80000008 0x0 $EBX 26 1 1
ret=$?
if [ $ret = $READ_CPUID_RET_OK ]; then
cap_hygon_ssb_no=1
elif [ $ret = $READ_CPUID_RET_ERR ]; then
cap_hygon_ssb_no=-1
fi
fi
pr_info " * L1 data cache invalidation"
if [ "$opt_allow_msr_write" = 1 ]; then
pr_info_nol " * FLUSH_CMD MSR is available: "
# the new MSR 'FLUSH_CMD' is at offset 0x10b, write-only
# this is probed for informational purposes only, the CPUID L1D flush bit
# (cap_l1df) is the authoritative indicator per Intel guidance
write_msr 0x10b
ret=$?
if [ $ret = $WRITE_MSR_RET_OK ]; then
pstatus green YES
elif [ $ret = $WRITE_MSR_RET_KO ]; then
pstatus yellow NO
else
pstatus yellow UNKNOWN "$ret_write_msr_msg"
fi
fi
# CPUID of L1D
pr_info_nol " * CPU indicates L1D flush capability: "
read_cpuid 0x7 0x0 $EDX 28 1 1
ret=$?
if [ $ret = $READ_CPUID_RET_OK ]; then
pstatus green YES "L1D flush feature bit"
cap_l1df=1
elif [ $ret = $READ_CPUID_RET_ERR ] && has_runtime && grep ^flags "$g_procfs/cpuinfo" | grep -qw flush_l1d; then
# CPUID device unavailable (e.g. in a VM): fall back to /proc/cpuinfo
pstatus green YES "flush_l1d flag in $g_procfs/cpuinfo"
cap_l1df=1
elif [ $ret = $READ_CPUID_RET_KO ]; then
pstatus yellow NO
cap_l1df=0
else
pstatus yellow UNKNOWN "$ret_read_cpuid_msg"
cap_l1df=-1
fi
if is_intel; then
pr_info " * Microarchitectural Data Sampling"
pr_info_nol " * VERW instruction is available: "
read_cpuid 0x7 0x0 $EDX 10 1 1
ret=$?
if [ $ret = $READ_CPUID_RET_OK ]; then
cap_md_clear=1
pstatus green YES "MD_CLEAR feature bit"
elif [ $ret = $READ_CPUID_RET_ERR ] && has_runtime && grep ^flags "$g_procfs/cpuinfo" | grep -qw md_clear; then
# CPUID device unavailable (e.g. in a VM): fall back to /proc/cpuinfo
cap_md_clear=1
pstatus green YES "md_clear flag in $g_procfs/cpuinfo"
elif [ $ret = $READ_CPUID_RET_KO ]; then
cap_md_clear=0
pstatus yellow NO
else
cap_md_clear=-1
pstatus yellow UNKNOWN "$ret_read_cpuid_msg"
fi
fi
if is_intel; then
pr_info " * Indirect Branch Predictor Controls"
pr_info_nol " * Indirect Predictor Disable feature is available: "
read_cpuid 0x7 0x2 $EDX 1 1 1
ret=$?
# cap_ipred is not yet used in verdict logic (no kernel sysfs/config to cross-reference)
# shellcheck disable=SC2034
if [ $ret = $READ_CPUID_RET_OK ]; then
cap_ipred=1
pstatus green YES "IPRED_CTRL feature bit"
elif [ $ret = $READ_CPUID_RET_KO ]; then
cap_ipred=0
pstatus yellow NO
else
cap_ipred=-1
pstatus yellow UNKNOWN "$ret_read_cpuid_msg"
fi
pr_info_nol " * Bottomless RSB Disable feature is available: "
read_cpuid 0x7 0x2 $EDX 2 1 1
ret=$?
if [ $ret = $READ_CPUID_RET_OK ]; then
cap_rrsba=1
pstatus green YES "RRSBA_CTRL feature bit"
elif [ $ret = $READ_CPUID_RET_KO ]; then
cap_rrsba=0
pstatus yellow NO
else
cap_rrsba=-1
pstatus yellow UNKNOWN "$ret_read_cpuid_msg"
fi
pr_info_nol " * BHB-Focused Indirect Predictor Disable feature is available: "
read_cpuid 0x7 0x2 $EDX 2 1 1
ret=$?
if [ $ret = $READ_CPUID_RET_OK ]; then
cap_bhi=1
pstatus green YES "BHI_CTRL feature bit"
elif [ $ret = $READ_CPUID_RET_KO ]; then
cap_bhi=0
pstatus yellow NO
else
cap_bhi=-1
pstatus yellow UNKNOWN "$ret_read_cpuid_msg"
fi
fi
if is_intel; then
pr_info " * Enhanced IBRS (IBRS_ALL)"
pr_info_nol " * CPU indicates ARCH_CAPABILITIES MSR availability: "
cap_arch_capabilities=-1
# A processor supports the ARCH_CAPABILITIES MSR if it enumerates CPUID (EAX=7H,ECX=0):EDX[29] as 1
read_cpuid 0x7 0x0 $EDX 29 1 1
ret=$?
if [ $ret = $READ_CPUID_RET_OK ]; then
pstatus green YES
cap_arch_capabilities=1
elif [ $ret = $READ_CPUID_RET_ERR ] && has_runtime && grep ^flags "$g_procfs/cpuinfo" | grep -qw arch_capabilities; then
# CPUID device unavailable (e.g. in a VM): fall back to /proc/cpuinfo
pstatus green YES "arch_capabilities flag in $g_procfs/cpuinfo"
cap_arch_capabilities=1
elif [ $ret = $READ_CPUID_RET_KO ]; then
pstatus yellow NO
cap_arch_capabilities=0
else
pstatus yellow UNKNOWN "$ret_read_cpuid_msg"
fi
pr_info_nol " * ARCH_CAPABILITIES MSR advertises IBRS_ALL capability: "
cap_taa_no=-1
cap_mds_no=-1
cap_rdcl_no=-1
cap_ibrs_all=-1
cap_rsba=-1
cap_l1dflush_no=-1
cap_ssb_no=-1
cap_pschange_msc_no=-1
cap_tsx_ctrl_msr=-1
cap_gds_ctrl=-1
cap_gds_no=-1
cap_rfds_no=-1
cap_rfds_clear=-1
cap_its_no=-1
cap_sbdr_ssdp_no=-1
cap_fbsdp_no=-1
cap_psdp_no=-1
cap_fb_clear=-1
if [ "$cap_arch_capabilities" = -1 ]; then
pstatus yellow UNKNOWN
elif [ "$cap_arch_capabilities" != 1 ]; then
cap_rdcl_no=0
cap_taa_no=0
cap_mds_no=0
cap_ibrs_all=0
cap_rsba=0
cap_l1dflush_no=0
cap_ssb_no=0
cap_pschange_msc_no=0
cap_tsx_ctrl_msr=0
cap_gds_ctrl=0
cap_gds_no=0
cap_rfds_no=0
cap_rfds_clear=0
cap_its_no=0
cap_sbdr_ssdp_no=0
cap_fbsdp_no=0
cap_psdp_no=0
cap_fb_clear=0
pstatus yellow NO
else
read_msr $MSR_IA32_ARCH_CAPABILITIES
ret=$?
cap_rdcl_no=0
cap_taa_no=0
cap_mds_no=0
cap_ibrs_all=0
cap_rsba=0
cap_l1dflush_no=0
cap_ssb_no=0
cap_pschange_msc_no=0
cap_tsx_ctrl_msr=0
cap_gds_ctrl=0
cap_gds_no=0
cap_rfds_no=0
cap_rfds_clear=0
cap_its_no=0
cap_sbdr_ssdp_no=0
cap_fbsdp_no=0
cap_psdp_no=0
cap_fb_clear=0
if [ $ret = $READ_MSR_RET_OK ]; then
capabilities=$ret_read_msr_value
# https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/x86/include/asm/msr-index.h#n82
pr_debug "capabilities MSR is $capabilities (hex)"
[ $((ret_read_msr_value_lo >> 0 & 1)) -eq 1 ] && cap_rdcl_no=1
[ $((ret_read_msr_value_lo >> 1 & 1)) -eq 1 ] && cap_ibrs_all=1
[ $((ret_read_msr_value_lo >> 2 & 1)) -eq 1 ] && cap_rsba=1
[ $((ret_read_msr_value_lo >> 3 & 1)) -eq 1 ] && cap_l1dflush_no=1
[ $((ret_read_msr_value_lo >> 4 & 1)) -eq 1 ] && cap_ssb_no=1
[ $((ret_read_msr_value_lo >> 5 & 1)) -eq 1 ] && cap_mds_no=1
[ $((ret_read_msr_value_lo >> 6 & 1)) -eq 1 ] && cap_pschange_msc_no=1
[ $((ret_read_msr_value_lo >> 7 & 1)) -eq 1 ] && cap_tsx_ctrl_msr=1
[ $((ret_read_msr_value_lo >> 8 & 1)) -eq 1 ] && cap_taa_no=1
[ $((ret_read_msr_value_lo >> 13 & 1)) -eq 1 ] && cap_sbdr_ssdp_no=1
[ $((ret_read_msr_value_lo >> 14 & 1)) -eq 1 ] && cap_fbsdp_no=1
[ $((ret_read_msr_value_lo >> 15 & 1)) -eq 1 ] && cap_psdp_no=1
[ $((ret_read_msr_value_lo >> 17 & 1)) -eq 1 ] && cap_fb_clear=1
[ $((ret_read_msr_value_lo >> 25 & 1)) -eq 1 ] && cap_gds_ctrl=1
[ $((ret_read_msr_value_lo >> 26 & 1)) -eq 1 ] && cap_gds_no=1
[ $((ret_read_msr_value_lo >> 27 & 1)) -eq 1 ] && cap_rfds_no=1
[ $((ret_read_msr_value_lo >> 28 & 1)) -eq 1 ] && cap_rfds_clear=1
[ $((ret_read_msr_value_hi >> 30 & 1)) -eq 1 ] && cap_its_no=1
pr_debug "capabilities says rdcl_no=$cap_rdcl_no ibrs_all=$cap_ibrs_all rsba=$cap_rsba l1dflush_no=$cap_l1dflush_no ssb_no=$cap_ssb_no mds_no=$cap_mds_no taa_no=$cap_taa_no pschange_msc_no=$cap_pschange_msc_no rfds_no=$cap_rfds_no rfds_clear=$cap_rfds_clear its_no=$cap_its_no sbdr_ssdp_no=$cap_sbdr_ssdp_no fbsdp_no=$cap_fbsdp_no psdp_no=$cap_psdp_no fb_clear=$cap_fb_clear"
if [ "$cap_ibrs_all" = 1 ]; then
pstatus green YES
else
pstatus yellow NO
fi
elif [ $ret = $READ_MSR_RET_KO ]; then
# the MSR access faulted: the register is genuinely absent, so the
# pre-seeded 0 ("not advertised") values are correct.
pstatus yellow NO
else
# RET_ERR (no msr module) or RET_LOCKDOWN (MSR reads restricted):
# CPUID told us the MSR exists but we couldn't read it, so its bits
# are undetermined, not 0. Leaving them at 0 would falsely claim the
# CPU "explicitly indicates not immune".
# Reset every arch-cap-derived value to -1 (UNKNOWN) instead.
cap_rdcl_no=-1
cap_taa_no=-1
cap_mds_no=-1
cap_ibrs_all=-1
cap_rsba=-1
cap_l1dflush_no=-1
cap_ssb_no=-1
cap_pschange_msc_no=-1
cap_tsx_ctrl_msr=-1
cap_gds_ctrl=-1
cap_gds_no=-1
cap_rfds_no=-1
cap_rfds_clear=-1
cap_its_no=-1
cap_sbdr_ssdp_no=-1
cap_fbsdp_no=-1
cap_psdp_no=-1
cap_fb_clear=-1
pstatus yellow UNKNOWN "$ret_read_msr_msg"
fi
fi
pr_info_nol " * CPU explicitly indicates not being affected by Meltdown/L1TF (RDCL_NO): "
if [ "$cap_rdcl_no" = -1 ]; then
pstatus yellow UNKNOWN
elif [ "$cap_rdcl_no" = 1 ]; then
pstatus green YES
else
pstatus yellow NO
fi
pr_info_nol " * CPU explicitly indicates not being affected by Variant 4 (SSB_NO): "
if [ "$cap_ssb_no" = -1 ]; then
pstatus yellow UNKNOWN
elif [ "$cap_ssb_no" = 1 ] || [ "$cap_amd_ssb_no" = 1 ] || [ "$cap_hygon_ssb_no" = 1 ]; then
pstatus green YES
else
pstatus yellow NO
fi
pr_info_nol " * CPU/Hypervisor indicates L1D flushing is not necessary on this system: "
if [ "$cap_l1dflush_no" = -1 ]; then
pstatus yellow UNKNOWN
elif [ "$cap_l1dflush_no" = 1 ]; then
pstatus green YES
else
pstatus yellow NO
fi
pr_info_nol " * Hypervisor indicates host CPU might be affected by RSB underflow (RSBA): "
if [ "$cap_rsba" = -1 ]; then
pstatus yellow UNKNOWN
elif [ "$cap_rsba" = 1 ]; then
pstatus yellow YES
else
pstatus blue NO
fi
pr_info_nol " * CPU explicitly indicates not being affected by Microarchitectural Data Sampling (MDS_NO): "
if [ "$cap_mds_no" = -1 ]; then
pstatus yellow UNKNOWN
elif [ "$cap_mds_no" = 1 ]; then
pstatus green YES
else
pstatus yellow NO
fi
pr_info_nol " * CPU explicitly indicates not being affected by TSX Asynchronous Abort (TAA_NO): "
if [ "$cap_taa_no" = -1 ]; then
pstatus yellow UNKNOWN
elif [ "$cap_taa_no" = 1 ]; then
pstatus green YES
else
pstatus yellow NO
fi
pr_info_nol " * CPU explicitly indicates not being affected by iTLB Multihit (PSCHANGE_MSC_NO): "
if [ "$cap_pschange_msc_no" = -1 ]; then
pstatus yellow UNKNOWN
elif [ "$cap_pschange_msc_no" = 1 ]; then
pstatus green YES
else
pstatus yellow NO
fi
pr_info_nol " * CPU explicitly indicates having MSR for TSX control (TSX_CTRL_MSR): "
if [ "$cap_tsx_ctrl_msr" = -1 ]; then
pstatus yellow UNKNOWN
elif [ "$cap_tsx_ctrl_msr" = 1 ]; then
pstatus green YES
else
pstatus yellow NO
fi
# IA32_TSX_CTRL (MSR 0x122): architectural way to disable TSX, available on
# Cascade Lake and newer, and some Coffee Lake steppings via microcode update
if [ "$cap_tsx_ctrl_msr" = 1 ]; then
read_msr $MSR_IA32_TSX_CTRL
ret=$?
if [ "$ret" = $READ_MSR_RET_OK ]; then
cap_tsx_ctrl_rtm_disable=$((ret_read_msr_value_lo >> 0 & 1))
cap_tsx_ctrl_cpuid_clear=$((ret_read_msr_value_lo >> 1 & 1))
fi
pr_info_nol " * TSX_CTRL MSR indicates TSX RTM is disabled: "
if [ "$cap_tsx_ctrl_rtm_disable" = 1 ]; then
pstatus blue YES
elif [ "$cap_tsx_ctrl_rtm_disable" = 0 ]; then
pstatus blue NO
else
# shellcheck disable=SC2154
pstatus yellow UNKNOWN "$ret_read_msr_0x122_msg"
fi
pr_info_nol " * TSX_CTRL MSR indicates TSX CPUID bit is cleared: "
if [ "$cap_tsx_ctrl_cpuid_clear" = 1 ]; then
pstatus blue YES
elif [ "$cap_tsx_ctrl_cpuid_clear" = 0 ]; then
pstatus blue NO
else
# shellcheck disable=SC2154
pstatus yellow UNKNOWN "$ret_read_msr_0x122_msg"
fi
fi
pr_info_nol " * CPU explicitly indicates being affected by GDS and having mitigation control (GDS_CTRL): "
if [ "$cap_gds_ctrl" = -1 ]; then
pstatus yellow UNKNOWN "couldn't read MSR"
elif [ "$cap_gds_ctrl" = 1 ]; then
pstatus green YES
else
pstatus blue NO
fi
cap_gds_mitg_dis=-1
cap_gds_mitg_lock=-1
if [ "$cap_gds_ctrl" = 1 ]; then
read_msr $MSR_IA32_MCU_OPT_CTRL
ret=$?
if [ "$ret" = $READ_MSR_RET_OK ]; then
cap_gds_mitg_dis=$((ret_read_msr_value_lo >> 4 & 1))
cap_gds_mitg_lock=$((ret_read_msr_value_lo >> 5 & 1))
fi
pr_info_nol " * GDS microcode mitigation is disabled (GDS_MITG_DIS): "
if [ "$cap_gds_mitg_dis" = -1 ]; then
# shellcheck disable=SC2154
pstatus yellow UNKNOWN "$ret_read_msr_0x123_msg"
elif [ "$cap_gds_mitg_dis" = 1 ]; then
pstatus yellow YES
else
pstatus green NO
fi
pr_info_nol " * GDS microcode mitigation is locked in enabled state (GDS_MITG_LOCK): "
if [ "$cap_gds_mitg_lock" = -1 ]; then
# shellcheck disable=SC2154
pstatus yellow UNKNOWN "$ret_read_msr_0x123_msg"
elif [ "$cap_gds_mitg_lock" = 1 ]; then
pstatus blue YES
else
pstatus blue NO
fi
fi
pr_info_nol " * CPU explicitly indicates not being affected by GDS (GDS_NO): "
if [ "$cap_gds_no" = -1 ]; then
pstatus yellow UNKNOWN "couldn't read MSR"
elif [ "$cap_gds_no" = 1 ]; then
pstatus green YES
else
pstatus yellow NO
fi
pr_info_nol " * CPU explicitly indicates not being affected by MMIO Stale Data (FBSDP_NO & PSDP_NO & SBDR_SSDP_NO): "
if [ "$cap_sbdr_ssdp_no" = -1 ]; then
pstatus yellow UNKNOWN "couldn't read MSR"
elif is_arch_cap_mmio_immune; then
pstatus green YES
else
pstatus yellow NO
fi
pr_info_nol " * CPU microcode supports Fill Buffer clearing (FB_CLEAR): "
if [ "$cap_fb_clear" = -1 ]; then
pstatus yellow UNKNOWN "couldn't read MSR"
elif [ "$cap_fb_clear" = 1 ]; then
pstatus green YES
else
pstatus yellow NO
fi
pr_info_nol " * CPU explicitly indicates not being affected by RFDS (RFDS_NO): "
if [ "$cap_rfds_no" = -1 ]; then
pstatus yellow UNKNOWN "couldn't read MSR"
elif [ "$cap_rfds_no" = 1 ]; then
pstatus green YES
else
pstatus yellow NO
fi
pr_info_nol " * CPU microcode supports clearing register files (RFDS_CLEAR): "
if [ "$cap_rfds_clear" = -1 ]; then
pstatus yellow UNKNOWN "couldn't read MSR"
elif [ "$cap_rfds_clear" = 1 ]; then
pstatus green YES
else
pstatus yellow NO
fi
fi
if is_amd || is_hygon; then
pr_info " * Selective Branch Predictor Barrier (SBPB)"
pr_info_nol " * PRED_CMD MSR supports SBPB bit write: "
if [ "$opt_allow_msr_write" = 1 ]; then
# the MSR PRED_SBPB is at offset 0x49, BIT(7), write-only
write_msr 0x49 128
ret=$?
if [ $ret = $WRITE_MSR_RET_OK ]; then
pstatus green YES
cap_sbpb=1
elif [ $ret = $WRITE_MSR_RET_KO ]; then
pstatus yellow NO
cap_sbpb=2
else
pstatus yellow UNKNOWN "$ret_write_msr_msg"
cap_sbpb=3
fi
else
pstatus yellow UNKNOWN "not allowed to write msr"
cap_sbpb=3
fi
fi
if is_amd || is_hygon; then
pr_info " * Transient Scheduler Attacks"
pr_info_nol " * CPU indicates TSA_SQ_NO: "
cap_tsa_sq_no=''
read_cpuid 0x80000021 0x0 $ECX 1 1 1
ret=$?
if [ $ret = $READ_CPUID_RET_OK ]; then
pstatus green YES
cap_tsa_sq_no=1
elif [ $ret = $READ_CPUID_RET_KO ]; then
pstatus yellow NO
cap_tsa_sq_no=0
else
pstatus yellow UNKNOWN "$ret_read_cpuid_msg"
fi
pr_info_nol " * CPU indicates TSA_L1_NO: "
cap_tsa_l1_no=''
read_cpuid 0x80000021 0x0 $ECX 2 1 1
ret=$?
if [ $ret = $READ_CPUID_RET_OK ]; then
pstatus green YES
cap_tsa_l1_no=1
elif [ $ret = $READ_CPUID_RET_KO ]; then
pstatus yellow NO
cap_tsa_l1_no=0
else
pstatus yellow UNKNOWN "$ret_read_cpuid_msg"
fi
pr_info_nol " * CPU indicates VERW clears CPU buffers: "
cap_verw_clear=''
read_cpuid 0x80000021 0x0 $EAX 5 1 1
ret=$?
if [ $ret = $READ_CPUID_RET_OK ]; then
pstatus green YES
cap_verw_clear=1
elif [ $ret = $READ_CPUID_RET_KO ]; then
pstatus yellow NO
cap_verw_clear=0
else
pstatus yellow UNKNOWN "$ret_read_cpuid_msg"
fi
pr_info_nol " * CPU indicates AutoIBRS capability: "
cap_autoibrs=''
read_cpuid 0x80000021 0x0 $EAX 8 1 1
ret=$?
if [ $ret = $READ_CPUID_RET_OK ]; then
pstatus green YES
cap_autoibrs=1
elif [ $ret = $READ_CPUID_RET_KO ]; then
pstatus yellow NO
cap_autoibrs=0
else
pstatus yellow UNKNOWN "$ret_read_cpuid_msg"
fi
fi
pr_info_nol " * CPU supports Transactional Synchronization Extensions (TSX): "
ret=$READ_CPUID_RET_KO
cap_rtm=0
if is_intel; then
read_cpuid 0x7 0x0 $EBX 11 1 1
ret=$?
fi
if [ $ret = $READ_CPUID_RET_OK ]; then
cap_rtm=1
pstatus green YES "RTM feature bit"
elif [ $ret = $READ_CPUID_RET_KO ]; then
pstatus yellow NO
else
cap_rtm=-1
pstatus yellow UNKNOWN "$ret_read_cpuid_msg"
fi
pr_info_nol " * CPU supports TSX Force Abort (TSX_FORCE_ABORT): "
ret=$READ_CPUID_RET_KO
cap_tsx_force_abort=0
if is_intel; then
read_cpuid 0x7 0x0 $EDX 13 1 1
ret=$?
fi
if [ $ret = $READ_CPUID_RET_OK ]; then
cap_tsx_force_abort=1
pstatus blue YES
elif [ $ret = $READ_CPUID_RET_KO ]; then
pstatus yellow NO
else
cap_tsx_force_abort=-1
pstatus yellow UNKNOWN "$ret_read_cpuid_msg"
fi
# IA32_TSX_FORCE_ABORT (MSR 0x10F): stopgap for older Skylake/Kaby Lake CPUs that
# don't support IA32_TSX_CTRL, forces all RTM transactions to abort via microcode update
if [ "$cap_tsx_force_abort" = 1 ]; then
read_msr $MSR_IA32_TSX_FORCE_ABORT
ret=$?
if [ "$ret" = $READ_MSR_RET_OK ]; then
cap_tsx_force_abort_rtm_disable=$((ret_read_msr_value_lo >> 0 & 1))
cap_tsx_force_abort_cpuid_clear=$((ret_read_msr_value_lo >> 1 & 1))
fi
pr_info_nol " * TSX_FORCE_ABORT MSR indicates all TSX transactions are aborted: "
if [ "$cap_tsx_force_abort_rtm_disable" = 1 ]; then
pstatus blue YES
elif [ "$cap_tsx_force_abort_rtm_disable" = 0 ]; then
pstatus blue NO
else
# shellcheck disable=SC2154
pstatus yellow UNKNOWN "$ret_read_msr_0x10f_msg"
fi
pr_info_nol " * TSX_FORCE_ABORT MSR indicates TSX CPUID bit is cleared: "
if [ "$cap_tsx_force_abort_cpuid_clear" = 1 ]; then
pstatus blue YES
elif [ "$cap_tsx_force_abort_cpuid_clear" = 0 ]; then
pstatus blue NO
else
# shellcheck disable=SC2154
pstatus yellow UNKNOWN "$ret_read_msr_0x10f_msg"
fi
fi
pr_info_nol " * CPU supports Software Guard Extensions (SGX): "
ret=$READ_CPUID_RET_KO
cap_sgx=0
if is_intel; then
read_cpuid 0x7 0x0 $EBX 2 1 1
ret=$?
fi
if [ $ret = $READ_CPUID_RET_OK ]; then
pstatus blue YES
cap_sgx=1
elif [ $ret = $READ_CPUID_RET_KO ]; then
pstatus green NO
else
cap_sgx=-1
pstatus yellow UNKNOWN "$ret_read_cpuid_msg"
fi
pr_info_nol " * CPU supports Special Register Buffer Data Sampling (SRBDS): "
# A processor supports SRBDS if it enumerates CPUID (EAX=7H,ECX=0):EDX[9] as 1
# That means the mitigation disabling SRBDS exists
ret=$READ_CPUID_RET_KO
cap_srbds=0
cap_srbds_on=0
if is_intel; then
read_cpuid 0x7 0x0 $EDX 9 1 1
ret=$?
fi
if [ $ret = $READ_CPUID_RET_OK ]; then
pstatus blue YES
cap_srbds=1
read_msr $MSR_IA32_MCU_OPT_CTRL
ret=$?
if [ $ret = $READ_MSR_RET_OK ]; then
if [ "$((ret_read_msr_value_lo >> 0 & 1))" = 0 ]; then
#SRBDS mitigation control exists and is enabled via microcode (RNGDS_MITG_DIS bit is 0)
cap_srbds_on=1
else
#SRBDS mitigation control exists but is disabled via microcode (RNGDS_MITG_DIS bit is 1)
cap_srbds_on=0
fi
else
cap_srbds_on=-1
fi
elif [ $ret = $READ_CPUID_RET_KO ]; then
pstatus green NO
else
pstatus yellow UNKNOWN "$ret_read_cpuid_msg"
cap_srbds=0
fi
if is_amd; then
pr_info_nol " * CPU microcode is known to fix Zenbleed: "
has_zenbleed_fixed_firmware
ret=$?
if [ $ret -eq 0 ]; then
# affected CPU, new fw
pstatus green YES
elif [ $ret -eq 1 ]; then
# affected CPU, old fw
pstatus red NO "required version: $g_zenbleed_fw_required"
else
# unaffected CPU
pstatus yellow NO
fi
fi
pr_info_nol " * CPU microcode is known to cause stability problems: "
if is_ucode_blacklisted; then
pstatus red YES "$g_ucode_found"
pr_warn
pr_warn "The microcode your CPU is running on is known to cause instability problems,"
pr_warn "such as intempestive reboots or random crashes."
pr_warn "You are advised to either revert to a previous microcode version (that might not have"
pr_warn "the mitigations for recent vulnerabilities), or upgrade to a newer one if available."
pr_warn
else
pstatus blue NO "$g_ucode_found"
fi
pr_info_nol " * CPU microcode is the latest known available version: "
is_latest_known_ucode
ret=$?
if [ $ret -eq 0 ]; then
pstatus green YES "$ret_is_latest_known_ucode_latest"
elif [ $ret -eq 1 ]; then
pstatus red NO "$ret_is_latest_known_ucode_latest"
else
pstatus blue UNKNOWN "$ret_is_latest_known_ucode_latest"
fi
if is_running_as_guest; then
pr_warn
pr_warn "Note: this system is running inside a VM ($g_is_guest_vm_reason)."
pr_warn "The hypervisor may be faking the CPU model and microcode version;"
pr_warn "verify the above microcode information on the hypervisor host for accuracy."
pr_warn
fi
}
# Display per-CVE CPU vulnerability status based on CPU model/family.
# Mirrors the main dispatch gate: under a default "all CVEs" run, skip CVEs
# whose arch tag doesn't match this system. Explicit selection via
# --cve/--variant/--errata bypasses the gate.
check_cpu_vulnerabilities() {
local cve
pr_info "* CPU vulnerability to the speculative execution attack variants"
for cve in $g_supported_cve_list; do
if [ "$opt_cve_all" = 1 ]; then
_is_cve_relevant_arch "$cve" || continue
elif ! echo "$opt_cve_list" | grep -qw "$cve"; then
continue
fi
pr_info_nol " * Affected by $cve ($(cve2name "$cve")): "
if is_cpu_affected "$cve"; then
pstatus yellow YES
else
pstatus green NO
fi
done
}
# Detect Red Hat/Canonical backported Spectre mitigations in the kernel binary
# Sets: g_redhat_canonical_spectre
check_redhat_canonical_spectre() {
# if we were already called, don't do it again
[ -n "${g_redhat_canonical_spectre:-}" ] && return
if ! command -v "${opt_arch_prefix}strings" >/dev/null 2>&1; then
g_redhat_canonical_spectre=-1
elif [ -n "$g_kernel_err" ]; then
g_redhat_canonical_spectre=-2
else
# Red Hat / Ubuntu specific affected_variant1 patch is difficult to detect,
# let's use the two same tricks than the official Red Hat detection script uses:
if "${opt_arch_prefix}strings" "$g_kernel" | grep -qw noibrs && "${opt_arch_prefix}strings" "$g_kernel" | grep -qw noibpb; then
# 1) detect their specific affected_variant2 patch. If it's present, it means
# that the affected_variant1 patch is also present (both were merged at the same time)
pr_debug "found redhat/canonical version of the affected_variant2 patch (implies affected_variant1)"
g_redhat_canonical_spectre=1
elif "${opt_arch_prefix}strings" "$g_kernel" | grep -q 'x86/pti:'; then
# 2) detect their specific affected_variant3 patch. If it's present, but the affected_variant2
# is not, it means that only affected_variant1 is present in addition to affected_variant3
pr_debug "found redhat/canonical version of the affected_variant3 patch (implies affected_variant1 but not affected_variant2)"
g_redhat_canonical_spectre=2
else
g_redhat_canonical_spectre=0
fi
fi
}
# Detect whether this system is hosting virtual machines (hypervisor check).
# Detection runs only on the first call; subsequent calls reuse the cached
# result. The status line is always printed so each CVE section shows the
# hypervisor context to the user.
# Sets: g_has_vmm, g_has_vmm_reason
check_has_vmm() {
local binary pid
pr_info_nol "* This system is a host running a hypervisor: "
if [ "$g_has_vmm_cached" != 1 ]; then
g_has_vmm=$opt_vmm
if [ "$g_has_vmm" != -1 ]; then
# --vmm was explicitly set on the command line
g_has_vmm_reason="forced from command line"
elif [ "$opt_paranoid" = 1 ]; then
# In paranoid mode, if --vmm was not specified on the command-line,
# we want to be secure before everything else, so assume we're running
# a hypervisor, as this requires more mitigations
g_has_vmm=1
g_has_vmm_reason="paranoid mode"
else
# Here, we want to know if we are hosting a hypervisor, and running some VMs on it.
# If we find no evidence that this is the case, assume we're not (to avoid scaring users),
# this can always be overridden with --vmm in any case.
g_has_vmm=0
if command -v pgrep >/dev/null 2>&1; then
# Exclude xenbus/xenwatch (present inside domU guests) and
# libvirtd (also manages containers, not just VMs).
# Use pgrep -x (exact match) for most binaries. QEMU is
# special: the binary is almost never just "qemu" — it is
# "qemu-system-x86_64", "qemu-system-aarch64", etc. We
# keep "qemu" for the rare wrapper/symlink case and add
# "qemu-system-" as a substring match via a separate pgrep
# call (without -x) to catch all qemu-system-* variants.
# Kernel threads (e.g. [kvm-irqfd-clean]) are filtered out
# below via the /proc/$pid/exe symlink check.
# Note: the kernel truncates process names to 15 chars
# (TASK_COMM_LEN), so pgrep -x can't match longer names.
# "cloud-hypervisor" (16 chars) is handled in the substring
# block below alongside qemu-system-*.
for binary in qemu kvm xenstored xenconsoled \
VBoxHeadless VBoxSVC vmware-vmx firecracker bhyve; do
for pid in $(pgrep -x "$binary"); do
# resolve the exe symlink, if it doesn't resolve with -m,
# which doesn't even need the dest to exist, it means the symlink
# is null, which is the case for kernel threads: ignore those to
# avoid false positives (such as [kvm-irqfd-clean] under at least RHEL 7.6/7.7)
if ! [ "$(readlink -m "/proc/$pid/exe")" = "/proc/$pid/exe" ]; then
pr_debug "g_has_vmm: found PID $pid ($binary)"
g_has_vmm=1
g_has_vmm_reason="$binary process found (PID $pid)"
fi
done
done
# substring matches for names that pgrep -x can't handle:
# - qemu-system-*: variable suffix (x86_64, aarch64, ...)
# - cloud-hypervisor: 16 chars, exceeds TASK_COMM_LEN (15)
if [ "$g_has_vmm" = 0 ]; then
for binary in "qemu-system-" "cloud-hyperviso"; do
for pid in $(pgrep "$binary"); do
if ! [ "$(readlink -m "/proc/$pid/exe")" = "/proc/$pid/exe" ]; then
pr_debug "g_has_vmm: found PID $pid ($binary*)"
g_has_vmm=1
g_has_vmm_reason="$binary* process found (PID $pid)"
fi
done
done
fi
unset binary pid
else
# ignore SC2009 as `ps ax` is actually used as a fallback if `pgrep` isn't installed
# shellcheck disable=SC2009
if command -v ps >/dev/null && ps ax | grep -vw grep | grep -q \
-e '\<qemu' -e '/qemu' -e '\<kvm' -e '/kvm' \
-e '/xenstored' -e '/xenconsoled' \
-e '\<VBoxHeadless' -e '\<VBoxSVC' -e '\<vmware-vmx' \
-e '\<firecracker' -e '\<cloud-hypervisor' -e '\<bhyve'; then
g_has_vmm=1
g_has_vmm_reason="hypervisor process found"
fi
fi
fi
g_has_vmm_cached=1
fi
if [ "$g_has_vmm" = 0 ]; then
pstatus green NO "$g_has_vmm_reason"
else
pstatus blue YES "$g_has_vmm_reason"
fi
}