feat: initial support for L1TF

This commit is contained in:
Stéphane Lesimple 2018-08-15 11:59:23 +02:00
parent 360be7b35f
commit e942616189
2 changed files with 120 additions and 29 deletions

View File

@ -2,11 +2,14 @@ Spectre & Meltdown Checker
==========================
A shell script to tell if your system is vulnerable against the several "speculative execution" CVEs that were made public in 2018.
- CVE-2017-5753 aka Spectre Variant 1
- CVE-2017-5715 aka Spectre Variant 2
- CVE-2017-5754 aka Meltdown or Variant 3
- CVE-2018-3640 aka Variant 3a
- CVE-2018-3639 aka Variant 4
- CVE-2017-5753 [bounds check bypass] aka 'Spectre Variant 1'
- CVE-2017-5715 [branch target injection] aka 'Spectre Variant 2'
- CVE-2017-5754 [rogue data cache load] aka 'Meltdown' aka 'Variant 3'
- CVE-2018-3640 [rogue system register read] aka 'Variant 3a'
- CVE-2018-3639 [speculative store bypass] aka 'Variant 4'
- CVE-2018-3615, CVE-2018-3620, CVE-2018-3646 [L1 terminal fault] aka 'Foreshadow & Foreshadow-NG'
**2018-08-15: Foreshadow support is being worked on, vulnerable/immune CPUs are already detected, and kernel-reported vulnerability status is checked, but manual mitigation detection is currently being implemented, will be available in the next few days**
Supported operating systems:
- Linux (all versions, flavors and distros)
@ -98,6 +101,18 @@ docker run --rm --privileged -v /boot:/boot:ro -v /lib/modules:/lib/modules:ro -
- Mitigation: microcode update + kernel update making possible for affected software to protect itself
- Performance impact of the mitigation: low to medium
**CVE-2018-3615** l1 terminal fault (Foreshadow)
- TBC
**CVE-2018-3620** l1 terminal fault (Foreshadow-NG)
- TBC
**CVE-2018-3646** l1 terminal fault (Foreshadow-NG)
- TBC
## Understanding what this script does and doesn't
This tool does its best to determine whether your system is immune (or has proper mitigations in place) for the collectively named "speculative execution" vulnerabilities. It doesn't attempt to run any kind of exploit, and can't guarantee that your system is secure, but rather helps you verifying whether your system has the known correct mitigations in place.

View File

@ -9,7 +9,7 @@
#
# Stephane Lesimple
#
VERSION='0.39'
VERSION='0.39+'
trap 'exit_cleanup' EXIT
trap '_warn "interrupted, cleaning up..."; exit_cleanup; exit 1' INT
@ -64,7 +64,7 @@ show_usage()
--batch nrpe produce machine readable output formatted for NRPE
--batch prometheus produce output for consumption by prometheus-node-exporter
--variant [1,2,3,3a,4] specify which variant you'd like to check, by default all variants are checked,
--variant [1,2,3,3a,4,l1tf] 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)
--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
@ -126,6 +126,7 @@ opt_variant2=0
opt_variant3=0
opt_variant3a=0
opt_variant4=0
opt_variantl1tf=0
opt_allvariants=1
opt_no_sysfs=0
opt_sysfs_only=0
@ -246,11 +247,12 @@ _is_cpu_vulnerable_cached()
{
# shellcheck disable=SC2086
{
[ "$1" = 1 ] && return $variant1
[ "$1" = 2 ] && return $variant2
[ "$1" = 3 ] && return $variant3
[ "$1" = 3a ] && return $variant3a
[ "$1" = 4 ] && return $variant4
[ "$1" = 1 ] && return $variant1
[ "$1" = 2 ] && return $variant2
[ "$1" = 3 ] && return $variant3
[ "$1" = 3a ] && return $variant3a
[ "$1" = 4 ] && return $variant4
[ "$1" = l1tf ] && return $variantl1tf
}
echo "$0: error: invalid variant '$1' passed to is_cpu_vulnerable()" >&2
exit 255
@ -273,6 +275,7 @@ is_cpu_vulnerable()
variant3=''
variant3a=''
variant4=''
variantl1tf=''
if is_cpu_specex_free; then
variant1=immune
@ -280,6 +283,7 @@ is_cpu_vulnerable()
variant3=immune
variant3a=immune
variant4=immune
variantl1tf=immune
elif is_intel; then
# Intel
# https://github.com/crozone/SpectrePoC/issues/1 ^F E5200 => spectre 2 not vulnerable
@ -294,20 +298,52 @@ is_cpu_vulnerable()
# capability bit for future Intel processor that will explicitly state
# that they're not vulnerable to Meltdown
# this var is set in check_cpu()
variant3=immune
_debug "is_cpu_vulnerable: RDCL_NO is set so not vuln to meltdown"
[ -z "$variant3" ] && variant3=immune
[ -z "$variantl1tf" ] && variantl1tf=immune
_debug "is_cpu_vulnerable: RDCL_NO is set so not vuln to meltdown nor l1tf"
fi
if [ "$capabilities_ssb_no" = 1 ]; then
# capability bit for future Intel processor that will explicitly state
# that they're not vulnerable to Variant 4
# this var is set in check_cpu()
variant4=immune
[ -z "$variant4" ] && variant4=immune
_debug "is_cpu_vulnerable: SSB_NO is set so not vuln to variant4"
fi
if is_cpu_ssb_free; then
[ -z "$variant4" ] && variant4=immune
_debug "is_cpu_vulnerable: cpu not affected by speculative store bypass so not vuln to variant4"
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
_debug "is_cpu_vulnerable: xeon phi immune to variant 3a"
[ -z "$variant3a" ] && variant3a=immune
fi
fi
# L1TF (RDCL_NO already checked above)
if [ "$cpu_family" = 6 ]; then
if [ "$cpu_model" = "$INTEL_FAM6_ATOM_CEDARVIEW" ] || \
[ "$cpu_model" = "$INTEL_FAM6_ATOM_CLOVERVIEW" ] || \
[ "$cpu_model" = "$INTEL_FAM6_ATOM_LINCROFT" ] || \
[ "$cpu_model" = "$INTEL_FAM6_ATOM_PENWELL" ] || \
[ "$cpu_model" = "$INTEL_FAM6_ATOM_PINEVIEW" ] || \
[ "$cpu_model" = "$INTEL_FAM6_ATOM_SILVERMONT1" ] || \
[ "$cpu_model" = "$INTEL_FAM6_ATOM_SILVERMONT2" ] || \
[ "$cpu_model" = "$INTEL_FAM6_ATOM_AIRMONT" ] || \
[ "$cpu_model" = "$INTEL_FAM6_ATOM_MERRIFIELD" ] || \
[ "$cpu_model" = "$INTEL_FAM6_XEON_PHI_KNL" ] || \
[ "$cpu_model" = "$INTEL_FAM6_XEON_PHI_KNM" ]; then
_debug "is_cpu_vulnerable: intel family 6 but model known to be immune"
[ -z "$variantl1tf" ] && variantl1tf=immune
else
_debug "is_cpu_vulnerable: intel family 6 is vuln"
variantl1tf=vuln
fi
elif [ "$cpu_family" -lt 6 ]; then
_debug "is_cpu_vulnerable: intel family < 6 is immune"
[ -z "$variantl1tf" ] && variantl1tf=immune
fi
elif is_amd; then
# AMD revised their statement about variant2 => vulnerable
# https://www.amd.com/en/corporate/speculative-execution
@ -321,9 +357,11 @@ is_cpu_vulnerable()
[ -z "$variant4" ] && variant4=immune
_debug "is_cpu_vulnerable: cpu not affected by speculative store bypass so not vuln to variant4"
fi
variantl1tf=immune
elif [ "$cpu_vendor" = CAVIUM ]; then
variant3=immune
variant3a=immune
variantl1tf=immune
elif [ "$cpu_vendor" = ARM ]; then
# ARM
# reference: https://developer.arm.com/support/security-update
@ -411,14 +449,16 @@ is_cpu_vulnerable()
fi
_debug "is_cpu_vulnerable: for cpu$i and so far, we have <$variant1> <$variant2> <$variant3> <$variant3a> <$variant4>"
done
variantl1tf=immune
fi
_debug "is_cpu_vulnerable: temp results are <$variant1> <$variant2> <$variant3> <$variant3a> <$variant4>"
[ "$variant1" = "immune" ] && variant1=1 || variant1=0
[ "$variant2" = "immune" ] && variant2=1 || variant2=0
[ "$variant3" = "immune" ] && variant3=1 || variant3=0
[ "$variant3a" = "immune" ] && variant3a=1 || variant3a=0
[ "$variant4" = "immune" ] && variant4=1 || variant4=0
_debug "is_cpu_vulnerable: final results are <$variant1> <$variant2> <$variant3> <$variant3a> <$variant4>"
_debug "is_cpu_vulnerable: temp results are <$variant1> <$variant2> <$variant3> <$variant3a> <$variant4> <$variantl1tf>"
[ "$variant1" = "immune" ] && variant1=1 || variant1=0
[ "$variant2" = "immune" ] && variant2=1 || variant2=0
[ "$variant3" = "immune" ] && variant3=1 || variant3=0
[ "$variant3a" = "immune" ] && variant3a=1 || variant3a=0
[ "$variant4" = "immune" ] && variant4=1 || variant4=0
[ "$variantl1tf" = "immune" ] && variantl1tf=1 || variantl1tf=0
_debug "is_cpu_vulnerable: final results are <$variant1> <$variant2> <$variant3> <$variant3a> <$variant4> <$variantl1tf>"
is_cpu_vulnerable_cached=1
_is_cpu_vulnerable_cached "$1"
return $?
@ -607,17 +647,18 @@ while [ -n "$1" ]; do
shift
elif [ "$1" = "--variant" ]; then
if [ -z "$2" ]; then
echo "$0: error: option --variant expects a parameter (1, 2, 3, 3a or 4)" >&2
echo "$0: error: option --variant expects a parameter (1, 2, 3, 3a, 4 or l1tf)" >&2
exit 255
fi
case "$2" in
1) opt_variant1=1; opt_allvariants=0;;
2) opt_variant2=1; opt_allvariants=0;;
3) opt_variant3=1; opt_allvariants=0;;
3a) opt_variant3a=1; opt_allvariants=0;;
4) opt_variant4=1; opt_allvariants=0;;
1) opt_variant1=1; opt_allvariants=0;;
2) opt_variant2=1; opt_allvariants=0;;
3) opt_variant3=1; opt_allvariants=0;;
3a) opt_variant3a=1; opt_allvariants=0;;
4) opt_variant4=1; opt_allvariants=0;;
l1tf) opt_variantl1tf=1; opt_allvariants=0;;
*)
echo "$0: error: invalid parameter '$2' for --variant, expected either 1, 2, 3, 3a or 4" >&2;
echo "$0: error: invalid parameter '$2' for --variant, expected either 1, 2, 3, 3a, 4 or l1tf" >&2;
exit 255
;;
esac
@ -686,6 +727,7 @@ pvulnstatus()
CVE-2017-5754) aka="MELTDOWN";;
CVE-2018-3640) aka="VARIANT 3A";;
CVE-2018-3639) aka="VARIANT 4";;
CVE-2018-3615/3620/3646) aka="L1TF";;
esac
case "$opt_batch_format" in
@ -3180,6 +3222,36 @@ check_variant4()
fi
}
check_variantl1tf()
{
_info "\033[1;34mCVE-2018-3615/3620/3646 [L1 terminal fault] aka 'Foreshadow & Foreshadow-NG'\033[0m"
status=UNK
sys_interface_available=0
msg=''
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
if [ "$opt_sysfs_only" != 1 ]; then
:
elif [ "$sys_interface_available" = 0 ]; then
# we have no sysfs but were asked to use it only!
msg="/sys vulnerability interface use forced, but it's not available!"
status=UNK
fi
cve='CVE-2018-3615/3620/3646'
if ! is_cpu_vulnerable l1tf; 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 [ -z "$msg" ]; then
pvulnstatus $cve VULN "your CPU is known to be vulnerable, and your kernel doesn't report that it mitigates the issue, but more thorough mitigation checking by this script is being worked on (check often for new versions!)"
else
pvulnstatus $cve "$status" "$msg"
fi
}
if [ "$opt_no_hw" = 0 ] && [ -z "$opt_arch_prefix" ]; then
check_cpu
check_cpu_vulnerabilities
@ -3207,6 +3279,10 @@ if [ "$opt_variant4" = 1 ] || [ "$opt_allvariants" = 1 ]; then
check_variant4
_info
fi
if [ "$opt_variantl1tf" = 1 ] || [ "$opt_allvariants" = 1 ]; then
check_variantl1tf
_info
fi
_vars=$(set | grep -Ev '^[A-Z_[:space:]]' | sort | tr "\n" '|')
_debug "variables at end of script: $_vars"