From 7e4899bcb8b862b4d9254a1bdedc9b78627f169f Mon Sep 17 00:00:00 2001 From: Rob Gill Date: Thu, 17 May 2018 23:39:48 +1000 Subject: [PATCH 1/4] ibrs can't be enabled on no ibrs cpu (#195) * ibrs can't be enabled on no ibrs cpu If the cpu is identified, and does not support SPEC_CTRL or IBRS, then ibrs can't be enabled, even if supported by the kernel. Instead of reporting IBRS enabled and active UNKNOWN, report IBRS enabled and active NO. --- spectre-meltdown-checker.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/spectre-meltdown-checker.sh b/spectre-meltdown-checker.sh index efa4812..6cfb1b8 100755 --- a/spectre-meltdown-checker.sh +++ b/spectre-meltdown-checker.sh @@ -2137,7 +2137,10 @@ check_variant2_linux() 1) if [ "$ibrs_fw_enabled" = 1 ]; then pstatus green YES "for kernel space and firmware code"; else pstatus green YES "for kernel space"; fi;; 2) if [ "$ibrs_fw_enabled" = 1 ]; then pstatus green YES "for kernel, user space, and firmware code" ; else pstatus green YES "for both kernel and user space"; fi;; 3) if [ "$ibrs_fw_enabled" = 1 ]; then pstatus green YES "for kernel and firmware code"; else pstatus green YES; fi;; - *) pstatus yellow UNKNOWN;; + *) if [ ! "$cpuid_ibrs" = 'SPEC_CTRL' ] && [ ! "cpuid_ibrs" = 'IBRS_SUPPORT' ] && [ ! "cpuid_spec_ctrl" = -1 ]; + then pstatus yellow NO; _debug "ibrs: known cpu not supporting SPEC-CTRL or IBRS"; + else + pstatus yellow UNKNOWN; fi;; esac fi else From 6a4318addf102fac2fd271d3f1e2330074177a4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lesimple?= Date: Mon, 21 May 2018 22:01:27 +0200 Subject: [PATCH 2/4] feat(variant3a/4): initial support for 2 new CVEs --- README.md | 25 +++++++- spectre-meltdown-checker.sh | 121 ++++++++++++++++++++++++++++++------ 2 files changed, 125 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 4a9c718..e139f18 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,16 @@ Spectre & Meltdown Checker ========================== -A shell script to tell if your system is vulnerable against the 3 "speculative execution" CVEs that were made public early 2018. +A shell script to tell if your system is vulnerable against the several "speculative execution" CVEs that were made public in 2018. + +This includes: +- 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 + +**Note: as CVE-2018-3639 and CVE-2018-3640 are extremely recent (published on May 21th 2018), expect frequent changes of the script in the next days to adjust detection.** Supported operating systems: - Linux (all versions, flavors and distros) @@ -74,7 +83,19 @@ sudo ./spectre-meltdown-checker.sh - Mitigation: updated kernel (with PTI/KPTI patches), updating the kernel is enough - Performance impact of the mitigation: low to medium -## Disclaimer +**CVE-2018-3640** rogue system register read (Variant 3a) + + - Impact: TBC + - Mitigation: TBC + - Performance impact of the mitigation: negligible + +**CVE-2018-3639** speculative store bypass (Variant 4) + + - Impact: software using JIT (no known exploitation against kernel) + - Mitigation: TBC + - Performance impact of the mitigation: low to medium + +## 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. 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). diff --git a/spectre-meltdown-checker.sh b/spectre-meltdown-checker.sh index 6cfb1b8..0314269 100755 --- a/spectre-meltdown-checker.sh +++ b/spectre-meltdown-checker.sh @@ -243,18 +243,22 @@ is_cpu_vulnerable_cached=0 _is_cpu_vulnerable_cached() { # shellcheck disable=SC2086 - [ "$1" = 1 ] && return $variant1 + [ "$1" = 1 ] && return $variant1 # shellcheck disable=SC2086 - [ "$1" = 2 ] && return $variant2 + [ "$1" = 2 ] && return $variant2 # shellcheck disable=SC2086 - [ "$1" = 3 ] && return $variant3 + [ "$1" = 3 ] && return $variant3 + # shellcheck disable=SC2086 + [ "$1" = 3a ] && return $variant3a + # shellcheck disable=SC2086 + [ "$1" = 4 ] && return $variant4 echo "$0: error: invalid variant '$1' passed to is_cpu_vulnerable()" >&2 exit 255 } is_cpu_vulnerable() { - # param: 1, 2 or 3 (variant) + # param: 1, 2, 3, 3a or 4 (variant) # returns 0 if vulnerable, 1 if not vulnerable # (note that in shell, a return of 0 is success) # by default, everything is vulnerable, we work in a "whitelist" logic here. @@ -267,11 +271,15 @@ is_cpu_vulnerable() variant1='' variant2='' variant3='' + variant3a='' + variant4='' if is_cpu_specex_free; then variant1=immune variant2=immune variant3=immune + variant3a=immune + variant4=immune elif is_intel; then # Intel # https://github.com/crozone/SpectrePoC/issues/1 ^F E5200 => spectre 2 not vulnerable @@ -343,16 +351,29 @@ is_cpu_vulnerable() _debug "checking cpu$i: this arm non vulnerable to meltdown" [ -z "$variant3" ] && variant3=immune fi + + # for variant3a, only A15/A57/A72 are vulnerable + if [ "$cpuarch" = 8 ] && echo "$cpupart" | grep -Eq '^0x(c0f|d07|d0a)$'; then + _debug "checking cpu$i: arm A15-A57-A72 vulnerable to variant3a" + variant3a=vuln + else + _debug "checking cpu$i: this arm non vulnerable to variant3a" + [ -z "$variant3" ] && variant3a=immune + fi fi - _debug "is_cpu_vulnerable: for cpu$i and so far, we have <$variant1> <$variant2> <$variant3>" + _debug "is_cpu_vulnerable: for cpu$i and so far, we have <$variant1> <$variant2> <$variant3> <$variant3a> <$variant4>" done fi - _debug "is_cpu_vulnerable: temp results are <$variant1> <$variant2> <$variant3>" + # from the information we have for now, it seems that CPUs that are vulnerable to variant1 are also vulnerable to variant4 + variant4=$variant1 + _debug "is_cpu_vulnerable: temp results are <$variant1> <$variant2> <$variant3> <$variant3a> <$variant4>" # if at least one of the cpu is vulnerable, then the system is vulnerable - [ "$variant1" = "immune" ] && variant1=1 || variant1=0 - [ "$variant2" = "immune" ] && variant2=1 || variant2=0 - [ "$variant3" = "immune" ] && variant3=1 || variant3=0 - _debug "is_cpu_vulnerable: final results are <$variant1> <$variant2> <$variant3>" + [ "$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>" is_cpu_vulnerable_cached=1 _is_cpu_vulnerable_cached "$1" return $? @@ -496,11 +517,13 @@ while [ -n "$1" ]; do 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;; + 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;; *) - echo "$0: error: invalid parameter '$2' for --variant, expected either 1, 2 or 3" >&2; + echo "$0: error: invalid parameter '$2' for --variant, expected either 1, 2, 3, 3a or 4" >&2; exit 255 ;; esac @@ -567,6 +590,8 @@ pvulnstatus() CVE-2017-5753) aka="SPECTRE VARIANT 1";; CVE-2017-5715) aka="SPECTRE VARIANT 2";; CVE-2017-5754) aka="MELTDOWN";; + CVE-2018-3640) aka="VARIANT 3A";; + CVE-2018-3639) aka="VARIANT 4";; esac case "$opt_batch_format" in @@ -888,6 +913,7 @@ parse_cpu_details() # 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 cpuid="$read_cpuid_value" + #cpuid_hex=$(printf "%X" "$cpuid") fi # under BSD, linprocfs often doesn't export ucode information, so fetch it ourselves the good old way @@ -1411,7 +1437,6 @@ read_msr() return 0 } - check_cpu() { _info "\033[1;34mHardware check\033[0m" @@ -1627,6 +1652,19 @@ check_cpu() fi fi + # variant 4 + _info " * Speculative Store Bypass Disable (SSBD)" + _info_nol " * CPU indicates SSBD capability: " + read_cpuid 0x7 $EDX 31 1 1; ret=$? + if [ $ret -eq 0 ]; then + #cpuid_ng1=1 + pstatus green YES "SSBD feature bit" + elif [ $ret -eq 1 ]; then + pstatus yellow NO + else + pstatus yellow UNKNOWN "is cpuid kernel module available?" + fi + if is_intel; then _info " * Enhanced IBRS (IBRS_ALL)" _info_nol " * CPU indicates ARCH_CAPABILITIES MSR availability: " @@ -1680,11 +1718,13 @@ check_cpu() capabilities=$val_cap_msr capabilities_rdcl_no=0 capabilities_ibrs_all=0 + capabilities_ssbd_no=0 if [ $val -eq 0 ]; then _debug "capabilities MSR lower byte is $capabilities (decimal)" [ $(( capabilities & 1 )) -eq 1 ] && capabilities_rdcl_no=1 [ $(( capabilities & 2 )) -eq 2 ] && capabilities_ibrs_all=1 - _debug "capabilities says rdcl_no=$capabilities_rdcl_no ibrs_all=$capabilities_ibrs_all" + [ $(( capabilities & 4 )) -eq 4 ] && capabilities_ssbd_no=1 + _debug "capabilities says rdcl_no=$capabilities_rdcl_no ibrs_all=$capabilities_ibrs_all ssbd_no=$capabilities_ssbd_no" if [ "$capabilities_ibrs_all" = 1 ]; then if [ $cpu_mismatch -eq 0 ]; then pstatus green YES @@ -1709,6 +1749,15 @@ check_cpu() else pstatus yellow NO fi + + _info_nol " * CPU explicitly indicates not being vulnerable to Variant 4 (SSBD_NO): " + if [ "$capabilities_ssbd_no" = -1 ]; then + pstatus yellow UNKNOWN + elif [ "$capabilities_ssbd_no" = 1 ]; then + pstatus green YES + else + pstatus yellow NO + fi fi _info_nol " * CPU microcode is known to cause stability problems: " @@ -1727,8 +1776,8 @@ check_cpu() check_cpu_vulnerabilities() { - _info "* CPU vulnerability to the three speculative execution attack variants" - for v in 1 2 3; do + _info "* CPU vulnerability to the speculative execution attack variants" + for v in 1 2 3 3a 4; do _info_nol " * Vulnerable to Variant $v: " if is_cpu_vulnerable $v; then pstatus yellow YES @@ -2137,7 +2186,7 @@ check_variant2_linux() 1) if [ "$ibrs_fw_enabled" = 1 ]; then pstatus green YES "for kernel space and firmware code"; else pstatus green YES "for kernel space"; fi;; 2) if [ "$ibrs_fw_enabled" = 1 ]; then pstatus green YES "for kernel, user space, and firmware code" ; else pstatus green YES "for both kernel and user space"; fi;; 3) if [ "$ibrs_fw_enabled" = 1 ]; then pstatus green YES "for kernel and firmware code"; else pstatus green YES; fi;; - *) if [ ! "$cpuid_ibrs" = 'SPEC_CTRL' ] && [ ! "cpuid_ibrs" = 'IBRS_SUPPORT' ] && [ ! "cpuid_spec_ctrl" = -1 ]; + *) if [ "$cpuid_ibrs" != 'SPEC_CTRL' ] && [ "$cpuid_ibrs" != 'IBRS_SUPPORT' ] && [ "$cpuid_spec_ctrl" != -1 ]; then pstatus yellow NO; _debug "ibrs: known cpu not supporting SPEC-CTRL or IBRS"; else pstatus yellow UNKNOWN; fi;; @@ -2817,6 +2866,32 @@ check_variant3_bsd() fi } +check_variant3a() +{ + _info "\033[1;34mCVE-2018-3640 [rogue system register read] aka 'Variant 3a'\033[0m" + + cve='CVE-2018-3640' + if ! is_cpu_vulnerable 3a; 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" + else + pvulnstatus $cve UNK "new vulnerability, script will when more technical information is available in the next hours/days" + fi +} + +check_variant4() +{ + _info "\033[1;34mCVE-2018-3639 [speculative store bypass] aka 'Variant 4'\033[0m" + + cve='CVE-2018-3639' + if ! is_cpu_vulnerable 4; 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" + else + pvulnstatus $cve UNK "new vulnerability, script will when more technical information is available in the next hours/days" + fi +} + if [ "$opt_no_hw" = 0 ] && [ -z "$opt_arch_prefix" ]; then check_cpu check_cpu_vulnerabilities @@ -2836,6 +2911,14 @@ if [ "$opt_variant3" = 1 ] || [ "$opt_allvariants" = 1 ]; then check_variant3 _info fi +if [ "$opt_variant3a" = 1 ] || [ "$opt_allvariants" = 1 ]; then + check_variant3a + _info +fi +if [ "$opt_variant4" = 1 ] || [ "$opt_allvariants" = 1 ]; then + check_variant4 + _info +fi _vars=$(set | grep -Ev '^[A-Z_[:space:]]' | sort | tr "\n" '|') _debug "variables at end of script: $_vars" From 3062a8416a2bfe18b0a37edcc8c5b7436c098501 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lesimple?= Date: Tue, 22 May 2018 00:10:08 +0200 Subject: [PATCH 3/4] fix(msg): add missing words --- spectre-meltdown-checker.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spectre-meltdown-checker.sh b/spectre-meltdown-checker.sh index 0314269..38ab895 100755 --- a/spectre-meltdown-checker.sh +++ b/spectre-meltdown-checker.sh @@ -2875,7 +2875,7 @@ check_variant3a() # override status & msg in case CPU is not vulnerable after all pvulnstatus $cve OK "your CPU vendor reported your CPU model as not vulnerable" else - pvulnstatus $cve UNK "new vulnerability, script will when more technical information is available in the next hours/days" + pvulnstatus $cve UNK "new vulnerability, script will be updated when more technical information is available in the next hours/days" fi } @@ -2888,7 +2888,7 @@ check_variant4() # override status & msg in case CPU is not vulnerable after all pvulnstatus $cve OK "your CPU vendor reported your CPU model as not vulnerable" else - pvulnstatus $cve UNK "new vulnerability, script will when more technical information is available in the next hours/days" + pvulnstatus $cve UNK "new vulnerability, script will be updated when more technical information is available in the next hours/days" fi } From 22d0b203dac650c5344d57ad647c3ca4e465856d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lesimple?= Date: Tue, 22 May 2018 00:38:31 +0200 Subject: [PATCH 4/4] fix(ssb_no): rename ssbd_no to ssb_no and fix shift --- spectre-meltdown-checker.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/spectre-meltdown-checker.sh b/spectre-meltdown-checker.sh index 38ab895..062eec8 100755 --- a/spectre-meltdown-checker.sh +++ b/spectre-meltdown-checker.sh @@ -1718,13 +1718,13 @@ check_cpu() capabilities=$val_cap_msr capabilities_rdcl_no=0 capabilities_ibrs_all=0 - capabilities_ssbd_no=0 + capabilities_ssb_no=0 if [ $val -eq 0 ]; then _debug "capabilities MSR lower byte is $capabilities (decimal)" - [ $(( capabilities & 1 )) -eq 1 ] && capabilities_rdcl_no=1 - [ $(( capabilities & 2 )) -eq 2 ] && capabilities_ibrs_all=1 - [ $(( capabilities & 4 )) -eq 4 ] && capabilities_ssbd_no=1 - _debug "capabilities says rdcl_no=$capabilities_rdcl_no ibrs_all=$capabilities_ibrs_all ssbd_no=$capabilities_ssbd_no" + [ $(( capabilities & 1 )) -eq 1 ] && capabilities_rdcl_no=1 + [ $(( capabilities & 2 )) -eq 2 ] && capabilities_ibrs_all=1 + [ $(( capabilities & 16 )) -eq 16 ] && capabilities_ssb_no=1 + _debug "capabilities says rdcl_no=$capabilities_rdcl_no ibrs_all=$capabilities_ibrs_all ssb_no=$capabilities_ssb_no" if [ "$capabilities_ibrs_all" = 1 ]; then if [ $cpu_mismatch -eq 0 ]; then pstatus green YES @@ -1750,10 +1750,10 @@ check_cpu() pstatus yellow NO fi - _info_nol " * CPU explicitly indicates not being vulnerable to Variant 4 (SSBD_NO): " - if [ "$capabilities_ssbd_no" = -1 ]; then + _info_nol " * CPU explicitly indicates not being vulnerable to Variant 4 (SSB_NO): " + if [ "$capabilities_ssb_no" = -1 ]; then pstatus yellow UNKNOWN - elif [ "$capabilities_ssbd_no" = 1 ]; then + elif [ "$capabilities_ssb_no" = 1 ]; then pstatus green YES else pstatus yellow NO