# 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" # 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 ] || [ "$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_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 [ "$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 kernel image, config, and System.map availability check_kernel_info() { local config_display pr_info "\033[1;34mKernel information\033[0m" if [ "$opt_live" = 1 ]; 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 } # Display hardware-level CPU mitigation support (microcode features, ARCH_CAPABILITIES, etc.) check_cpu() { local capabilities ret spec_ctrl_msr codename ucode_str 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 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 [ $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 # 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 "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 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 [ $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 # 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_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_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_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 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 # 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 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_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 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 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 [ "$cap_sbdr_ssdp_no" = 1 ] && [ "$cap_fbsdp_no" = 1 ] && [ "$cap_psdp_no" = 1 ]; 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 pstatus yellow UNKNOWN "couldn't read MSR" 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 pstatus yellow UNKNOWN "couldn't read MSR" 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 } # 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). # 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 '\