14 Commits
v0.41 ... v0.42

Author SHA1 Message Date
fcc4ff4de2 update MCEdb from v110 to v111, bump to v0.42 2019-05-24 22:49:45 +02:00
0bd38ddda0 enh: -v -v now implies --dump-mock-data 2019-05-24 11:36:39 +02:00
e83dc818cd feat(mds): implement FreeBSD mitigation detection 2019-05-24 11:17:04 +02:00
d69ea67101 feat(mock): add --dump-mock-data 2019-05-24 10:49:40 +02:00
dfe0d10f2a fix(mds): remove useless display of MD_CLEAR info in non-hw section 2019-05-24 10:20:48 +02:00
58a5acfdbb fix(bsd): read_msr returned data in an incorrect format 2019-05-24 09:33:56 +02:00
ccb4dbef7c enh(mock): avoid reading the sysfs interface outside sys_interface_check() for higher mocking coverage 2019-05-24 09:28:18 +02:00
afbb26277f feat(mock): add mocking functionality to help reproducing issues under specific CPUs 2019-05-24 09:28:18 +02:00
77b34d48c6 fix(mds): check MDS_NO bit in is_cpu_mds_free() 2019-05-24 09:28:18 +02:00
497efe6a82 fix(l1tf): RDCL_NO bit didn't take precedence for vulnerability check on some Intel CPUs 2019-05-24 09:28:18 +02:00
62b46df4e7 fix(l1tf): remove libvirtd from hypervisor detection (#278) 2019-05-18 14:22:42 +02:00
7d1f269bed fix(mds): AMD confirms they're not vulnerable 2019-05-16 11:31:28 +02:00
4f9ca803c8 Fix help text (#285)
* fix --help message

Commit 7b72c20f89 added help text for the
--cve switch, and the "can be specified multiple times" note got
associated with the --cve switch instead of staying with the --variant
switch.  Restore the line to belong to the --variant switch help
message.

* Add new variants to error message

Commit 8e870db4f5 added new variants but
did not add them to the error message that listed the allowable
variants.  Add them now.
2019-05-15 19:34:51 +02:00
5788cec18b fix(mds): ARM and CAVIUM are not thought to be vulnerable 2019-05-15 10:56:49 +02:00
2 changed files with 357 additions and 109 deletions

View File

@ -143,7 +143,7 @@ docker run --rm --privileged -v /boot:/boot:ro -v /dev/cpu:/dev/cpu:ro -v /lib/m
- Note: These 4 CVEs are similar and collectively named "MDS" vulnerabilities, the mitigation is identical for all
- Impact: Kernel
- Mitigation: microcode update + kernel update making possible to protect various CPU internal buffers from unprivileged speculative access to data
- Performance impact of the mitigation: TBC
- Performance impact of the mitigation: low to significant
## Understanding what this script does and doesn't

View File

@ -11,7 +11,7 @@
#
# Stephane Lesimple
#
VERSION='0.41'
VERSION='0.42'
trap 'exit_cleanup' EXIT
trap '_warn "interrupted, cleaning up..."; exit_cleanup; exit 1' INT
@ -79,13 +79,14 @@ show_usage()
--variant VARIANT specify which variant you'd like to check, by default all variants are checked
VARIANT can be one of 1, 2, 3, 3a, 4, l1tf, msbds, mfbds, mlpds, mdsum
--cve [cve1,cve2,...] specify which CVE you'd like to check, by default all supported CVEs are checked
can be specified multiple times (e.g. --variant 2 --variant 3)
--cve [cve1,cve2,...] specify which CVE you'd like to check, by default all supported CVEs are checked
--hw-only only check for CPU information, don't check for any variant
--no-hw skip CPU information and checks, if you're inspecting a kernel not to be run on this host
--vmm [auto,yes,no] override the detection of the presence of a hypervisor (for CVE-2018-3646), default: auto
--update-mcedb update our local copy of the CPU microcodes versions database (from the awesome MCExtractor project)
--update-builtin-mcedb same as --update-mcedb but update builtin DB inside the script itself
--dump-mock-data used to mimick a CPU on an other system, mainly used to help debugging this script
Return codes:
0 (not vulnerable), 2 (vulnerable), 3 (unknown), 255 (error)
@ -150,6 +151,7 @@ opt_no_hw=0
opt_vmm=-1
opt_explain=0
opt_paranoid=0
opt_mock=0
global_critical=0
global_unknown=0
@ -325,6 +327,14 @@ is_cpu_vulnerable()
variant_mlpds=''
variant_mdsum=''
if is_cpu_mds_free; then
[ -z "$variant_msbds" ] && variant_msbds=immune
[ -z "$variant_mfbds" ] && variant_mfbds=immune
[ -z "$variant_mlpds" ] && variant_mlpds=immune
[ -z "$variant_mdsum" ] && variant_mdsum=immune
_debug "is_cpu_vulnerable: cpu not affected by Microarchitectural Data Sampling"
fi
if is_cpu_specex_free; then
variant1=immune
variant2=immune
@ -365,13 +375,6 @@ is_cpu_vulnerable()
[ -z "$variant4" ] && variant4=immune
_debug "is_cpu_vulnerable: cpu not affected by speculative store bypass so not vuln to variant4"
fi
if is_cpu_mds_free; then
[ -z "$variant_msbds" ] && variant_msbds=immune
[ -z "$variant_mfbds" ] && variant_mfbds=immune
[ -z "$variant_mlpds" ] && variant_mlpds=immune
[ -z "$variant_mdsum" ] && variant_mdsum=immune
_debug "is_cpu_vulnerable: cpu not affected by Microarchitectural Data Sampling"
fi
# variant 4a for xeon phi
if [ "$cpu_family" = 6 ]; then
if [ "$cpu_model" = "$INTEL_FAM6_XEON_PHI_KNL" ] || [ "$cpu_model" = "$INTEL_FAM6_XEON_PHI_KNM" ]; then
@ -401,7 +404,7 @@ is_cpu_vulnerable()
[ -z "$variantl1tf" ] && variantl1tf=immune
else
_debug "is_cpu_vulnerable: intel family 6 is vuln"
variantl1tf=vuln
[ -z "$variantl1tf" ] && variantl1tf=vuln
fi
elif [ "$cpu_family" -lt 6 ]; then
_debug "is_cpu_vulnerable: intel family < 6 is immune"
@ -420,13 +423,6 @@ is_cpu_vulnerable()
[ -z "$variant4" ] && variant4=immune
_debug "is_cpu_vulnerable: cpu not affected by speculative store bypass so not vuln to variant4"
fi
if is_cpu_mds_free; then
[ -z "$variant_msbds" ] && variant_msbds=immune
[ -z "$variant_mfbds" ] && variant_mfbds=immune
[ -z "$variant_mlpds" ] && variant_mlpds=immune
[ -z "$variant_mdsum" ] && variant_mdsum=immune
_debug "is_cpu_vulnerable: cpu not affected by Microarchitectural Data Sampling"
fi
variantl1tf=immune
elif [ "$cpu_vendor" = CAVIUM ]; then
variant3=immune
@ -593,30 +589,31 @@ is_cpu_mds_free()
#VULNWL_AMD(X86_FAMILY_ANY, NO_MELTDOWN | NO_L1TF | NO_MDS),
#VULNWL_HYGON(X86_FAMILY_ANY, NO_MELTDOWN | NO_L1TF | NO_MDS),
parse_cpu_details
if is_intel; then
if is_intel; then
if [ "$cpu_family" = 6 ]; then
if [ "$cpu_model" = "$INTEL_FAM6_ATOM_GOLDMONT" ] || \
if [ "$cpu_model" = "$INTEL_FAM6_ATOM_GOLDMONT" ] || \
[ "$cpu_model" = "$INTEL_FAM6_ATOM_GOLDMONT_X" ] || \
[ "$cpu_model" = "$INTEL_FAM6_ATOM_GOLDMONT_PLUS" ]; then
return 0
fi
fi
return 0
fi
fi
[ "$capabilities_mds_no" = 1 ] && return 0
fi
# official statement from AMD says none of their CPUs are vulnerable
# https://www.amd.com/en/corporate/product-security
# https://www.amd.com/system/files/documents/security-whitepaper.pdf
if is_amd; then
if [ "$cpu_family" = "18" ] || \
[ "$cpu_family" = "17" ] || \
[ "$cpu_family" = "16" ] || \
[ "$cpu_family" = "15" ]; then
return 0
fi
fi
if is_hygon; then
return 0
elif is_hygon; then
return 0
elif [ "$cpu_vendor" = CAVIUM ]; then
return 0
elif [ "$cpu_vendor" = ARM ]; then
return 0
fi
return 1
return 1
}
is_cpu_ssb_free()
@ -821,6 +818,9 @@ while [ -n "$1" ]; do
elif [ "$1" = "--update-builtin-mcedb" ]; then
update_mcedb builtin
exit $?
elif [ "$1" = "--dump-mock-data" ]; then
opt_mock=1
shift
elif [ "$1" = "--explain" ]; then
opt_explain=1
shift
@ -841,6 +841,7 @@ while [ -n "$1" ]; do
esac
elif [ "$1" = "-v" ] || [ "$1" = "--verbose" ]; then
opt_verbose=$(( opt_verbose + 1 ))
[ "$opt_verbose" -ge 2 ] && opt_mock=1
shift
elif [ "$1" = "--cve" ]; then
if [ -z "$2" ]; then
@ -885,7 +886,7 @@ while [ -n "$1" ]; do
mdsum) opt_cve_list="$opt_cve_list CVE-2019-11091"; opt_cve_all=0;;
l1tf) opt_cve_list="$opt_cve_list CVE-2018-3615 CVE-2018-3620 CVE-2018-3646"; opt_cve_all=0;;
*)
echo "$0: error: invalid parameter '$2' for --variant, expected either 1, 2, 3, 3a, 4 or l1tf" >&2;
echo "$0: error: invalid parameter '$2' for --variant, expected either 1, 2, 3, 3a, 4, msbds, mfbds, mlpds, mdsum, or l1tf" >&2;
exit 255
;;
esac
@ -1216,6 +1217,14 @@ read_cpuid()
fi
_debug "cpuid: leaf$_leaf on cpu0, eax-ebx-ecx-edx: $_cpuid"
_mockvarname="SMC_MOCK_CPUID_${_leaf}"
if [ -n "$(eval echo \$$_mockvarname)" ]; then
_cpuid="$(eval echo \$$_mockvarname)"
_debug "read_cpuid: MOCKING enabled for leaf $_leaf, will return $_cpuid"
mocked=1
else
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_CPUID_${_leaf}='$_cpuid'")
fi
[ -z "$_cpuid" ] && return 2
# get the value of the register we want
_reg=$(echo "$_cpuid" | awk '{print $'"$_register"'}')
@ -1299,6 +1308,42 @@ parse_cpu_details()
cpu_friendly_name=$(sysctl -n hw.model)
fi
if [ -n "$SMC_MOCK_CPU_FRIENDLY_NAME" ]; then
cpu_friendly_name="$SMC_MOCK_CPU_FRIENDLY_NAME"
_debug "parse_cpu_details: MOCKING cpu friendly name to $cpu_friendly_name"
mocked=1
else
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_CPU_FRIENDLY_NAME='$cpu_friendly_name'")
fi
if [ -n "$SMC_MOCK_CPU_VENDOR" ]; then
cpu_vendor="$SMC_MOCK_CPU_VENDOR"
_debug "parse_cpu_details: MOCKING cpu vendor to $cpu_vendor"
mocked=1
else
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_CPU_VENDOR='$cpu_vendor'")
fi
if [ -n "$SMC_MOCK_CPU_FAMILY" ]; then
cpu_family="$SMC_MOCK_CPU_FAMILY"
_debug "parse_cpu_details: MOCKING cpu family to $cpu_family"
mocked=1
else
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_CPU_FAMILY='$cpu_family'")
fi
if [ -n "$SMC_MOCK_CPU_MODEL" ]; then
cpu_model="$SMC_MOCK_CPU_MODEL"
_debug "parse_cpu_details: MOCKING cpu model to $cpu_model"
mocked=1
else
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_CPU_MODEL='$cpu_model'")
fi
if [ -n "$SMC_MOCK_CPU_STEPPING" ]; then
cpu_stepping="$SMC_MOCK_CPU_STEPPING"
_debug "parse_cpu_details: MOCKING cpu stepping to $cpu_stepping"
mocked=1
else
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_CPU_STEPPING='$cpu_stepping'")
fi
# get raw cpuid, it's always useful (referenced in the Intel doc for firmware updates for example)
if read_cpuid 0x1 $EAX 0 0xFFFFFFFF; then
cpu_cpuid="$read_cpuid_value"
@ -1326,6 +1371,14 @@ parse_cpu_details()
# if we got no cpu_ucode (e.g. we're in a vm), fall back to 0x0
[ -z "$cpu_ucode" ] && cpu_ucode=0x0
if [ -n "$SMC_MOCK_CPU_UCODE" ]; then
cpu_ucode="$SMC_MOCK_CPU_UCODE"
_debug "parse_cpu_details: MOCKING cpu ucode to $cpu_ucode"
mocked=1
else
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_CPU_UCODE='$cpu_ucode'")
fi
echo "$cpu_ucode" | grep -q ^0x && cpu_ucode=$(( cpu_ucode ))
ucode_found=$(printf "model 0x%x family 0x%x stepping 0x%x ucode 0x%x cpuid 0x%x" "$cpu_model" "$cpu_family" "$cpu_stepping" "$cpu_ucode" "$cpu_cpuid")
@ -1877,12 +1930,41 @@ sys_interface_check()
file="$1"
regex="$2"
mode="$3"
[ "$opt_live" = 1 ] && [ "$opt_no_sysfs" = 0 ] && [ -r "$file" ] || return 1
msg=''
fullmsg=''
if [ "$opt_live" = 1 ] && [ "$opt_no_sysfs" = 0 ] && [ -r "$file" ]; then
:
else
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_SYSFS_$(basename "$file")_RET=1")
return 1
fi
_mockvarname="SMC_MOCK_SYSFS_$(basename "$file")_RET"
# shellcheck disable=SC2086
if [ -n "$(eval echo \$$_mockvarname)" ]; then
_debug "sysfs: MOCKING enabled for $file func returns $(eval echo \$$_mockvarname)"
mocked=1
return "$(eval echo \$$_mockvarname)"
fi
[ -n "$regex" ] || regex='.*'
fullmsg=$(cat "$file")
msg=$(grep -Eo "$regex" "$file")
_mockvarname="SMC_MOCK_SYSFS_$(basename "$file")"
# shellcheck disable=SC2086
if [ -n "$(eval echo \$$_mockvarname)" ]; then
fullmsg="$(eval echo \$$_mockvarname)"
msg=$(echo "$fullmsg" | grep -Eo "$regex")
_debug "sysfs: MOCKING enabled for $file, will return $fullmsg"
mocked=1
else
fullmsg=$(cat "$file")
msg=$(grep -Eo "$regex" "$file")
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_SYSFS_$(basename "$file")='$fullmsg'")
fi
if [ "$mode" = silent ]; then
_info "* Information from the /sys interface: $msg"
return 0
elif [ "$mode" = quiet ]; then
_info "* Information from the /sys interface: $fullmsg"
return 0
fi
_info_nol "* Mitigated according to the /sys interface: "
@ -1919,90 +2001,128 @@ number_of_cpus()
return "$n"
}
# $1 - msr number
# $2 - cpu index
# write_msr
# param1 (mandatory): MSR, can be in hex or decimal.
# param2 (optional): CPU index, starting from 0. Default 0.
write_msr()
{
# _msr must be in hex, in the form 0x1234:
_msr="$1"
# cpu index, starting from 0:
_msr_dec=$(( $1 ))
_msr=$(printf "0x%x" "$_msr_dec")
_cpu="$2"
[ -z "$_cpu" ] && _cpu=0
_mockvarname="SMC_MOCK_WRMSR_${_msr}_RET"
# shellcheck disable=SC2086
if [ -n "$(eval echo \$$_mockvarname)" ]; then
_debug "write_msr: MOCKING enabled for msr $_msr func returns $(eval echo \$$_mockvarname)"
mocked=1
return "$(eval echo \$$_mockvarname)"
fi
if [ "$os" != Linux ]; then
cpucontrol -m "$_msr=0" "/dev/cpuctl$_cpu" >/dev/null 2>&1; ret=$?
else
# for Linux
# convert to decimal
_msr=$(( _msr ))
if [ ! -w /dev/cpu/"$_cpu"/msr ]; then
ret=200 # permission error
# if wrmsr is available, use it
elif command -v wrmsr >/dev/null 2>&1 && [ "$SMC_NO_WRMSR" != 1 ]; then
_debug "write_msr: using wrmsr"
wrmsr $_msr 0 2>/dev/null; ret=$?
wrmsr $_msr_dec 0 2>/dev/null; ret=$?
# or if we have perl, use it, any 5.x version will work
elif command -v perl >/dev/null 2>&1 && [ "$SMC_NO_PERL" != 1 ]; then
_debug "write_msr: using perl"
ret=1
perl -e "open(M,'>','/dev/cpu/$_cpu/msr') and seek(M,$_msr,0) and exit(syswrite(M,pack('H16',0)))"; [ $? -eq 8 ] && ret=0
perl -e "open(M,'>','/dev/cpu/$_cpu/msr') and seek(M,$_msr_dec,0) and exit(syswrite(M,pack('H16',0)))"; [ $? -eq 8 ] && ret=0
# fallback to dd if it supports seek_bytes
elif dd if=/dev/null of=/dev/null bs=8 count=1 seek="$_msr" oflag=seek_bytes 2>/dev/null; then
elif dd if=/dev/null of=/dev/null bs=8 count=1 seek="$_msr_dec" oflag=seek_bytes 2>/dev/null; then
_debug "write_msr: using dd"
dd if=/dev/zero of=/dev/cpu/"$_cpu"/msr bs=8 count=1 seek="$_msr" oflag=seek_bytes 2>/dev/null; ret=$?
dd if=/dev/zero of=/dev/cpu/"$_cpu"/msr bs=8 count=1 seek="$_msr_dec" oflag=seek_bytes 2>/dev/null; ret=$?
else
_debug "write_msr: got no wrmsr, perl or recent enough dd!"
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_WRMSR_${_msr}_RET=201")
return 201 # missing tool error
fi
fi
# normalize ret
[ "$ret" != 0 ] && ret=1
_debug "write_msr: for cpu $_cpu on msr $_msr, ret=$ret"
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_WRMSR_${_msr}_RET=$ret")
return $ret
}
# read_msr
# param1 (mandatory): MSR, can be in hex or decimal.
# param2 (optional): CPU index, starting from 0. Default 0.
read_msr()
{
# _msr must be in hex, in the form 0x1234:
_msr="$1"
# cpu index, starting from 0:
_msr_dec=$(( $1 ))
_msr=$(printf "0x%x" "$_msr_dec")
_cpu="$2"
[ -z "$_cpu" ] && _cpu=0
read_msr_value=''
_mockvarname="SMC_MOCK_RDMSR_${_msr}"
# shellcheck disable=SC2086
if [ -n "$(eval echo \$$_mockvarname)" ]; then
read_msr_value="$(eval echo \$$_mockvarname)"
_debug "read_msr: MOCKING enabled for msr $_msr, returning $read_msr_value"
mocked=1
return 0
else
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_RDMSR_${_msr}='$read_msr_value'")
fi
_mockvarname="SMC_MOCK_RDMSR_${_msr}_RET"
# shellcheck disable=SC2086
if [ -n "$(eval echo \$$_mockvarname)" ] && [ "$(eval echo \$$_mockvarname)" -ne 0 ]; then
_debug "read_msr: MOCKING enabled for msr $_msr func returns $(eval echo \$$_mockvarname)"
mocked=1
return "$(eval echo \$$_mockvarname)"
fi
if [ "$os" != Linux ]; then
# for BSD
_msr=$(cpucontrol -m "$_msr" "/dev/cpuctl$_cpu" 2>/dev/null); ret=$?
[ $ret -ne 0 ] && return 1
if [ $ret -ne 0 ]; then
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_RDMSR_${_msr}_RET=1")
return 1
fi
# MSR 0x10: 0x000003e1 0xb106dded
_msr_h=$(echo "$_msr" | awk '{print $3}');
_msr_h="$(( _msr_h >> 24 & 0xFF )) $(( _msr_h >> 16 & 0xFF )) $(( _msr_h >> 8 & 0xFF )) $(( _msr_h & 0xFF ))"
_msr_l=$(echo "$_msr" | awk '{print $4}');
_msr_l="$(( _msr_l >> 24 & 0xFF )) $(( _msr_l >> 16 & 0xFF )) $(( _msr_l >> 8 & 0xFF )) $(( _msr_l & 0xFF ))"
read_msr_value="$_msr_h $_msr_l"
read_msr_value=$(( _msr_h << 32 | _msr_l ))
else
# for Linux
# convert to decimal
_msr=$(( _msr ))
if [ ! -r /dev/cpu/"$_cpu"/msr ]; then
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_RDMSR_${_msr}_RET=200")
return 200 # permission error
# if rdmsr is available, use it
elif command -v rdmsr >/dev/null 2>&1 && [ "$SMC_NO_RDMSR" != 1 ]; then
_debug "read_msr: using rdmsr"
read_msr_value=$(rdmsr -r $_msr 2>/dev/null | od -t u8 -A n)
read_msr_value=$(rdmsr -r $_msr_dec 2>/dev/null | od -t u8 -A n)
# or if we have perl, use it, any 5.x version will work
elif command -v perl >/dev/null 2>&1 && [ "$SMC_NO_PERL" != 1 ]; then
_debug "read_msr: using perl"
read_msr_value=$(perl -e "open(M,'<','/dev/cpu/$_cpu/msr') and seek(M,$_msr,0) and read(M,\$_,8) and print" | od -t u8 -A n)
read_msr_value=$(perl -e "open(M,'<','/dev/cpu/$_cpu/msr') and seek(M,$_msr_dec,0) and read(M,\$_,8) and print" | od -t u8 -A n)
# fallback to dd if it supports skip_bytes
elif dd if=/dev/null of=/dev/null bs=8 count=1 skip="$_msr" iflag=skip_bytes 2>/dev/null; then
elif dd if=/dev/null of=/dev/null bs=8 count=1 skip="$_msr_dec" iflag=skip_bytes 2>/dev/null; then
_debug "read_msr: using dd"
read_msr_value=$(dd if=/dev/cpu/"$_cpu"/msr bs=8 count=1 skip="$_msr" iflag=skip_bytes 2>/dev/null | od -t u8 -A n)
read_msr_value=$(dd if=/dev/cpu/"$_cpu"/msr bs=8 count=1 skip="$_msr_dec" iflag=skip_bytes 2>/dev/null | od -t u8 -A n)
else
_debug "read_msr: got no rdmsr, perl or recent enough dd!"
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_RDMSR_${_msr}_RET=201")
return 201 # missing tool error
fi
if [ -z "$read_msr_value" ]; then
# MSR doesn't exist, don't check for $? because some versions of dd still return 0!
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_RDMSR_${_msr}_RET=1")
return 1
fi
fi
_debug "read_msr: MSR=$1 value is $read_msr_value"
_debug "read_msr: MSR=$_msr value is $read_msr_value"
return 0
}
@ -2447,7 +2567,7 @@ check_cpu()
fi
fi
_info_nol " * CPU explicitly indicates not being vulnerable to Meltdown (RDCL_NO): "
_info_nol " * CPU explicitly indicates not being vulnerable to Meltdown/L1TF (RDCL_NO): "
if [ "$capabilities_rdcl_no" = -1 ]; then
pstatus yellow UNKNOWN
elif [ "$capabilities_rdcl_no" = 1 ]; then
@ -2483,14 +2603,14 @@ check_cpu()
pstatus blue NO
fi
_info_nol " * CPU explicitly indicates not being vulnerable to Microarchitectural Data Sampling (MDC_NO): "
_info_nol " * CPU explicitly indicates not being vulnerable to Microarchitectural Data Sampling (MDS_NO): "
if [ "$capabilities_mds_no" = -1 ]; then
pstatus yellow UNKNOWN
elif [ "$capabilities_mds_no" = 1 ]; then
pstatus green YES
else
pstatus yellow NO
fi
fi
fi
_info_nol " * CPU supports Software Guard Extensions (SGX): "
@ -2865,21 +2985,21 @@ check_CVE_2017_5715_linux()
# XXX and what about ibpb ?
fi
fi
if [ -e "/sys/devices/system/cpu/vulnerabilities/spectre_v2" ]; then
if [ -n "$fullmsg" ]; then
# when IBPB is enabled on 4.15+, we can see it in sysfs
if grep -q 'IBPB' "/sys/devices/system/cpu/vulnerabilities/spectre_v2"; then
if echo "$fullmsg" | grep -q 'IBPB'; then
_debug "ibpb: found enabled in sysfs"
[ -z "$ibpb_supported" ] && ibpb_supported='IBPB found enabled in sysfs'
[ -z "$ibpb_enabled" ] && ibpb_enabled=1
fi
# when IBRS_FW is enabled on 4.15+, we can see it in sysfs
if grep -q ', IBRS_FW' "/sys/devices/system/cpu/vulnerabilities/spectre_v2"; then
if echo "$fullmsg" | grep -q ', IBRS_FW'; then
_debug "ibrs: found IBRS_FW in sysfs"
[ -z "$ibrs_supported" ] && ibrs_supported='found IBRS_FW in sysfs'
ibrs_fw_enabled=1
fi
# when IBRS is enabled on 4.15+, we can see it in sysfs
if grep -q -e '\<IBRS\>' -e 'Indirect Branch Restricted Speculation' "/sys/devices/system/cpu/vulnerabilities/spectre_v2"; then
if echo "$fullmsg" | grep -q -e '\<IBRS\>' -e 'Indirect Branch Restricted Speculation'; then
_debug "ibrs: found IBRS in sysfs"
[ -z "$ibrs_supported" ] && ibrs_supported='found IBRS in sysfs'
[ -z "$ibrs_enabled" ] && ibrs_enabled=3
@ -3064,9 +3184,9 @@ check_CVE_2017_5715_linux()
#
# if there is "retpoline" in the file and NOT "minimal", then it's full retpoline
# (works for vanilla and Red Hat variants)
if [ "$opt_live" = 1 ] && [ -e "/sys/devices/system/cpu/vulnerabilities/spectre_v2" ]; then
if grep -qwi retpoline /sys/devices/system/cpu/vulnerabilities/spectre_v2; then
if grep -qwi minimal /sys/devices/system/cpu/vulnerabilities/spectre_v2; then
if [ "$opt_live" = 1 ] && [ -n "$fullmsg" ]; then
if echo "$fullmsg" | grep -qwi retpoline; then
if echo "$fullmsg" | grep -qwi minimal; then
retpoline_compiler=0
retpoline_compiler_reason="kernel reports minimal retpoline compilation"
else
@ -3887,7 +4007,7 @@ check_CVE_2018_3620_linux()
status=UNK
sys_interface_available=0
msg=''
if sys_interface_check "/sys/devices/system/cpu/vulnerabilities/l1tf" '^[^;]+'; then
if sys_interface_check "/sys/devices/system/cpu/vulnerabilities/l1tf"; then
# this kernel has the /sys interface, trust it over everything
sys_interface_available=1
fi
@ -3908,8 +4028,8 @@ check_CVE_2018_3620_linux()
_info_nol "* PTE inversion enabled and active: "
if [ "$opt_live" = 1 ]; then
if [ "$sys_interface_available" = 1 ]; then
if grep -q 'Mitigation: PTE Inversion' /sys/devices/system/cpu/vulnerabilities/l1tf; then
if [ -n "$fullmsg" ]; then
if echo "$fullmsg" | grep -q 'Mitigation: PTE Inversion'; then
pstatus green YES
pteinv_active=1
else
@ -3999,7 +4119,7 @@ check_CVE_2018_3646_linux()
status=UNK
sys_interface_available=0
msg=''
if sys_interface_check "/sys/devices/system/cpu/vulnerabilities/l1tf" 'VMX:.*' silent; then
if sys_interface_check "/sys/devices/system/cpu/vulnerabilities/l1tf" '.*' quiet; then
# this kernel has the /sys interface, trust it over everything
sys_interface_available=1
fi
@ -4019,14 +4139,15 @@ check_CVE_2018_3646_linux()
has_vmm=0
if command -v pgrep >/dev/null 2>&1; then
# remove xenbus and xenwatch, also present inside domU
if pgrep qemu >/dev/null || pgrep kvm >/dev/null || pgrep libvirtd >/dev/null || \
# remove libvirtd as it can also be used to manage containers and not VMs
if pgrep qemu >/dev/null || pgrep kvm >/dev/null || \
pgrep xenstored >/dev/null || pgrep xenconsoled >/dev/null; then
has_vmm=1
fi
else
# ignore SC2009 as `ps ax` is actually used as a fallback if `pgrep` isn't installed
# shellcheck disable=SC2009
if ps ax | grep -vw grep | grep -q -e '\<qemu' -e '/qemu' -e '<\kvm' -e '/kvm' -e '/libvirtd' -e '/xenstored' -e '/xenconsoled'; then
if ps ax | grep -vw grep | grep -q -e '\<qemu' -e '/qemu' -e '<\kvm' -e '/kvm' -e '/xenstored' -e '/xenconsoled'; then
has_vmm=1
fi
fi
@ -4087,18 +4208,19 @@ check_CVE_2018_3646_linux()
_info_nol " * L1D flush enabled: "
if [ "$opt_live" = 1 ]; then
if [ -r "/sys/devices/system/cpu/vulnerabilities/l1tf" ]; then
if [ -n "$fullmsg" ]; then
# vanilla: VMX: $l1dstatus, SMT $smtstatus
# Red Hat: VMX: SMT $smtstatus, L1D $l1dstatus
# $l1dstatus is one of (auto|vulnerable|conditional cache flushes|cache flushes|EPT disabled|flush not necessary)
# $smtstatus is one of (vulnerable|disabled)
if grep -Eq '(VMX:|L1D) (EPT disabled|vulnerable|flush not necessary)' "/sys/devices/system/cpu/vulnerabilities/l1tf"; then
# can also just be "Not affected"
if echo "$fullmsg" | grep -Eq -e 'Not affected' -e '(VMX:|L1D) (EPT disabled|vulnerable|flush not necessary)'; then
l1d_mode=0
pstatus yellow NO
elif grep -Eq '(VMX:|L1D) conditional cache flushes' "/sys/devices/system/cpu/vulnerabilities/l1tf"; then
elif echo "$fullmsg" | grep -Eq '(VMX:|L1D) conditional cache flushes'; then
l1d_mode=1
pstatus green YES "conditional flushes"
elif grep -Eq '(VMX:|L1D) cache flushes' "/sys/devices/system/cpu/vulnerabilities/l1tf"; then
elif echo "$fullmsg" | grep -Eq '(VMX:|L1D) cache flushes'; then
l1d_mode=2
pstatus green YES "unconditional flushes"
else
@ -4165,6 +4287,9 @@ check_CVE_2018_3646_linux()
if ! is_cpu_vulnerable "$cve"; then
# override status & msg in case CPU is not vulnerable after all
pvulnstatus $cve OK "your CPU vendor reported your CPU model as not vulnerable"
elif [ "$fullmsg" = "Not affected" ]; then
# just in case a very recent kernel knows better than we do
pvulnstatus $cve OK "your kernel reported your CPU model as not vulnerable"
elif [ "$has_vmm" = 0 ]; then
pvulnstatus $cve OK "this system is not running a hypervisor"
else
@ -4280,7 +4405,114 @@ check_mds()
{
cve=$1
_info "\033[1;34m$cve aka '$(cve2name "$cve")'\033[0m"
if [ "$os" = Linux ]; then
check_mds_linux "$cve"
elif echo "$os" | grep -q BSD; then
check_mds_bsd "$cve"
else
_warn "Unsupported OS ($os)"
fi
}
check_mds_bsd()
{
_info_nol "* Kernel supports using MD_CLEAR mitigation: "
if [ "$opt_live" = 1 ]; then
if sysctl hw.mds_disable >/dev/null 2>&1; then
pstatus green YES
kernel_md_clear=1
else
pstatus yellow NO
kernel_md_clear=0
fi
else
if command -v "strings" >/dev/null 2>&1; then
if strings /boot/kernel/kernel | grep -Fq hw.mds_disable; then
pstatus green YES
kernel_md_clear=1
else
kernel_md_clear=0
pstatus yellow NO
fi
else
pstatus yellow UNKNOWN
fi
fi
_info_nol "* CPU Hyper-Threading (SMT) is disabled: "
if sysctl machdep.hyperthreading_allowed >/dev/null 2>&1; then
kernel_smt_allowed=$(sysctl -n machdep.hyperthreading_allowed 2>/dev/null)
if [ "$kernel_smt_allowed" = 1 ]; then
pstatus yellow NO
else
pstatus green YES
fi
else
pstatus yellow UNKNOWN "sysctl machdep.hyperthreading_allowed doesn't exist"
fi
_info_nol "* Kernel mitigation is enabled: "
if [ "$kernel_md_clear" = 1 ]; then
kernel_mds_enabled=$(sysctl -n hw.mds_disable 2>/dev/null)
else
kernel_mds_enabled=0
fi
case "$kernel_mds_enabled" in
0) pstatus yellow NO;;
1) pstatus green YES "with microcode support";;
2) pstatus green YES "software-only support (SLOW)";;
3) pstatus green YES;;
*) pstatus yellow UNKNOWN "unknown value $kernel_mds_enabled"
esac
_info_nol "* Kernel mitigation is active: "
if [ "$kernel_md_clear" = 1 ]; then
kernel_mds_state=$(sysctl -n hw.mds_disable_state 2>/dev/null)
else
kernel_mds_state=inactive
fi
# https://github.com/freebsd/freebsd/blob/master/sys/x86/x86/cpu_machdep.c#L953
case "$kernel_mds_state" in
inactive) pstatus yellow NO;;
VERW) pstatus green YES "with microcode support";;
software*) pstatus green YES "software-only support (SLOW)";;
*) pstatus yellow UNKNOWN
esac
if ! is_cpu_vulnerable "$cve"; then
pvulnstatus "$cve" OK "your CPU vendor reported your CPU model as not vulnerable"
else
if [ "$cpuid_md_clear" = 1 ]; then
if [ "$kernel_md_clear" = 1 ]; then
if [ "$opt_live" = 1 ]; then
# mitigation must also be enabled
if [ "$kernel_mds_enabled" -ge 1 ]; then
if [ "$opt_paranoid" != 1 ] || [ "$kernel_smt_allowed" = 0 ]; then
pvulnstatus "$cve" OK "Your microcode and kernel are both up to date for this mitigation, and mitigation is enabled"
else
pvulnstatus "$cve" VULN "Your microcode and kernel are both up to date for this mitigation, but your must disable SMT (Hyper-Threading) for a complete mitigation"
fi
else
pvulnstatus "$cve" VULN "Your microcode and kernel are both up to date for this mitigation, but the mitigation is not active"
fi
else
pvulnstatus "$cve" OK "Your microcode and kernel are both up to date for this mitigation"
fi
else
pvulnstatus "$cve" VULN "Your microcode supports mitigation, but your kernel doesn't, upgrade it to mitigate the vulnerability"
fi
else
if [ "$kernel_md_clear" = 1 ]; then
pvulnstatus "$cve" VULN "Your kernel supports mitigation, but your CPU microcode also needs to be updated to mitigate the vulnerability"
else
pvulnstatus "$cve" VULN "Neither your kernel or your microcode support mitigation, upgrade both to mitigate the vulnerability"
fi
fi
fi
}
check_mds_linux()
{
status=UNK
sys_interface_available=0
msg=''
@ -4289,15 +4521,6 @@ check_mds()
sys_interface_available=1
fi
if [ "$opt_sysfs_only" != 1 ]; then
_info_nol "* CPU supports the MD_CLEAR functionality: "
if [ "$cpuid_md_clear" = 1 ]; then
pstatus green YES
elif [ "$cpuid_md_clear" = 0 ]; then
pstatus yellow NO
else
pstatus yellow UNKNOWN "is cpuid module loaded?"
fi
_info_nol "* Kernel supports using MD_CLEAR mitigation: "
kernel_md_clear=''
kernel_md_clear_can_tell=1
@ -4326,7 +4549,7 @@ check_mds()
if [ "$opt_live" = 1 ] && [ "$sys_interface_available" = 1 ]; then
_info_nol "* Kernel mitigation is enabled and active: "
if grep -qi ^mitigation /sys/devices/system/cpu/vulnerabilities/mds; then
if echo "$fullmsg" | grep -qi ^mitigation; then
mds_mitigated=1
pstatus green YES
else
@ -4334,7 +4557,7 @@ check_mds()
pstatus yellow NO
fi
_info_nol "* SMT is either mitigated or disabled: "
if grep -Eq 'SMT (disabled|mitigated)' /sys/devices/system/cpu/vulnerabilities/mds; then
if echo "$fullmsg" | grep -Eq 'SMT (disabled|mitigated)'; then
mds_smt_mitigated=1
pstatus green YES
else
@ -4382,7 +4605,7 @@ check_mds()
else
if [ "$opt_paranoid" = 1 ]; then
# in paranoid mode, we don't only need microcode + kernel update, we also want SMT mitigation
if grep -qF -e 'SMT mitigated' -e 'SMT disabled' /sys/devices/system/cpu/vulnerabilities/mds; then
if echo "$fullmsg" | grep -qF -e 'SMT mitigated' -e 'SMT disabled'; then
pvulnstatus "$cve" OK "$fullmsg"
else
pvulnstatus "$cve" VULN "Your kernel and microcode partially mitigate the vulnerability, but you must disable SMT (Hyper-Threading) for a complete mitigation"
@ -4417,15 +4640,40 @@ if [ "$bad_accuracy" = 1 ]; then
_warn "We're missing some kernel info (see -v), accuracy might be reduced"
fi
_vars=$(set | grep -Ev '^[A-Z_[:space:]]' | sort | tr "\n" '|')
_vars=$(set | grep -Ev '^[A-Z_[:space:]]' | grep -v -F 'mockme=' | sort | tr "\n" '|')
_debug "variables at end of script: $_vars"
if [ -n "$mockme" ] && [ "$opt_mock" = 1 ]; then
if command -v "gzip" >/dev/null 2>&1; then
# not a useless use of cat: gzipping cpuinfo directly doesn't work well
# shellcheck disable=SC2002
if command -v "base64" >/dev/null 2>&1; then
mock_cpuinfo="$(cat /proc/cpuinfo | gzip -c | base64 -w0)"
elif command -v "uuencode" >/dev/null 2>&1; then
mock_cpuinfo="$(cat /proc/cpuinfo | gzip -c | uuencode -m - | grep -Fv 'begin-base64' | grep -Fxv -- '====' | tr -d "\n")"
fi
fi
if [ -n "$mock_cpuinfo" ]; then
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_CPUINFO='$mock_cpuinfo'")
unset mock_cpuinfo
fi
_info ""
# shellcheck disable=SC2046
_warn "To mock this CPU, set those vars: "$(echo "$mockme" | sort -u)
fi
if [ "$opt_explain" = 0 ]; then
_info "Need more detailed information about mitigation options? Use --explain"
fi
_info "A false sense of security is worse than no security at all, see --disclaimer"
if [ "$mocked" = 1 ]; then
_info ""
_warn "One or several values have been mocked. This should only be done when debugging/testing this script."
_warn "The results do NOT reflect the actual status of the system we're running on."
fi
if [ "$opt_batch" = 1 ] && [ "$opt_batch_format" = "nrpe" ]; then
if [ -n "$nrpe_vuln" ]; then
echo "Vulnerable:$nrpe_vuln"
@ -4460,7 +4708,7 @@ exit 0 # ok
# wget https://github.com/platomav/MCExtractor/raw/master/MCE.db
# sqlite3 MCE.db "select '%%% MCEDB v'||revision||' - '||strftime('%Y/%m/%d', date, 'unixepoch') from MCE; select '# I,0x'||cpuid||',0x'||version||','||max(yyyymmdd) from Intel group by cpuid order by cpuid asc; select '# A,0x'||cpuid||',0x'||version||','||max(yyyymmdd) from AMD group by cpuid order by cpuid asc"
# %%% MCEDB v110 - 2019/05/11
# %%% MCEDB v111 - 2019/05/18
# I,0x00000611,0x00000B27,19961218
# I,0x00000612,0x000000C6,19961210
# I,0x00000616,0x000000C6,19961210
@ -4639,8 +4887,8 @@ exit 0 # ok
# I,0x00030671,0x00000117,20130410
# I,0x00030672,0x0000022E,20140401
# I,0x00030673,0x00000326,20180110
# I,0x00030678,0x00000837,20180125
# I,0x00030679,0x0000090A,20180110
# I,0x00030678,0x00000838,20190422
# I,0x00030679,0x0000090C,20190423
# I,0x000306A0,0x00000007,20110407
# I,0x000306A2,0x0000000C,20110725
# I,0x000306A4,0x00000007,20110908
@ -4678,8 +4926,8 @@ exit 0 # ok
# I,0x000406A9,0x0000081F,20140812
# I,0x000406C1,0x0000010B,20140814
# I,0x000406C2,0x00000221,20150218
# I,0x000406C3,0x00000367,20171225
# I,0x000406C4,0x00000410,20180104
# I,0x000406C3,0x00000368,20190423
# I,0x000406C4,0x00000411,20190423
# I,0x000406D0,0x0000000E,20130612
# I,0x000406D8,0x0000012A,20180104
# I,0x000406E1,0x00000020,20141111
@ -4694,8 +4942,8 @@ exit 0 # ok
# I,0x00050653,0x01000146,20180824
# I,0x00050654,0x0200005E,20190402
# I,0x00050655,0x03000010,20181116
# I,0x00050656,0x04000021,20190227
# I,0x00050657,0x05000021,20190227
# I,0x00050656,0x04000024,20190407
# I,0x00050657,0x05000024,20190407
# I,0x00050661,0xF1000008,20150130
# I,0x00050662,0x0000001A,20190323
# I,0x00050663,0x07000017,20190323
@ -4715,7 +4963,7 @@ exit 0 # ok
# I,0x000506E3,0x000000CC,20190401
# I,0x000506E8,0x00000034,20160710
# I,0x000506F0,0x00000010,20160607
# I,0x000506F1,0x0000002A,20190211
# I,0x000506F1,0x0000002E,20190321
# I,0x00060660,0x0000000C,20160821
# I,0x00060661,0x0000000E,20170128
# I,0x00060662,0x00000022,20171129
@ -4730,8 +4978,8 @@ exit 0 # ok
# I,0x00080650,0x00000018,20180108
# I,0x000806E9,0x000000B4,20190401
# I,0x000806EA,0x000000B4,20190401
# I,0x000806EB,0x000000AE,20190214
# I,0x000806EC,0x000000B4,20190228
# I,0x000806EB,0x000000B8,20190330
# I,0x000806EC,0x000000B8,20190330
# I,0x000906E9,0x000000B4,20190401
# I,0x000906EA,0x000000B4,20190401
# I,0x000906EB,0x000000B4,20190401
@ -4808,10 +5056,10 @@ exit 0 # ok
# A,0x00800F12,0x08001230,20180804
# A,0x00800F82,0x0800820C,20190204
# A,0x00810F00,0x08100004,20161120
# A,0x00810F10,0x08101013,20181129
# A,0x00810F10,0x08101014,20190307
# A,0x00810F11,0x08101102,20181106
# A,0x00810F80,0x08108002,20180605
# A,0x00810F81,0x08108102,20180813
# A,0x00820F00,0x08200002,20180214
# A,0x00870F00,0x08700004,20181206
# A,0x00870F10,0x0870100A,20190322
# A,0x00870F10,0x08701011,20190415