mirror of
https://github.com/speed47/spectre-meltdown-checker.git
synced 2026-08-15 16:25:36 +02:00
cb48e1ace8
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)
1597 lines
61 KiB
Bash
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
|
|
}
|