Files
spectre-meltdown-checker/src/libs/400_hw_check.sh
2026-03-31 20:16:47 +00:00

1179 lines
43 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 [ "$opt_hw_only" = 1 ]; 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 [ "$opt_live" = 1 ]; then
pr_info "Checking for vulnerabilities on current system"
pr_info "Kernel is \033[35m$g_os $(uname -r) $(uname -v) $(uname -m)\033[0m"
pr_info "CPU is \033[35m$cpu_friendly_name\033[0m"
# 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"
pr_info "CPU is \033[35m$cpu_friendly_name\033[0m"
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
if [ "${g_bad_accuracy:=0}" = 1 ]; then
pr_warn "We're missing some kernel info (see -v), accuracy might be reduced"
fi
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 ] || [ "$opt_hw_only" = 1 ]; 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 [ "$opt_live" = 1 ]; 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_info "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 msg mockvarname
file="$1"
regex="${2:-}"
mode="${3:-}"
msg=''
ret_sys_interface_check_fullmsg=''
if [ "$opt_live" = 1 ] && [ "$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
return 0
elif [ "$mode" = quiet ]; then
pr_info "* Information from the /sys interface: $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
pr_debug "sys_interface_check: $file=$msg (re=$regex)"
return 0
}
# Display hardware-level CPU mitigation support (microcode features, ARCH_CAPABILITIES, etc.)
check_cpu() {
local capabilities ret spec_ctrl_msr
pr_info "\033[1;34mHardware check\033[0m"
if ! uname -m | grep -qwE 'x86_64|i[3-6]86|amd64'; then
return
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: "
# the new MSR 'SPEC_CTRL' is at offset 0x48
read_msr 0x48
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 [ $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_KO ]; then
pstatus yellow NO
else
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 "is msr kernel module available?"
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
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"
#cpuid_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"
#cpuid_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"
#cpuid_stibp='HYGON STIBP'
fi
else
ret=invalid
pstatus yellow UNKNOWN "unknown CPU"
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 [ -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
write_msr 0x10b
ret=$?
if [ $ret = $WRITE_MSR_RET_OK ]; then
pstatus green YES
cap_flush_cmd=1
elif [ $ret = $WRITE_MSR_RET_KO ]; then
pstatus yellow NO
cap_flush_cmd=0
else
pstatus yellow UNKNOWN "$ret_write_msr_msg"
cap_flush_cmd=-1
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_KO ]; then
pstatus yellow NO
cap_l1df=0
else
pstatus yellow UNKNOWN "$ret_read_cpuid_msg"
cap_l1df=-1
fi
# if we weren't allowed to probe the write-only MSR but the CPUID
# bit says that it shoul be there, make the assumption that it is
if [ "$opt_allow_msr_write" != 1 ]; then
cap_flush_cmd=$cap_l1df
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_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=$?
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
# make shellcheck happy while we're not yet using these new cpuid values in our checks
export cap_ipred cap_rrsba cap_bhi
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_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
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
pstatus yellow NO
else
# the new MSR 'ARCH_CAPABILITIES' is at offset 0x10a
read_msr 0x10a
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
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 (decimal)"
[ $((capabilities >> 0 & 1)) -eq 1 ] && cap_rdcl_no=1
[ $((capabilities >> 1 & 1)) -eq 1 ] && cap_ibrs_all=1
[ $((capabilities >> 2 & 1)) -eq 1 ] && cap_rsba=1
[ $((capabilities >> 3 & 1)) -eq 1 ] && cap_l1dflush_no=1
[ $((capabilities >> 4 & 1)) -eq 1 ] && cap_ssb_no=1
[ $((capabilities >> 5 & 1)) -eq 1 ] && cap_mds_no=1
[ $((capabilities >> 6 & 1)) -eq 1 ] && cap_pschange_msc_no=1
[ $((capabilities >> 7 & 1)) -eq 1 ] && cap_tsx_ctrl_msr=1
[ $((capabilities >> 8 & 1)) -eq 1 ] && cap_taa_no=1
[ $((capabilities >> 25 & 1)) -eq 1 ] && cap_gds_ctrl=1
[ $((capabilities >> 26 & 1)) -eq 1 ] && cap_gds_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"
if [ "$cap_ibrs_all" = 1 ]; then
pstatus green YES
else
pstatus yellow NO
fi
elif [ $ret = $READ_MSR_RET_KO ]; then
pstatus yellow NO
else
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
if [ "$cap_tsx_ctrl_msr" = 1 ]; then
read_msr 0x122
ret=$?
if [ "$ret" = $READ_MSR_RET_OK ]; then
g_tsx_ctrl_msr=$ret_read_msr_value
cap_tsx_ctrl_rtm_disable=$((g_tsx_ctrl_msr >> 0 & 1))
cap_tsx_ctrl_cpuid_clear=$((g_tsx_ctrl_msr >> 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
pstatus yellow UNKNOWN "couldn't read MSR"
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
pstatus yellow UNKNOWN "couldn't read MSR"
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 the IA32_MCU_OPT_CTRL MSR
read_msr 0x123
ret=$?
if [ "$ret" = $READ_MSR_RET_OK ]; then
g_mcu_opt_ctrl=$ret_read_msr_value
cap_gds_mitg_dis=$((g_mcu_opt_ctrl >> 4 & 1))
cap_gds_mitg_lock=$((g_mcu_opt_ctrl >> 5 & 1))
fi
pr_info_nol " * GDS microcode mitigation is disabled (GDS_MITG_DIS): "
if [ "$cap_gds_mitg_dis" = -1 ]; then
pstatus yellow UNKNOWN "couldn't read MSR"
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
pstatus yellow UNKNOWN "couldn't read MSR"
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
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
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 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 0x123
ret=$?
if [ $ret = $READ_MSR_RET_OK ]; then
if [ "$ret_read_msr_value" = 0 ]; then
#SRBDS mitigation control exists and is enabled via microcode
cap_srbds_on=1
else
#SRBDS mitigation control exists but is disabled via microcode
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
}
# Display per-CVE CPU vulnerability status based on CPU model/family
check_cpu_vulnerabilities() {
local cve
pr_info "* CPU vulnerability to the speculative execution attack variants"
for cve in $g_supported_cve_list; do
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)
# Sets: g_has_vmm
check_has_vmm() {
local binary pid
pr_info_nol "* This system is a host running a hypervisor: "
g_has_vmm=$opt_vmm
if [ "$g_has_vmm" = -1 ] && [ "$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=2
elif [ "$g_has_vmm" = -1 ]; then
# 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
# remove xenbus and xenwatch, also present inside domU
# remove libvirtd as it can also be used to manage containers and not VMs
# for each binary we want to grep, get the pids
for binary in qemu kvm xenstored xenconsoled; 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"
g_has_vmm=1
fi
done
done
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'; then
g_has_vmm=1
fi
fi
fi
if [ "$g_has_vmm" = 0 ]; then
if [ "$opt_vmm" != -1 ]; then
pstatus green NO "forced from command line"
else
pstatus green NO
fi
else
if [ "$opt_vmm" != -1 ]; then
pstatus blue YES "forced from command line"
elif [ "$g_has_vmm" = 2 ]; then
pstatus blue YES "paranoid mode"
else
pstatus blue YES
fi
fi
}