LazyFPState Restore / CVE-2018-3665

Adds detection of vulnerable hardware (all intel core-based processors)
https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00145.html

Adds detection of kernel mitigations based on version, boot options and processor type
https://xenbits.xen.org/xsa/advisory-267.html

Signed-off-by: Rob Gill <rrobgill@protonmail.com>
This commit is contained in:
Rob Gill 2018-08-16 20:25:16 +10:00
parent fc1dffd09a
commit f03382b81f
1 changed files with 147 additions and 3 deletions

View File

@ -127,6 +127,7 @@ opt_variant3=0
opt_variant3a=0
opt_variant4=0
opt_variantl1tf=0
opt_variantlfps=0
opt_allvariants=1
opt_no_sysfs=0
opt_sysfs_only=0
@ -253,6 +254,7 @@ _is_cpu_vulnerable_cached()
[ "$1" = 3a ] && return $variant3a
[ "$1" = 4 ] && return $variant4
[ "$1" = l1tf ] && return $variantl1tf
[ "$1" = lfps ] && return $variantlfps
}
echo "$0: error: invalid variant '$1' passed to is_cpu_vulnerable()" >&2
exit 255
@ -276,6 +278,7 @@ is_cpu_vulnerable()
variant3a=''
variant4=''
variantl1tf=''
variantlfps=''
if is_cpu_specex_free; then
variant1=immune
@ -284,6 +287,7 @@ is_cpu_vulnerable()
variant3a=immune
variant4=immune
variantl1tf=immune
variantlfps=immune
elif is_intel; then
# Intel
# https://github.com/crozone/SpectrePoC/issues/1 ^F E5200 => spectre 2 not vulnerable
@ -344,6 +348,30 @@ is_cpu_vulnerable()
_debug "is_cpu_vulnerable: intel family < 6 is immune"
[ -z "$variantl1tf" ] && variantl1tf=immune
fi
# LFPS
if [ "$cpu_family" = 6 ]; then
# LFPS affects all Core-based intel processors
# (https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00145.html)
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" ]; then
_debug "is_cpu_vulnerable: intel family 6 but model known to be immune"
[ -z "$variantlfps" ] && variantlfps=immune
else
_debug "is_cpu_vulnerable: intel family 6 is vuln"
variantlfps=vuln
fi
elif [ "$cpu_family" -lt 6 ]; then
_debug "is_cpu_vulnerable: intel family < 6 is immune"
[ -z "$variantl1tf" ] && variantlfps=immune
fi
elif is_amd; then
# AMD revised their statement about variant2 => vulnerable
# https://www.amd.com/en/corporate/speculative-execution
@ -358,10 +386,12 @@ is_cpu_vulnerable()
_debug "is_cpu_vulnerable: cpu not affected by speculative store bypass so not vuln to variant4"
fi
variantl1tf=immune
variantlfps=immune
elif [ "$cpu_vendor" = CAVIUM ]; then
variant3=immune
variant3a=immune
variantl1tf=immune
variantlfps=immune
elif [ "$cpu_vendor" = ARM ]; then
# ARM
# reference: https://developer.arm.com/support/security-update
@ -450,15 +480,17 @@ is_cpu_vulnerable()
_debug "is_cpu_vulnerable: for cpu$i and so far, we have <$variant1> <$variant2> <$variant3> <$variant3a> <$variant4>"
done
variantl1tf=immune
variantlfps=immune
fi
_debug "is_cpu_vulnerable: temp results are <$variant1> <$variant2> <$variant3> <$variant3a> <$variant4> <$variantl1tf>"
_debug "is_cpu_vulnerable: temp results are <$variant1> <$variant2> <$variant3> <$variant3a> <$variant4> <$variantl1tf> <variantlfps>"
[ "$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>"
[ "$variantlfps" = "immune" ] && variantlfps=1 || variantlfps=0
_debug "is_cpu_vulnerable: final results are <$variant1> <$variant2> <$variant3> <$variant3a> <$variant4> <$variantl1tf> <variantlfps>"
is_cpu_vulnerable_cached=1
_is_cpu_vulnerable_cached "$1"
return $?
@ -657,6 +689,7 @@ while [ -n "$1" ]; do
3a) opt_variant3a=1; opt_allvariants=0;;
4) opt_variant4=1; opt_allvariants=0;;
l1tf) opt_variantl1tf=1; opt_allvariants=0;;
lfps) opt_variantlfps=1; opt_allvariants=0;;
*)
echo "$0: error: invalid parameter '$2' for --variant, expected either 1, 2, 3, 3a, 4 or l1tf" >&2;
exit 255
@ -728,6 +761,7 @@ pvulnstatus()
CVE-2018-3640) aka="VARIANT 3A";;
CVE-2018-3639) aka="VARIANT 4";;
CVE-2018-3615/3620/3646) aka="L1TF";;
CVE-2018-3655) aka="LAZY FP STATE RESTORE";;
esac
case "$opt_batch_format" in
@ -2094,7 +2128,7 @@ check_cpu()
check_cpu_vulnerabilities()
{
_info "* CPU vulnerability to the speculative execution attack variants"
for v in 1 2 3 3a 4; do
for v in 1 2 3 3a 4 l1tf lfps; do
_info_nol " * Vulnerable to Variant $v: "
if is_cpu_vulnerable $v; then
pstatus yellow YES
@ -3330,6 +3364,112 @@ check_variantl1tf()
fi
}
check_variantlfps()
{
_info "\033[1;34mCVE-2018-3665 [Lazy FP State Restore]\033[0m"
status=UNK
sys_interface_available=0
cve='CVE-2018-3665'
msg=''
if ! is_cpu_vulnerable lfps; then
# Dont bother with kernel checking if CPU is not vulnerable
msg="your CPU vendor reported your CPU model as not vulnerable"
status=OK
elif [ "$opt_sysfs_only" != 1 ]; then
if [ "$opt_live" = 1 ]; then
if [ "$os" = Linux ]; then
## Get kernel version number
kern_major=$(uname -r | cut -d. -f1)
kern_minor=$(uname -r | cut -d. -f2)
kern_point=$(uname -r | cut -d. -f3)
# Different versions are vulnerable under different circumstances
# https://blog.cyberus-technology.de/posts/2018-06-06-intel-lazyfp-vulnerability.html
# Newer kernels (>4.9) are immune
if [ "$kern_major" = "4" ]; then
if [ "$kern_minor" -gt "8" ]; then
status=OK
msg="Mitigation: Kernel version is $kern_major.$kern_minor, kernel > 4.9 not vulnerable to Lazy FP State Restore"
# older kernels are vulnerable, or immune, depending upon certain conditions
# 4.6-4.8 are vulnerable if default boot options are overridden
elif [ $"kern_minor" -gt "5" ]; then
if grep -q -w eagerfpu=off /proc/commandline ; then
status=VULN
msg="your kernel supports Eager FPU state restoration, but it is disabled at boot"
explain="kernel > 4.6, but boot option eagerfpu=off reverts to lazy FP state restoration"
else
status=OK
msg="Mitigation: Kernel version is $kern_major.$kern_minor"
explain="kernel > 4.6 and lazy fp state restore not enabled via boot options"
fi
# kernel 4.4 < 138 has its own issues
elif [ "$kern_minor" = "4" ] && [ "$kern_point" -lt "138" ]; then
status=VULN
msg="your kernel version $kern_major.$kern_minor has documented a bug in eager FPU switching code"
explain="kernel versions 4.4 before point release 138 have a bug in the eager FPU switching code, leaving them vulnerable to Lazy FP State Restore"
else
# <4.6 are vulnerable on pre-Haswell hardware, or if default boot options are overridden
if grep -q -w eagerfpu=off /proc/commandline ; then
status=VULN
msg="your kernel supports Eager FPU state restoration, but it is disabled at boot"
explain="kernel < 4.6, but boot option eagerfpu=off reverts to lazy FP state restoration"
elif grep -q -w noxsave /proc/commandline ; then
status=VULN
msg="your kernel supports Eager FPU state restoration, but it is disabled at boot"
explain="kernel < 4.6, but boot option noxsave reverts to lazy FP state restoration"
elif echo "$cpupart" | grep -q -w -e HASWELL -e BROADWELL -e SKYLAKE -e KABYLAKE; then
status=OK
msg="Mitigation: your kernel version $kern_major.$kern_minor is immunne when running a Haswell or newer cpu, provided lazy fp state restore not enabled via boot options"
else
status=VULN
msg="your kernel supports Eager FPU state restoration, but not for this cpu"
explain="kernel < 4.6 supports Eager FPU state restoration, but only on Haswell or newer cpus"
fi
fi
fi
# Very old kernels (< 3.7) are vulnerable under all conditions
if [ "$kern_major" -lt "3" ]; then
status=VULN
msg="your kernel version $kern_major.$kern_minor is older than kernel 3.7, and is vulnerable to Lazy FP State Restore on all vulnerable processors"
fi
if [ "$kern_major" = "3" ]; then
if [ "$kern_minor" -lt "7" ]; then
status=VULN
msg="your kernel version $kern_major.$kern_minor is older than kernel 3.7, and is vulnerable to Lazy FP State Restore on all vulnerable processors" else
# Kernel versions 3.7 - 3.9 on pre-Haswell hardware, or if default boot options are overridden
if grep -q -w eagerfpu=off /proc/commandline ; then
status=VULN
msg="your kernel supports Eager FPU state restoration, but it is disabled at boot"
explain="kernel > 3.7, but boot option eagerfpu=off reverts to lazy FP state restoration"
elif grep -q -w noxsave /proc/commandline ; then
status=VULN
msg="your kernel supports Eager FPU state restoration, but it is disabled at boot"
explain="kernel > 3.7, but boot option noxsave reverts to lazy FP state restoration"
elif echo "$cpupart" | grep -q -w -e HASWELL -e BROADWELL -e SKYLAKE -e KABYLAKE; then
status=OK
msg="Mitigation: your kernel version $kern_major.$kern_minor is immunne when running a Haswell or newer cpu, provided lazy fp state restore not enabled via boot options"
else
status=VULN
msg="your kernel supports Eager FPU state restoration, but not for this cpu"
explain="kernel > 3.7 supports Eager FPU state restoration, but only on Haswell or newer cpus"
fi
fi
fi
else
status=VULN
msg="your CPU is known to be vulnerable, but more thorough mitigation checking by this script is being worked on (check often for new versions!)"
fi
fi
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
pvulnstatus $cve "$status" "$msg"
}
if [ "$opt_no_hw" = 0 ] && [ -z "$opt_arch_prefix" ]; then
check_cpu
check_cpu_vulnerabilities
@ -3361,6 +3501,10 @@ if [ "$opt_variantl1tf" = 1 ] || [ "$opt_allvariants" = 1 ]; then
check_variantl1tf
_info
fi
if [ "$opt_variantlfps" = 1 ] || [ "$opt_allvariants" = 1 ]; then
check_variantlfps
_info
fi
_vars=$(set | grep -Ev '^[A-Z_[:space:]]' | sort | tr "\n" '|')
_debug "variables at end of script: $_vars"