2018-01-07 15:00:59 +01:00
#! /bin/sh
2018-09-18 20:00:53 +02:00
# SPDX-License-Identifier: GPL-3.0-only
2018-09-15 15:33:41 +02:00
#
2018-01-07 15:00:59 +01:00
# Spectre & Meltdown checker
2018-01-09 09:21:42 +01:00
#
# Check for the latest version at:
# https://github.com/speed47/spectre-meltdown-checker
# git clone https://github.com/speed47/spectre-meltdown-checker.git
2018-03-16 18:24:59 +01:00
# or wget https://meltdown.ovh -O spectre-meltdown-checker.sh
# or curl -L https://meltdown.ovh -o spectre-meltdown-checker.sh
2018-01-09 09:21:42 +01:00
#
2018-01-07 16:22:30 +01:00
# Stephane Lesimple
2018-01-09 09:21:42 +01:00
#
2018-10-03 20:56:46 +02:00
VERSION = '0.40'
2018-01-07 15:00:59 +01:00
2018-02-02 11:09:36 +01:00
trap 'exit_cleanup' EXIT
trap '_warn "interrupted, cleaning up..."; exit_cleanup; exit 1' INT
exit_cleanup( )
{
# cleanup the temp decompressed config & kernel image
[ -n " $dumped_config " ] && [ -f " $dumped_config " ] && rm -f " $dumped_config "
2018-04-01 17:29:12 +02:00
[ -n " $kerneltmp " ] && [ -f " $kerneltmp " ] && rm -f " $kerneltmp "
[ -n " $kerneltmp2 " ] && [ -f " $kerneltmp2 " ] && rm -f " $kerneltmp2 "
2018-09-23 17:29:14 +02:00
[ -n " $mcedb_tmp " ] && [ -f " $mcedb_tmp " ] && rm -f " $mcedb_tmp "
2018-02-02 11:09:36 +01:00
[ " $mounted_debugfs " = 1 ] && umount /sys/kernel/debug 2>/dev/null
2018-03-25 12:23:46 +02:00
[ " $mounted_procfs " = 1 ] && umount " $procfs " 2>/dev/null
2018-02-02 11:09:36 +01:00
[ " $insmod_cpuid " = 1 ] && rmmod cpuid 2>/dev/null
[ " $insmod_msr " = 1 ] && rmmod msr 2>/dev/null
2018-03-25 12:23:46 +02:00
[ " $kldload_cpuctl " = 1 ] && kldunload cpuctl 2>/dev/null
2019-04-19 19:47:04 +02:00
[ " $kldload_vmm " = 1 ] && kldunload vmm 2>/dev/null
2018-02-02 11:09:36 +01:00
}
2018-08-16 15:18:47 +02:00
# if we were git clone'd, adjust VERSION
if [ -d " $( dirname " $0 " ) /.git " ] && which git >/dev/null 2>& 1; then
describe = $( git -C " $( dirname " $0 " ) " describe --tags --dirty 2>/dev/null)
[ -n " $describe " ] && VERSION = $( echo " $describe " | sed -e s/^v//)
fi
2018-01-08 20:50:42 +01:00
show_usage( )
{
2018-01-29 10:29:48 +01:00
# shellcheck disable=SC2086
2018-01-08 20:50:42 +01:00
cat <<EOF
Usage:
2018-01-29 10:29:48 +01:00
Live mode: $( basename $0 ) [ options] [ --live]
2018-04-01 17:29:12 +02:00
Offline mode: $( basename $0 ) [ options] [ --kernel <kernel_file>] [ --config <kernel_config>] [ --map <kernel_map_file>]
2018-01-09 10:32:51 +01:00
Modes:
2018-01-08 20:50:42 +01:00
Two modes are available.
First mode is the "live" mode ( default) , it does its best to find information about the currently running kernel.
2018-01-12 11:46:36 +01:00
To run under this mode, just start the script without any option ( you can also use --live explicitly)
2018-01-08 20:50:42 +01:00
Second mode is the "offline" mode, where you can inspect a non-running kernel.
2018-04-01 17:29:12 +02:00
You' ll need to specify the location of the kernel file, config and System.map files:
2018-01-08 20:50:42 +01:00
2018-03-25 12:27:40 +02:00
--kernel kernel_file specify a ( possibly compressed) Linux or BSD kernel file
--config kernel_config specify a kernel config file ( Linux only)
--map kernel_map_file specify a kernel System.map file ( Linux only)
2018-01-08 20:50:42 +01:00
2018-01-09 10:32:51 +01:00
Options:
2018-03-25 12:27:40 +02:00
--no-color don' t use color codes
--verbose, -v increase verbosity level, possibly several times
2018-08-04 16:31:41 +02:00
--explain produce an additional human-readable explanation of actions to take to mitigate a vulnerability
2018-04-15 23:05:30 +02:00
--paranoid require IBPB to deem Variant 2 as mitigated
2018-09-17 21:44:22 +02:00
also require SMT disabled + unconditional L1D flush to deem Foreshadow-NG VMM as mitigated
2018-03-25 12:27:40 +02:00
--no-sysfs don' t use the /sys interface even if present [ Linux]
--sysfs-only only use the /sys interface, don' t run our own checks [ Linux]
--coreos special mode for CoreOS ( use an ephemeral toolbox to inspect kernel) [ Linux]
--arch-prefix PREFIX specify a prefix for cross-inspecting a kernel of a different arch, for example "aarch64-linux-gnu-" ,
so that invoked tools will be prefixed with this ( i.e. aarch64-linux-gnu-objdump)
--batch text produce machine readable output, this is the default if --batch is specified alone
2018-09-15 15:45:10 +02:00
--batch short produce only one line with the vulnerabilities separated by spaces
2018-03-25 12:27:40 +02:00
--batch json produce JSON output formatted for Puppet, Ansible, Chef...
--batch nrpe produce machine readable output formatted for NRPE
--batch prometheus produce output for consumption by prometheus-node-exporter
2018-09-17 21:44:22 +02:00
--variant [ 1,2,3,3a,4,l1tf] specify which variant you' d like to check, by default all variants are checked
--cve [ cve1,cve2,...] specify which CVE you' d like to check, by default all supported CVEs are checked
2018-03-25 12:27:40 +02:00
can be specified multiple times ( e.g. --variant 2 --variant 3)
2018-04-08 17:56:55 +02:00
--hw-only only check for CPU information, don' t check for any variant
2018-04-03 19:34:24 +02:00
--no-hw skip CPU information and checks, if you' re inspecting a kernel not to be run on this host
2018-12-10 19:33:07 +01:00
--vmm [ auto,yes,no] override the detection of the presence of a hypervisor ( for CVE-2018-3646) , default: auto
2018-09-23 17:29:14 +02:00
--update-mcedb update our local copy of the CPU microcodes versions database ( from the awesome MCExtractor project)
2018-09-30 16:56:58 +02:00
--update-builtin-mcedb same as --update-mcedb but update builtin DB inside the script itself
2018-01-09 10:32:51 +01:00
2018-01-16 10:33:01 +01:00
Return codes:
0 ( not vulnerable) , 2 ( vulnerable) , 3 ( unknown) , 255 ( error)
2018-01-12 19:10:44 +01:00
2018-01-09 18:52:21 +01:00
IMPORTANT:
A false sense of security is worse than no security at all.
Please use the --disclaimer option to understand exactly what this script does.
EOF
}
show_disclaimer( )
{
cat <<EOF
Disclaimer:
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.
However, some mitigations could also exist in your kernel that this script doesn' t know ( yet) how to detect, or it might
falsely detect mitigations that in the end don' t work as expected ( for example, on backported or modified kernels) .
Your system exposure also depends on your CPU. As of now, AMD and ARM processors are marked as immune to some or all of these
2018-01-30 14:33:30 +01:00
vulnerabilities ( except some specific ARM models) . All Intel processors manufactured since circa 1995 are thought to be vulnerable,
except some specific/old models, such as some early Atoms. Whatever processor one uses, one might seek more information
from the manufacturer of that processor and/or of the device in which it runs.
2018-01-09 18:52:21 +01:00
The nature of the discovered vulnerabilities being quite new, the landscape of vulnerable processors can be expected
to change over time, which is why this script makes the assumption that all CPUs are vulnerable, except if the manufacturer
2018-01-12 11:46:36 +01:00
explicitly stated otherwise in a verifiable public announcement.
2018-01-09 18:52:21 +01:00
2018-01-30 14:33:30 +01:00
Please also note that for Spectre vulnerabilities, all software can possibly be exploited, this tool only verifies that the
kernel ( which is the core of the system) you' re using has the proper protections in place. Verifying all the other software
is out of the scope of this tool. As a general measure, ensure you always have the most up to date stable versions of all
2018-04-08 17:56:55 +02:00
the software you use, especially for those who are exposed to the world, such as network daemons and browsers.
2018-01-30 14:33:30 +01:00
2018-01-09 18:52:21 +01:00
This tool has been released in the hope that it'll be useful, but don' t use it to jump to conclusions about your security.
2018-01-08 20:50:42 +01:00
EOF
}
2018-03-25 12:23:46 +02:00
os = $( uname -s)
2018-01-09 13:04:49 +01:00
# parse options
opt_kernel = ''
opt_config = ''
opt_map = ''
opt_live_explicit = 0
opt_live = 1
opt_no_color = 0
2018-01-09 13:37:26 +01:00
opt_batch = 0
2018-12-10 19:33:07 +01:00
opt_batch_format = 'text'
2018-01-09 13:37:26 +01:00
opt_verbose = 1
2018-09-17 21:44:22 +02:00
opt_cve_list = ''
opt_cve_all = 1
2018-01-11 12:23:16 +01:00
opt_no_sysfs = 0
2018-01-21 18:02:11 +01:00
opt_sysfs_only = 0
2018-01-16 10:33:01 +01:00
opt_coreos = 0
2018-03-17 13:17:10 +01:00
opt_arch_prefix = ''
2018-03-25 12:48:37 +02:00
opt_hw_only = 0
2018-04-03 19:34:24 +02:00
opt_no_hw = 0
2018-09-17 21:44:22 +02:00
opt_vmm = -1
2018-08-04 16:31:41 +02:00
opt_explain = 0
2018-04-15 23:05:30 +02:00
opt_paranoid = 0
2018-01-10 12:57:45 +01:00
2018-01-13 20:33:12 +01:00
global_critical = 0
global_unknown = 0
2018-12-10 19:33:07 +01:00
nrpe_vuln = ''
2018-01-09 13:04:49 +01:00
2018-09-17 21:44:22 +02:00
supported_cve_list = 'CVE-2017-5753 CVE-2017-5715 CVE-2017-5754 CVE-2018-3640 CVE-2018-3639 CVE-2018-3615 CVE-2018-3620 CVE-2018-3646'
2018-03-25 12:23:46 +02:00
# find a sane command to print colored messages, we prefer `printf` over `echo`
# because `printf` behavior is more standard across Linux/BSD
2018-01-24 17:55:36 +01:00
# we'll try to avoid using shell builtins that might not take options
2018-03-25 12:23:46 +02:00
echo_cmd_type = echo
if which printf >/dev/null 2>& 1; then
echo_cmd = $( which printf )
echo_cmd_type = printf
elif which echo >/dev/null 2>& 1; then
2018-01-29 10:29:48 +01:00
echo_cmd = $( which echo )
2018-01-24 17:55:36 +01:00
else
2018-08-11 10:31:10 +02:00
# maybe the `which` command is broken?
2018-01-24 17:55:36 +01:00
[ -x /bin/echo ] && echo_cmd = /bin/echo
2018-03-25 12:23:46 +02:00
# for Android
2018-01-24 17:55:36 +01:00
[ -x /system/bin/echo ] && echo_cmd = /system/bin/echo
fi
2018-08-11 10:31:10 +02:00
# still empty? fallback to builtin
2018-01-24 17:55:36 +01:00
[ -z " $echo_cmd " ] && echo_cmd = echo
2018-01-09 10:32:51 +01:00
__echo( )
{
opt = " $1 "
shift
2018-01-29 10:29:48 +01:00
_msg = " $* "
2018-01-18 11:00:40 +01:00
2018-01-09 10:32:51 +01:00
if [ " $opt_no_color " = 1 ] ; then
# strip ANSI color codes
2018-02-02 10:59:49 +01:00
# some sed versions (i.e. toybox) can't seem to handle
# \033 aka \x1B correctly, so do it for them.
2018-03-25 12:23:46 +02:00
if [ " $echo_cmd_type " = printf ] ; then
_interpret_chars = ''
else
_interpret_chars = '-e'
fi
_ctrlchar = $( $echo_cmd $_interpret_chars "\033" )
_msg = $( $echo_cmd $_interpret_chars " $_msg " | sed -r " s/ $_ctrlchar \[([0-9][0-9]?(;[0-9][0-9]?)?)?m//g " )
fi
if [ " $echo_cmd_type " = printf ] ; then
if [ " $opt " = "-n" ] ; then
$echo_cmd " $_msg "
else
$echo_cmd " $_msg \n "
fi
else
# shellcheck disable=SC2086
$echo_cmd $opt -e " $_msg "
2018-01-09 10:32:51 +01:00
fi
}
_echo( )
{
2018-01-29 10:29:48 +01:00
if [ " $opt_verbose " -ge " $1 " ] ; then
2018-01-09 13:37:26 +01:00
shift
2018-01-29 10:29:48 +01:00
__echo '' " $* "
2018-01-09 13:37:26 +01:00
fi
2018-01-09 10:32:51 +01:00
}
_echo_nol( )
{
2018-01-29 10:29:48 +01:00
if [ " $opt_verbose " -ge " $1 " ] ; then
2018-01-09 13:37:26 +01:00
shift
2018-01-29 10:29:48 +01:00
__echo -n " $* "
2018-01-09 13:37:26 +01:00
fi
}
_warn( )
{
2018-01-29 10:29:48 +01:00
_echo 0 " \033[31m $* \033[0m " >& 2
2018-01-09 13:37:26 +01:00
}
_info( )
{
2018-01-29 10:29:48 +01:00
_echo 1 " $* "
2018-01-09 13:37:26 +01:00
}
_info_nol( )
{
2018-01-29 10:29:48 +01:00
_echo_nol 1 " $* "
2018-01-09 13:37:26 +01:00
}
_verbose( )
{
2018-01-29 10:29:48 +01:00
_echo 2 " $* "
2018-01-09 10:32:51 +01:00
}
2018-01-14 15:44:11 +01:00
_verbose_nol( )
{
2018-01-29 10:29:48 +01:00
_echo_nol 2 " $* "
2018-01-14 15:44:11 +01:00
}
2018-01-10 15:46:29 +01:00
_debug( )
{
2018-01-29 10:29:48 +01:00
_echo 3 " \033[34m(debug) $* \033[0m "
2018-01-10 15:46:29 +01:00
}
2018-04-05 00:03:04 +02:00
explain( )
{
2018-08-04 16:31:41 +02:00
if [ " $opt_explain " = 1 ] ; then
2018-04-05 00:03:04 +02:00
_info ''
2018-04-07 18:04:06 +02:00
_info " > \033[41m\033[30mHow to fix:\033[0m $* "
2018-04-05 00:03:04 +02:00
fi
}
2018-09-17 21:44:22 +02:00
cve2name( )
{
case " $1 " in
CVE-2017-5753) echo "Spectre Variant 1, bounds check bypass" ; ;
CVE-2017-5715) echo "Spectre Variant 2, branch target injection" ; ;
CVE-2017-5754) echo "Variant 3, Meltdown, rogue data cache load" ; ;
CVE-2018-3640) echo "Variant 3a, rogue system register read" ; ;
CVE-2018-3639) echo "Variant 4, speculative store bypass" ; ;
CVE-2018-3615) echo "Foreshadow (SGX), L1 terminal fault" ; ;
CVE-2018-3620) echo "Foreshadow-NG (OS), L1 terminal fault" ; ;
CVE-2018-3646) echo "Foreshadow-NG (VMM), L1 terminal fault" ; ;
esac
}
2018-01-18 21:16:14 +01:00
is_cpu_vulnerable_cached = 0
_is_cpu_vulnerable_cached( )
{
2018-01-29 10:29:48 +01:00
# shellcheck disable=SC2086
2018-09-17 21:44:22 +02:00
case " $1 " in
CVE-2017-5753) return $variant1 ; ;
CVE-2017-5715) return $variant2 ; ;
CVE-2017-5754) return $variant3 ; ;
CVE-2018-3640) return $variant3a ; ;
CVE-2018-3639) return $variant4 ; ;
CVE-2018-3615) return $variantl1tf_sgx ; ;
CVE-2018-3620) return $variantl1tf ; ;
CVE-2018-3646) return $variantl1tf ; ;
esac
2018-01-18 21:16:14 +01:00
echo " $0 : error: invalid variant ' $1 ' passed to is_cpu_vulnerable() " >& 2
exit 255
}
2018-01-09 13:11:48 +01:00
is_cpu_vulnerable( )
{
2018-09-17 21:44:22 +02:00
# param: one of the $supported_cve_list items
2018-01-13 13:16:37 +01:00
# returns 0 if vulnerable, 1 if not vulnerable
# (note that in shell, a return of 0 is success)
2018-01-09 14:54:17 +01:00
# by default, everything is vulnerable, we work in a "whitelist" logic here.
2018-09-17 21:44:22 +02:00
# usage: is_cpu_vulnerable CVE-xxxx-yyyy && do something if vulnerable
2018-01-18 21:16:14 +01:00
if [ " $is_cpu_vulnerable_cached " = 1 ] ; then
_is_cpu_vulnerable_cached " $1 "
return $?
fi
2018-01-18 18:50:19 +01:00
variant1 = ''
variant2 = ''
variant3 = ''
2018-05-21 22:01:27 +02:00
variant3a = ''
variant4 = ''
2018-08-15 11:59:23 +02:00
variantl1tf = ''
2018-01-13 13:35:13 +01:00
2018-01-31 14:21:29 +01:00
if is_cpu_specex_free; then
variant1 = immune
variant2 = immune
variant3 = immune
2018-05-21 22:01:27 +02:00
variant3a = immune
variant4 = immune
2018-08-15 11:59:23 +02:00
variantl1tf = immune
2018-04-08 13:02:43 +02:00
elif is_intel; then
2018-01-14 15:10:39 +01:00
# Intel
2018-01-29 11:18:15 +01:00
# https://github.com/crozone/SpectrePoC/issues/1 ^F E5200 => spectre 2 not vulnerable
# https://github.com/paboldin/meltdown-exploit/issues/19 ^F E5200 => meltdown vulnerable
# model name : Pentium(R) Dual-Core CPU E5200 @ 2.50GHz
2018-03-25 12:23:46 +02:00
if grep -qE '^model name.+ Pentium\(R\) Dual-Core[[:space:]]+CPU[[:space:]]+E[0-9]{4}K? ' " $procfs /cpuinfo " ; then
2018-01-29 11:18:15 +01:00
variant1 = vuln
[ -z " $variant2 " ] && variant2 = immune
variant3 = vuln
2018-01-14 15:10:39 +01:00
fi
2018-01-24 14:44:16 +01:00
if [ " $capabilities_rdcl_no " = 1 ] ; then
# capability bit for future Intel processor that will explicitly state
# that they're not vulnerable to Meltdown
# this var is set in check_cpu()
2018-08-15 11:59:23 +02:00
[ -z " $variant3 " ] && variant3 = immune
[ -z " $variantl1tf " ] && variantl1tf = immune
_debug "is_cpu_vulnerable: RDCL_NO is set so not vuln to meltdown nor l1tf"
2018-01-24 14:44:16 +01:00
fi
2018-05-22 09:18:30 +02:00
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()
2018-08-15 11:59:23 +02:00
[ -z " $variant4 " ] && variant4 = immune
2018-05-22 09:18:30 +02:00
_debug "is_cpu_vulnerable: SSB_NO is set so not vuln to variant4"
fi
2018-05-27 15:14:29 +02:00
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
2018-08-15 11:59:23 +02:00
# 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
2018-12-10 19:33:07 +01:00
if [ " $cpu_model " = " $INTEL_FAM6_ATOM_SALTWELL " ] || \
[ " $cpu_model " = " $INTEL_FAM6_ATOM_SALTWELL_TABLET " ] || \
[ " $cpu_model " = " $INTEL_FAM6_ATOM_SALTWELL_MID " ] || \
[ " $cpu_model " = " $INTEL_FAM6_ATOM_BONNELL " ] || \
[ " $cpu_model " = " $INTEL_FAM6_ATOM_BONNELL_MID " ] || \
[ " $cpu_model " = " $INTEL_FAM6_ATOM_SILVERMONT " ] || \
[ " $cpu_model " = " $INTEL_FAM6_ATOM_SILVERMONT_MID " ] || \
[ " $cpu_model " = " $INTEL_FAM6_ATOM_SILVERMONT_X " ] || \
2018-08-15 11:59:23 +02:00
[ " $cpu_model " = " $INTEL_FAM6_ATOM_AIRMONT " ] || \
2018-12-10 19:33:07 +01:00
[ " $cpu_model " = " $INTEL_FAM6_ATOM_AIRMONT_MID " ] || \
[ " $cpu_model " = " $INTEL_FAM6_ATOM_GOLDMONT " ] || \
[ " $cpu_model " = " $INTEL_FAM6_ATOM_GOLDMONT_X " ] || \
[ " $cpu_model " = " $INTEL_FAM6_ATOM_GOLDMONT_PLUS " ] || \
2018-08-15 11:59:23 +02:00
[ " $cpu_model " = " $INTEL_FAM6_XEON_PHI_KNL " ] || \
2018-12-10 19:33:07 +01:00
[ " $cpu_model " = " $INTEL_FAM6_XEON_PHI_KNM " ] ; then
2018-08-15 11:59:23 +02:00
_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
2019-01-20 19:32:36 +01:00
elif is_amd || is_hygon; then
2018-01-13 13:35:13 +01:00
# AMD revised their statement about variant2 => vulnerable
# https://www.amd.com/en/corporate/speculative-execution
2018-01-18 18:50:19 +01:00
variant1 = vuln
variant2 = vuln
2018-05-23 20:38:43 +02:00
[ -z " $variant3 " ] && variant3 = immune
# https://www.amd.com/en/corporate/security-updates
# "We have not identified any AMD x86 products susceptible to the Variant 3a vulnerability in our analysis to-date."
[ -z " $variant3a " ] && variant3a = immune
2018-05-27 15:14:29 +02:00
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
2018-08-15 11:59:23 +02:00
variantl1tf = immune
2018-07-22 19:06:19 +02:00
elif [ " $cpu_vendor " = CAVIUM ] ; then
variant3 = immune
variant3a = immune
2018-08-15 11:59:23 +02:00
variantl1tf = immune
2018-01-31 13:00:58 +01:00
elif [ " $cpu_vendor " = ARM ] ; then
2018-01-09 13:11:48 +01:00
# ARM
# reference: https://developer.arm.com/support/security-update
2018-01-18 18:50:19 +01:00
# some devices (phones or other) have several ARMs and as such different part numbers,
# an example is "bigLITTLE". we shouldn't rely on the first CPU only, so we check the whole list
i = 0
2018-01-31 13:00:58 +01:00
for cpupart in $cpu_part_list
2018-01-18 18:50:19 +01:00
do
i = $(( i + 1 ))
2018-02-02 11:00:23 +01:00
# do NOT quote $cpu_arch_list below
# shellcheck disable=SC2086
cpuarch = $( echo $cpu_arch_list | awk '{ print $' $i ' }' )
2018-01-18 18:50:19 +01:00
_debug " checking cpu $i : < $cpupart > < $cpuarch > "
# some kernels report AArch64 instead of 8
[ " $cpuarch " = "AArch64" ] && cpuarch = 8
2018-01-29 10:29:48 +01:00
if [ -n " $cpupart " ] && [ -n " $cpuarch " ] ; then
2018-01-18 18:50:19 +01:00
# Cortex-R7 and Cortex-R8 are real-time and only used in medical devices or such
# I can't find their CPU part number, but it's probably not that useful anyway
2018-06-21 23:24:18 +02:00
# model R7 R8 A8 A9 A12 A15 A17 A57 A72 A73 A75 A76
# part ? ? c08 c09 c0d c0f c0e d07 d08 d09 d0a d0b?
# arch 7? 7? 7 7 7 7 7 8 8 8 8 8
2018-01-18 18:50:19 +01:00
#
2018-06-16 12:14:39 +02:00
# Whitelist identified non-vulnerable processors, use vulnerability information from
# https://developer.arm.com/support/arm-security-updates/speculative-processor-vulnerability
#
# Maintain cumulative check of vulnerabilities -
# if at least one of the cpu is vulnerable, then the system is vulnerable
2018-06-21 23:24:18 +02:00
if [ " $cpuarch " = 7 ] && echo " $cpupart " | grep -q -w -e 0xc08 -e 0xc09 -e 0xc0d -e 0xc0e; then
2018-01-18 18:50:19 +01:00
variant1 = vuln
variant2 = vuln
2018-06-16 12:14:39 +02:00
[ -z " $variant3 " ] && variant3 = immune
[ -z " $variant3a " ] && variant3a = immune
[ -z " $variant4 " ] && variant4 = immune
2018-06-21 23:24:18 +02:00
_debug " checking cpu $i : armv7 A8/A9/A12/A17 non vulnerable to variants 3, 3a & 4 "
elif [ " $cpuarch " = 7 ] && echo " $cpupart " | grep -q -w -e 0xc0f; then
2018-01-18 18:50:19 +01:00
variant1 = vuln
variant2 = vuln
2018-06-16 12:14:39 +02:00
[ -z " $variant3 " ] && variant3 = immune
2018-06-21 23:24:18 +02:00
variant3a = vuln
2018-06-16 12:14:39 +02:00
[ -z " $variant4 " ] && variant4 = immune
2018-08-01 20:40:20 +02:00
_debug " checking cpu $i : armv7 A15 non vulnerable to variants 3 & 4 "
2018-06-21 23:24:18 +02:00
elif [ " $cpuarch " = 8 ] && echo " $cpupart " | grep -q -w -e 0xd07 -e 0xd08; then
2018-06-16 12:14:39 +02:00
variant1 = vuln
variant2 = vuln
2018-06-21 23:24:18 +02:00
[ -z " $variant3 " ] && variant3 = immune
variant3a = vuln
variant4 = vuln
_debug " checking cpu $i : armv8 A57/A72 non vulnerable to variants 3 "
elif [ " $cpuarch " = 8 ] && echo " $cpupart " | grep -q -w -e 0xd09; then
2018-06-16 12:14:39 +02:00
variant1 = vuln
variant2 = vuln
2018-01-18 18:50:19 +01:00
[ -z " $variant3 " ] && variant3 = immune
2018-05-22 09:19:29 +02:00
[ -z " $variant3a " ] && variant3a = immune
variant4 = vuln
2018-06-21 23:24:18 +02:00
_debug " checking cpu $i : armv8 A73 non vulnerable to variants 3 & 3a "
elif [ " $cpuarch " = 8 ] && echo " $cpupart " | grep -q -w -e 0xd0a; then
2018-06-16 12:14:39 +02:00
variant1 = vuln
variant2 = vuln
variant3 = vuln
[ -z " $variant3a " ] && variant3a = immune
variant4 = vuln
2018-06-21 23:24:18 +02:00
_debug " checking cpu $i : armv8 A75 non vulnerable to variant 3a "
elif [ " $cpuarch " = 8 ] && echo " $cpupart " | grep -q -w -e 0xd0b; then
variant1 = vuln
[ -z " $variant2 " ] && variant2 = immune
[ -z " $variant3 " ] && variant3 = immune
[ -z " $variant3a " ] && variant3a = immune
variant4 = vuln
_debug " checking cpu $i : armv8 A76 non vulnerable to variant 2, 3 & 3a "
2018-08-01 00:31:31 +02:00
elif [ " $cpuarch " -le 7 ] || ( [ " $cpuarch " = 8 ] && [ $(( cpupart )) -lt $(( 0 xd07 )) ] ) ; then
2018-06-21 23:24:18 +02:00
[ -z " $variant1 " ] && variant1 = immune
[ -z " $variant2 " ] && variant2 = immune
[ -z " $variant3 " ] && variant3 = immune
[ -z " $variant3a " ] && variant3a = immune
[ -z " $variant4 " ] && variant4 = immune
2018-08-01 20:40:20 +02:00
_debug " checking cpu $i : arm arch $cpuarch , all immune (v7 or v8 and model < 0xd07) "
2018-05-22 09:19:29 +02:00
else
2018-06-16 12:14:39 +02:00
variant1 = vuln
variant2 = vuln
variant3 = vuln
variant3a = vuln
variant4 = vuln
2018-06-21 23:24:18 +02:00
_debug " checking cpu $i : arm unknown arch $cpuarch part $cpupart , considering vuln "
2018-05-21 22:01:27 +02:00
fi
2018-01-09 13:11:48 +01:00
fi
2018-05-21 22:01:27 +02:00
_debug " is_cpu_vulnerable: for cpu $i and so far, we have < $variant1 > < $variant2 > < $variant3 > < $variant3a > < $variant4 > "
2018-01-18 18:50:19 +01:00
done
2018-08-15 11:59:23 +02:00
variantl1tf = immune
fi
_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
2018-09-29 11:35:10 +02:00
variantl1tf_sgx = " $variantl1tf "
2018-09-17 21:44:22 +02:00
# even if we are vulnerable to L1TF, if there's no SGX, we're safe for the original foreshadow
[ " $cpuid_sgx " = 0 ] && variantl1tf_sgx = 1
_debug " is_cpu_vulnerable: final results are < $variant1 > < $variant2 > < $variant3 > < $variant3a > < $variant4 > < $variantl1tf > < $variantl1tf_sgx > "
2018-01-18 21:16:14 +01:00
is_cpu_vulnerable_cached = 1
_is_cpu_vulnerable_cached " $1 "
return $?
2018-01-09 13:11:48 +01:00
}
2018-01-31 14:21:29 +01:00
is_cpu_specex_free( )
{
# return true (0) if the CPU doesn't do speculative execution, false (1) if it does.
# if it's not in the list we know, return false (1).
# source: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/kernel/cpu/common.c#n882
2018-12-10 19:33:07 +01:00
# { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SALTWELL, X86_FEATURE_ANY },
# { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SALTWELL_TABLET, X86_FEATURE_ANY },
# { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_BONNELL_MID, X86_FEATURE_ANY },
# { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SALTWELL_MID, X86_FEATURE_ANY },
# { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_BONNELL, X86_FEATURE_ANY },
2018-01-31 14:21:29 +01:00
# { X86_VENDOR_CENTAUR, 5 },
# { X86_VENDOR_INTEL, 5 },
# { X86_VENDOR_NSC, 5 },
# { X86_VENDOR_ANY, 4 },
2018-12-10 19:33:07 +01:00
2018-02-02 11:10:36 +01:00
parse_cpu_details
2018-04-08 13:02:43 +02:00
if is_intel; then
2018-01-31 14:21:29 +01:00
if [ " $cpu_family " = 6 ] ; then
2018-12-10 19:33:07 +01:00
if [ " $cpu_model " = " $INTEL_FAM6_ATOM_SALTWELL " ] || \
[ " $cpu_model " = " $INTEL_FAM6_ATOM_SALTWELL_TABLET " ] || \
[ " $cpu_model " = " $INTEL_FAM6_ATOM_BONNELL_MID " ] || \
[ " $cpu_model " = " $INTEL_FAM6_ATOM_SALTWELL_MID " ] || \
[ " $cpu_model " = " $INTEL_FAM6_ATOM_BONNELL " ] ; then
2018-01-31 14:21:29 +01:00
return 0
fi
elif [ " $cpu_family " = 5 ] ; then
return 0
fi
fi
2018-03-10 16:02:44 +01:00
[ " $cpu_family " = 4 ] && return 0
2018-01-31 14:21:29 +01:00
return 1
}
2018-05-27 15:14:29 +02:00
is_cpu_ssb_free( )
{
# return true (0) if the CPU isn't affected by speculative store bypass, false (1) if it does.
# if it's not in the list we know, return false (1).
# source1: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/kernel/cpu/common.c#n945
# source2: https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git/tree/arch/x86/kernel/cpu/common.c
# Only list CPUs that speculate but are immune, to avoid duplication of cpus listed in is_cpu_specex_free()
2018-12-10 19:33:07 +01:00
#{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SILVERMONT },
2018-05-27 15:14:29 +02:00
#{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_AIRMONT },
2018-12-10 19:33:07 +01:00
#{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SILVERMONT_X },
#{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SILVERMONT_MID },
2018-05-27 15:14:29 +02:00
#{ X86_VENDOR_INTEL, 6, INTEL_FAM6_CORE_YONAH },
#{ X86_VENDOR_INTEL, 6, INTEL_FAM6_XEON_PHI_KNL },
#{ X86_VENDOR_INTEL, 6, INTEL_FAM6_XEON_PHI_KNM },
#{ X86_VENDOR_AMD, 0x12, },
#{ X86_VENDOR_AMD, 0x11, },
#{ X86_VENDOR_AMD, 0x10, },
#{ X86_VENDOR_AMD, 0xf, },
parse_cpu_details
if is_intel; then
if [ " $cpu_family " = 6 ] ; then
if [ " $cpu_model " = " $INTEL_FAM6_ATOM_AIRMONT " ] || \
2018-12-10 19:33:07 +01:00
[ " $cpu_model " = " $INTEL_FAM6_ATOM_SILVERMONT " ] || \
[ " $cpu_model " = " $INTEL_FAM6_ATOM_SILVERMONT_X " ] || \
[ " $cpu_model " = " $INTEL_FAM6_ATOM_SILVERMONT_MID " ] ; then
2018-05-27 15:14:29 +02:00
return 0
elif [ " $cpu_model " = " $INTEL_FAM6_CORE_YONAH " ] || \
[ " $cpu_model " = " $INTEL_FAM6_XEON_PHI_KNL " ] || \
[ " $cpu_model " = " $INTEL_FAM6_XEON_PHI_KNM " ] ; then
return 0
fi
fi
fi
if is_amd; then
if [ " $cpu_family " = "18" ] || \
[ " $cpu_family " = "17" ] || \
[ " $cpu_family " = "16" ] || \
[ " $cpu_family " = "15" ] ; then
return 0
fi
2019-01-20 19:32:36 +01:00
fi
if is_hygon; then
return 1
fi
2018-05-27 15:14:29 +02:00
[ " $cpu_family " = 4 ] && return 0
return 1
}
2018-01-09 13:37:26 +01:00
show_header( )
{
2018-02-12 18:13:44 +01:00
_info " Spectre and Meltdown mitigation detection tool v $VERSION "
2018-01-09 13:37:26 +01:00
_info
}
2018-01-08 11:31:19 +01:00
2018-09-30 16:56:58 +02:00
[ -z " $HOME " ] && HOME = " $( getent passwd " $( whoami) " | cut -d: -f6) "
mcedb_cache = " $HOME /.mcedb "
2018-09-23 17:29:14 +02:00
update_mcedb( )
{
# We're using MCE.db from the excellent platomav's MCExtractor project
show_header
if [ -r " $mcedb_cache " ] ; then
previous_mcedb_revision = $( awk '/^# %%% MCEDB / { print $4 }' " $mcedb_cache " )
fi
# first download the database
mcedb_tmp = " $( mktemp /tmp/mcedb-XXXXXX) "
mcedb_url = 'https://github.com/platomav/MCExtractor/raw/master/MCE.db'
_info_nol "Fetching MCE.db from the MCExtractor project... "
if which wget >/dev/null 2>& 1; then
wget -q " $mcedb_url " -O " $mcedb_tmp " ; ret = $?
elif which curl >/dev/null 2>& 1; then
curl -sL " $mcedb_url " -o " $mcedb_tmp " ; ret = $?
elif which fetch >/dev/null 2>& 1; then
fetch -q " $mcedb_url " -o " $mcedb_tmp " ; ret = $?
else
echo ERROR "please install one of \`wget\`, \`curl\` of \`fetch\` programs"
return 1
fi
if [ " $ret " != 0 ] ; then
echo ERROR " error $ret while downloading MCE.db "
return $ret
fi
echo DONE
# now extract contents using sqlite
_info_nol "Extracting data... "
if ! which sqlite3 >/dev/null 2>& 1; then
echo ERROR "please install the \`sqlite3\` program"
return 1
fi
mcedb_revision = $( sqlite3 " $mcedb_tmp " "select revision from MCE" )
mcedb_date = $( sqlite3 " $mcedb_tmp " "select strftime('%Y/%m/%d', date, 'unixepoch') from MCE" )
if [ -z " $mcedb_revision " ] ; then
echo ERROR "downloaded file seems invalid"
return 1
fi
echo OK " MCExtractor database revision $mcedb_revision dated $mcedb_date "
if [ -n " $previous_mcedb_revision " ] ; then
if [ " $previous_mcedb_revision " = " v $mcedb_revision " ] ; then
echo "We already have this version locally, no update needed"
2018-09-30 16:56:58 +02:00
[ " $1 " != builtin ] && return 0
2018-09-23 17:29:14 +02:00
fi
fi
echo "# Spectre & Meltdown Checker" > " $mcedb_cache "
echo " # %%% MCEDB v $mcedb_revision - $mcedb_date " >> " $mcedb_cache "
2018-09-30 16:56:58 +02:00
sqlite3 " $mcedb_tmp " "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" | grep -v '^# .,0x00000000,' >> " $mcedb_cache "
2018-09-23 17:29:14 +02:00