Merge remote-tracking branch 'origin/master' into check_msr_on_each_cpu

This commit is contained in:
Oleksandr Bazhaniuk 2018-03-12 18:14:13 -07:00
commit 2282910d06
2 changed files with 116 additions and 36 deletions

View File

@ -8,6 +8,28 @@ You can also specify a kernel image on the command line, if you'd like to inspec
The script will do its best to detect mitigations, including backported non-vanilla patches, regardless of the advertised kernel version number. The script will do its best to detect mitigations, including backported non-vanilla patches, regardless of the advertised kernel version number.
## Easy way to run the script
- Get the latest version of the script using `curl` *or* `wget`
```bash
curl -L https://meltdown.ovh -o spectre-meltdown-checker.sh
wget https://meltdown.ovh -O spectre-meltdown-checker.sh
```
- Inspect the script. You never blindly run scripts you downloaded from the Internet, do you?
```bash
vim spectre-meltdown-checker.sh
```
- When you're ready, run the script as root
```bash
chmod +x spectre-meltdown-checker.sh
sudo ./spectre-meltdown-checker.sh
```
## Example of script output ## Example of script output
- Intel Haswell CPU running under Ubuntu 16.04 LTS - Intel Haswell CPU running under Ubuntu 16.04 LTS

View File

@ -4,11 +4,12 @@
# Check for the latest version at: # Check for the latest version at:
# https://github.com/speed47/spectre-meltdown-checker # https://github.com/speed47/spectre-meltdown-checker
# git clone https://github.com/speed47/spectre-meltdown-checker.git # git clone https://github.com/speed47/spectre-meltdown-checker.git
# or wget https://raw.githubusercontent.com/speed47/spectre-meltdown-checker/master/spectre-meltdown-checker.sh # or wget meltdown.ovh -O spectre-meltdown-checker.sh
# or curl -L meltdown.ovh -o spectre-meltdown-checker.sh
# #
# Stephane Lesimple # Stephane Lesimple
# #
VERSION='0.34+' VERSION='0.35'
trap 'exit_cleanup' EXIT trap 'exit_cleanup' EXIT
trap '_warn "interrupted, cleaning up..."; exit_cleanup; exit 1' INT trap '_warn "interrupted, cleaning up..."; exit_cleanup; exit 1' INT
@ -52,6 +53,7 @@ show_usage()
--batch text Produce machine readable output, this is the default if --batch is specified alone --batch text Produce machine readable output, this is the default if --batch is specified alone
--batch json Produce JSON output formatted for Puppet, Ansible, Chef... --batch json Produce JSON output formatted for Puppet, Ansible, Chef...
--batch nrpe Produce machine readable output formatted for NRPE --batch nrpe Produce machine readable output formatted for NRPE
--batch prometheus Produce output for consumption by prometheus-node-exporter
--variant [1,2,3] Specify which variant you'd like to check, by default all variants are checked --variant [1,2,3] Specify which variant you'd like to check, by default all variants are checked
Can be specified multiple times (e.g. --variant 2 --variant 3) Can be specified multiple times (e.g. --variant 2 --variant 3)
@ -337,13 +339,13 @@ is_cpu_specex_free()
return 0 return 0
fi fi
fi fi
[ "$cpu_family" -eq 4 ] && return 0 [ "$cpu_family" = 4 ] && return 0
return 1 return 1
} }
show_header() show_header()
{ {
_info "\033[1;34mSpectre and Meltdown mitigation detection tool v$VERSION\033[0m" _info "Spectre and Meltdown mitigation detection tool v$VERSION"
_info _info
} }
@ -414,7 +416,7 @@ while [ -n "$1" ]; do
opt_verbose=0 opt_verbose=0
shift shift
case "$1" in case "$1" in
text|nrpe|json) opt_batch_format="$1"; shift;; text|nrpe|json|prometheus) opt_batch_format="$1"; shift;;
--*) ;; # allow subsequent flags --*) ;; # allow subsequent flags
'') ;; # allow nothing at all '') ;; # allow nothing at all
*) *)
@ -492,14 +494,15 @@ pstatus()
pvulnstatus() pvulnstatus()
{ {
if [ "$opt_batch" = 1 ]; then if [ "$opt_batch" = 1 ]; then
case "$1" in
CVE-2017-5753) aka="SPECTRE VARIANT 1";;
CVE-2017-5715) aka="SPECTRE VARIANT 2";;
CVE-2017-5754) aka="MELTDOWN";;
esac
case "$opt_batch_format" in case "$opt_batch_format" in
text) _echo 0 "$1: $2 ($3)";; text) _echo 0 "$1: $2 ($3)";;
json) json)
case "$1" in
CVE-2017-5753) aka="SPECTRE VARIANT 1";;
CVE-2017-5715) aka="SPECTRE VARIANT 2";;
CVE-2017-5754) aka="MELTDOWN";;
esac
case "$2" in case "$2" in
UNK) is_vuln="null";; UNK) is_vuln="null";;
VULN) is_vuln="true";; VULN) is_vuln="true";;
@ -509,6 +512,9 @@ pvulnstatus()
;; ;;
nrpe) [ "$2" = VULN ] && nrpe_vuln="$nrpe_vuln $1";; nrpe) [ "$2" = VULN ] && nrpe_vuln="$nrpe_vuln $1";;
prometheus)
prometheus_output="${prometheus_output:+$prometheus_output\n}specex_vuln_status{name=\"$aka\",cve=\"$1\",status=\"$2\",info=\"$3\"} 1"
;;
esac esac
fi fi
@ -601,6 +607,7 @@ extract_vmlinux()
try_decompress '\135\0\0\0' xxx unlzma '' xz-utils "$1" && return 0 try_decompress '\135\0\0\0' xxx unlzma '' xz-utils "$1" && return 0
try_decompress '\211\114\132' xy 'lzop' '-d' lzop "$1" && return 0 try_decompress '\211\114\132' xy 'lzop' '-d' lzop "$1" && return 0
try_decompress '\002\041\114\030' xyy 'lz4' '-d -l' liblz4-tool "$1" && return 0 try_decompress '\002\041\114\030' xyy 'lz4' '-d -l' liblz4-tool "$1" && return 0
try_decompress '\177ELF' xxy 'cat' '' cat "$1" && return 0
return 1 return 1
} }
@ -780,22 +787,24 @@ is_ucode_blacklisted()
[ "$cpu_family" = 6 ] || return 1 [ "$cpu_family" = 6 ] || return 1
# now, check each known bad microcode # now, check each known bad microcode
# source: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/kernel/cpu/intel.c#n105 # source: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/kernel/cpu/intel.c#n105
# 2018-02-08 update: https://newsroom.intel.com/wp-content/uploads/sites/11/2018/02/microcode-update-guidance.pdf
# model,stepping,microcode # model,stepping,microcode
ucode_found="model $cpu_model stepping $cpu_stepping ucode $cpu_ucode" ucode_found="model $cpu_model stepping $cpu_stepping ucode $cpu_ucode"
for tuple in \ for tuple in \
$INTEL_FAM6_KABYLAKE_DESKTOP,0x0B,0x84 \ $INTEL_FAM6_KABYLAKE_DESKTOP,0x0B,0x80 \
$INTEL_FAM6_KABYLAKE_DESKTOP,0x0A,0x84 \ $INTEL_FAM6_KABYLAKE_DESKTOP,0x0A,0x80 \
$INTEL_FAM6_KABYLAKE_DESKTOP,0x09,0x84 \ $INTEL_FAM6_KABYLAKE_DESKTOP,0x09,0x80 \
$INTEL_FAM6_KABYLAKE_MOBILE,0x0A,0x84 \ $INTEL_FAM6_KABYLAKE_MOBILE,0x0A,0x80 \
$INTEL_FAM6_KABYLAKE_MOBILE,0x09,0x84 \ $INTEL_FAM6_KABYLAKE_MOBILE,0x09,0x80 \
$INTEL_FAM6_SKYLAKE_X,0x03,0x0100013e \ $INTEL_FAM6_SKYLAKE_X,0x03,0x0100013e \
$INTEL_FAM6_SKYLAKE_X,0x04,0x02000036 \
$INTEL_FAM6_SKYLAKE_X,0x04,0x0200003a \
$INTEL_FAM6_SKYLAKE_X,0x04,0x0200003c \ $INTEL_FAM6_SKYLAKE_X,0x04,0x0200003c \
$INTEL_FAM6_SKYLAKE_MOBILE,0x03,0xc2 \
$INTEL_FAM6_SKYLAKE_DESKTOP,0x03,0xc2 \
$INTEL_FAM6_BROADWELL_CORE,0x04,0x28 \ $INTEL_FAM6_BROADWELL_CORE,0x04,0x28 \
$INTEL_FAM6_BROADWELL_GT3E,0x01,0x1b \ $INTEL_FAM6_BROADWELL_GT3E,0x01,0x1b \
$INTEL_FAM6_BROADWELL_XEON_D,0x02,0x14 \ $INTEL_FAM6_BROADWELL_XEON_D,0x02,0x14 \
$INTEL_FAM6_BROADWELL_XEON_D,0x03,0x07000011 \ $INTEL_FAM6_BROADWELL_XEON_D,0x03,0x07000011 \
$INTEL_FAM6_BROADWELL_X,0x01,0x0b000023 \
$INTEL_FAM6_BROADWELL_X,0x01,0x0b000025 \ $INTEL_FAM6_BROADWELL_X,0x01,0x0b000025 \
$INTEL_FAM6_HASWELL_ULT,0x01,0x21 \ $INTEL_FAM6_HASWELL_ULT,0x01,0x21 \
$INTEL_FAM6_HASWELL_GT3E,0x01,0x18 \ $INTEL_FAM6_HASWELL_GT3E,0x01,0x18 \
@ -803,7 +812,6 @@ is_ucode_blacklisted()
$INTEL_FAM6_HASWELL_X,0x02,0x3b \ $INTEL_FAM6_HASWELL_X,0x02,0x3b \
$INTEL_FAM6_HASWELL_X,0x04,0x10 \ $INTEL_FAM6_HASWELL_X,0x04,0x10 \
$INTEL_FAM6_IVYBRIDGE_X,0x04,0x42a \ $INTEL_FAM6_IVYBRIDGE_X,0x04,0x42a \
$INTEL_FAM6_ATOM_GEMINI_LAKE,0x01,0x22 \
$INTEL_FAM6_SANDYBRIDGE_X,0x06,0x61b \ $INTEL_FAM6_SANDYBRIDGE_X,0x06,0x61b \
$INTEL_FAM6_SANDYBRIDGE_X,0x07,0x712 $INTEL_FAM6_SANDYBRIDGE_X,0x07,0x712
do do
@ -910,6 +918,8 @@ if [ "$opt_live" = 1 ]; then
[ -e "/boot/vmlinuz-linux" ] && opt_kernel="/boot/vmlinuz-linux" [ -e "/boot/vmlinuz-linux" ] && opt_kernel="/boot/vmlinuz-linux"
# Linux-Libre: # Linux-Libre:
[ -e "/boot/vmlinuz-linux-libre" ] && opt_kernel="/boot/vmlinuz-linux-libre" [ -e "/boot/vmlinuz-linux-libre" ] && opt_kernel="/boot/vmlinuz-linux-libre"
# pine64
[ -e "/boot/pine64/Image" ] && opt_kernel="/boot/pine64/Image"
# generic: # generic:
[ -e "/boot/vmlinuz-$(uname -r)" ] && opt_kernel="/boot/vmlinuz-$(uname -r)" [ -e "/boot/vmlinuz-$(uname -r)" ] && opt_kernel="/boot/vmlinuz-$(uname -r)"
[ -e "/boot/kernel-$( uname -r)" ] && opt_kernel="/boot/kernel-$( uname -r)" [ -e "/boot/kernel-$( uname -r)" ] && opt_kernel="/boot/kernel-$( uname -r)"
@ -918,6 +928,8 @@ if [ "$opt_live" = 1 ]; then
[ -e "/boot/kernel-genkernel-$(uname -m)-$(uname -r)" ] && opt_kernel="/boot/kernel-genkernel-$(uname -m)-$(uname -r)" [ -e "/boot/kernel-genkernel-$(uname -m)-$(uname -r)" ] && opt_kernel="/boot/kernel-genkernel-$(uname -m)-$(uname -r)"
# NixOS: # NixOS:
[ -e "/run/booted-system/kernel" ] && opt_kernel="/run/booted-system/kernel" [ -e "/run/booted-system/kernel" ] && opt_kernel="/run/booted-system/kernel"
# 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"
fi fi
# system.map # system.map
@ -995,6 +1007,10 @@ if [ -z "$vmlinux" ] || [ ! -r "$vmlinux" ]; then
[ -z "$vmlinux_err" ] && vmlinux_err="couldn't extract your kernel from $opt_kernel" [ -z "$vmlinux_err" ] && vmlinux_err="couldn't extract your kernel from $opt_kernel"
else else
vmlinux_version=$(strings "$vmlinux" 2>/dev/null | grep '^Linux version ' | head -1) vmlinux_version=$(strings "$vmlinux" 2>/dev/null | grep '^Linux version ' | head -1)
if [ -z "$vmlinux_version" ]; then
# try harder with some kernels (such as Red Hat) that don't have ^Linux version before their version string
vmlinux_version=$(strings "$vmlinux" 2>/dev/null | grep -E '^[[:alnum:]][^[:space:]]+ \([^[:space:]]+\) #[0-9]+ .+ (19|20)[0-9][0-9]$' | head -1)
fi
if [ -n "$vmlinux_version" ]; then if [ -n "$vmlinux_version" ]; then
# in live mode, check if the img we found is the correct one # in live mode, check if the img we found is the correct one
if [ "$opt_live" = 1 ]; then if [ "$opt_live" = 1 ]; then
@ -1006,6 +1022,8 @@ else
else else
_info "Kernel image is \033[35m$vmlinux_version" _info "Kernel image is \033[35m$vmlinux_version"
fi fi
else
_verbose "Kernel image version is unknown"
fi fi
fi fi
@ -1318,7 +1336,7 @@ check_cpu()
_warn "the mitigations for Spectre), or upgrade to a newer one if available." _warn "the mitigations for Spectre), or upgrade to a newer one if available."
_warn _warn
else else
pstatus green NO "$ucode_found" pstatus blue NO "$ucode_found"
fi fi
_info "* CPU vulnerability to the three speculative execution attacks variants" _info "* CPU vulnerability to the three speculative execution attacks variants"
@ -1334,6 +1352,30 @@ check_cpu()
_info _info
} }
check_redhat_canonical_spectre()
{
# if we were already called, don't do it again
[ -n "$redhat_canonical_spectre" ] && return
if ! which strings >/dev/null 2>&1; then
redhat_canonical_spectre=-1
elif [ -n "$vmlinux_err" ]; then
redhat_canonical_spectre=-2
else
# Red Hat / Ubuntu specific variant1 patch is difficult to detect,
# let's use the same way than the official Red Hat detection script,
# and detect their specific variant2 patch. If it's present, it means
# that the variant1 patch is also present (both were merged at the same time)
if strings "$vmlinux" | grep -qw noibrs && strings "$vmlinux" | grep -qw noibpb; then
_debug "found redhat/canonical version of the variant2 patch (implies variant1)"
redhat_canonical_spectre=1
else
redhat_canonical_spectre=0
fi
fi
}
################### ###################
# SPECTRE VARIANT 1 # SPECTRE VARIANT 1
check_variant1() check_variant1()
@ -1384,7 +1426,19 @@ check_variant1()
fi fi
fi fi
if [ "$opt_verbose" -ge 2 ] || [ "$v1_mask_nospec" != 1 ]; then _info_nol "* Kernel has the Red Hat/Ubuntu patch: "
check_redhat_canonical_spectre
if [ "$redhat_canonical_spectre" = -1 ]; then
pstatus yellow UNKNOWN "missing 'strings' tool, please install it, usually it's in the binutils package"
elif [ "$redhat_canonical_spectre" = -2 ]; then
pstatus yellow UNKNOWN "couldn't check ($vmlinux_err)"
elif [ "$redhat_canonical_spectre" = 1 ]; then
pstatus green YES
else
pstatus red NO
fi
if [ "$opt_verbose" -ge 2 ] || ( [ "$v1_mask_nospec" != 1 ] && [ "$redhat_canonical_spectre" != 1 ] ); then
# this is a slow heuristic and we don't need it if we already know the kernel is patched # this is a slow heuristic and we don't need it if we already know the kernel is patched
# but still show it in verbose mode # but still show it in verbose mode
_info_nol "* Checking count of LFENCE instructions following a jump in kernel... " _info_nol "* Checking count of LFENCE instructions following a jump in kernel... "
@ -1427,6 +1481,8 @@ check_variant1()
# if msg is empty, sysfs check didn't fill it, rely on our own test # if msg is empty, sysfs check didn't fill it, rely on our own test
if [ "$v1_mask_nospec" = 1 ]; then if [ "$v1_mask_nospec" = 1 ]; then
pvulnstatus $cve OK "Kernel source has been patched to mitigate the vulnerability (array_index_mask_nospec)" pvulnstatus $cve OK "Kernel source has been patched to mitigate the vulnerability (array_index_mask_nospec)"
elif [ "$redhat_canonical_spectre" = 1 ]; then
pvulnstatus $cve OK "Kernel source has been patched to mitigate the vulnerability (Red Hat/Ubuntu patch)"
elif [ "$v1_lfence" = 1 ]; then elif [ "$v1_lfence" = 1 ]; then
pvulnstatus $cve OK "Kernel source has PROBABLY been patched to mitigate the vulnerability (jump-then-lfence instructions heuristic)" pvulnstatus $cve OK "Kernel source has PROBABLY been patched to mitigate the vulnerability (jump-then-lfence instructions heuristic)"
elif [ "$vmlinux_err" ]; then elif [ "$vmlinux_err" ]; then
@ -1472,7 +1528,7 @@ check_variant2()
if [ -e "$dir/ibrs_enabled" ]; then if [ -e "$dir/ibrs_enabled" ]; then
# if the file is there, we have IBRS compiled-in # if the file is there, we have IBRS compiled-in
# /sys/kernel/debug/ibrs_enabled: vanilla # /sys/kernel/debug/ibrs_enabled: vanilla
# /sys/kernel/debug/x86/ibrs_enabled: RedHat (see https://access.redhat.com/articles/3311301) # /sys/kernel/debug/x86/ibrs_enabled: Red Hat (see https://access.redhat.com/articles/3311301)
# /proc/sys/kernel/ibrs_enabled: OpenSUSE tumbleweed # /proc/sys/kernel/ibrs_enabled: OpenSUSE tumbleweed
pstatus green YES pstatus green YES
ibrs_knob_dir=$dir ibrs_knob_dir=$dir
@ -1513,6 +1569,13 @@ check_variant2()
_debug "ibrs: found '*spec_ctrl*' symbol in $opt_map" _debug "ibrs: found '*spec_ctrl*' symbol in $opt_map"
fi fi
fi fi
if [ "$ibrs_supported" != 1 ]; then
check_redhat_canonical_spectre
if [ "$redhat_canonical_spectre" = 1 ]; then
pstatus green YES "Red Hat/Ubuntu patch"
ibrs_supported=1
fi
fi
if [ "$ibrs_supported" != 1 ]; then if [ "$ibrs_supported" != 1 ]; then
if [ "$ibrs_can_tell" = 1 ]; then if [ "$ibrs_can_tell" = 1 ]; then
pstatus red NO pstatus red NO
@ -1680,19 +1743,6 @@ check_variant2()
pstatus red NO pstatus red NO
fi fi
fi fi
_info_nol " * Retpoline enabled: "
if [ "$opt_live" = 1 ]; then
# kernel adds this flag when retpoline is supported and enabled,
# regardless of the fact that it's minimal / full and generic / amd
if grep -qw retpoline /proc/cpuinfo; then
pstatus green YES
else
pstatus red NO
fi
else
pstatus blue N/A "can't check this in offline mode"
fi
elif [ "$sys_interface_available" = 0 ]; then elif [ "$sys_interface_available" = 0 ]; then
# we have no sysfs but were asked to use it only! # we have no sysfs but were asked to use it only!
msg="/sys vulnerability interface use forced, but it's not available!" msg="/sys vulnerability interface use forced, but it's not available!"
@ -1715,6 +1765,8 @@ check_variant2()
pvulnstatus $cve OK "IBRS is mitigating the vulnerability" pvulnstatus $cve OK "IBRS is mitigating the vulnerability"
elif [ "$ibpb_enabled" = 2 ]; then elif [ "$ibpb_enabled" = 2 ]; then
pvulnstatus $cve OK "Full IBPB is mitigating the vulnerability" pvulnstatus $cve OK "Full IBPB is mitigating the vulnerability"
elif [ "$ibrs_supported" = 1 ] && [ "$cpuid_spec_ctrl" != 1 ]; then
pvulnstatus $cve VULN "Your kernel is compiled with IBRS but your CPU microcode is lacking support to successfully mitigate the vulnerability"
else else
pvulnstatus $cve VULN "IBRS hardware + kernel support OR kernel with retpoline are needed to mitigate the vulnerability" pvulnstatus $cve VULN "IBRS hardware + kernel support OR kernel with retpoline are needed to mitigate the vulnerability"
fi fi
@ -1804,7 +1856,7 @@ check_variant3()
_debug "kpti_enabled: found 'kaiser' flag in /proc/cpuinfo" _debug "kpti_enabled: found 'kaiser' flag in /proc/cpuinfo"
kpti_enabled=1 kpti_enabled=1
elif [ -e /sys/kernel/debug/x86/pti_enabled ]; then elif [ -e /sys/kernel/debug/x86/pti_enabled ]; then
# RedHat Backport creates a dedicated file, see https://access.redhat.com/articles/3311301 # Red Hat Backport creates a dedicated file, see https://access.redhat.com/articles/3311301
kpti_enabled=$(cat /sys/kernel/debug/x86/pti_enabled 2>/dev/null) kpti_enabled=$(cat /sys/kernel/debug/x86/pti_enabled 2>/dev/null)
_debug "kpti_enabled: file /sys/kernel/debug/x86/pti_enabled exists and says: $kpti_enabled" _debug "kpti_enabled: file /sys/kernel/debug/x86/pti_enabled exists and says: $kpti_enabled"
fi fi
@ -1899,7 +1951,7 @@ check_variant3()
elif [ "$xen_pv_domo" = 1 ]; then elif [ "$xen_pv_domo" = 1 ]; then
pvulnstatus $cve OK "Xen Dom0s are safe and do not require PTI" pvulnstatus $cve OK "Xen Dom0s are safe and do not require PTI"
elif [ "$xen_pv_domu" = 1 ]; then elif [ "$xen_pv_domu" = 1 ]; then
pvulnstatus $cve VULN "Xen PV DomUs are vulnerable and need to be run in HVM, PVHVM or PVH mode" pvulnstatus $cve VULN "Xen PV DomUs are vulnerable and need to be run in HVM, PVHVM, PVH mode, or the Xen hypervisor must have the Xen's own PTI patch"
else else
pvulnstatus $cve VULN "PTI is needed to mitigate the vulnerability" pvulnstatus $cve VULN "PTI is needed to mitigate the vulnerability"
fi fi
@ -1964,6 +2016,12 @@ if [ "$opt_batch" = 1 ] && [ "$opt_batch_format" = "json" ]; then
_echo 0 "${json_output%?}]" _echo 0 "${json_output%?}]"
fi fi
if [ "$opt_batch" = 1 ] && [ "$opt_batch_format" = "prometheus" ]; then
echo "# TYPE specex_vuln_status untyped"
echo "# HELP specex_vuln_status Exposure of system to speculative execution vulnerabilities"
echo "$prometheus_output"
fi
# exit with the proper exit code # exit with the proper exit code
[ "$global_critical" = 1 ] && exit 2 # critical [ "$global_critical" = 1 ] && exit 2 # critical
[ "$global_unknown" = 1 ] && exit 3 # unknown [ "$global_unknown" = 1 ] && exit 3 # unknown