diff -Nru netext73-1.3.62/changelog netext73-1.3.63/changelog --- netext73-1.3.62/changelog 2019-06-04 06:11:30.000000000 +0000 +++ netext73-1.3.63/changelog 2019-06-06 09:05:37.000000000 +0000 @@ -1,5 +1,7 @@ NeteXt'73 Changelog +v1.3.63 +- nowy spectre-meltdown-checker v1.3.62 - zależności: libisl21 v1.3.61 diff -Nru netext73-1.3.62/debian/bzr-builder.manifest netext73-1.3.63/debian/bzr-builder.manifest --- netext73-1.3.62/debian/bzr-builder.manifest 2019-06-04 06:11:31.000000000 +0000 +++ netext73-1.3.63/debian/bzr-builder.manifest 2019-06-06 09:05:38.000000000 +0000 @@ -1,2 +1,2 @@ -# bzr-builder format 0.3 deb-version {debupstream}-N~227 -lp:netext73 revid:netbit73@gmail.com-20190604060909-nnwv9fed9ecsvnw1 +# bzr-builder format 0.3 deb-version {debupstream}-N~228 +lp:netext73 revid:netbit73@gmail.com-20190606085900-lko2pe4z1dy1ws3c diff -Nru netext73-1.3.62/debian/changelog netext73-1.3.63/debian/changelog --- netext73-1.3.62/debian/changelog 2019-06-04 06:11:31.000000000 +0000 +++ netext73-1.3.63/debian/changelog 2019-06-06 09:05:38.000000000 +0000 @@ -1,8 +1,14 @@ -netext73 (1.3.62-N~227~ubuntu18.10.1) cosmic; urgency=low +netext73 (1.3.63-N~228~ubuntu18.10.1) cosmic; urgency=low * Auto build. - -- NetBit73 Tue, 04 Jun 2019 06:11:31 +0000 + -- NetBit73 Thu, 06 Jun 2019 09:05:38 +0000 + +netext73 (1.3.63) bionic; urgency=medium + + * nowy spectre-meltdown-checker + + -- NetBit73 Thu, 06 Jun 2019 10:58:36 +0200 netext73 (1.3.62) bionic; urgency=medium diff -Nru netext73-1.3.62/pakiety/spectre-meltdown-checker.sh netext73-1.3.63/pakiety/spectre-meltdown-checker.sh --- netext73-1.3.62/pakiety/spectre-meltdown-checker.sh 2019-06-04 06:11:30.000000000 +0000 +++ netext73-1.3.63/pakiety/spectre-meltdown-checker.sh 2019-06-06 09:05:37.000000000 +0000 @@ -11,7 +11,7 @@ # # Stephane Lesimple # -VERSION='0.40' +VERSION='0.42' trap 'exit_cleanup' EXIT trap '_warn "interrupted, cleaning up..."; exit_cleanup; exit 1' INT @@ -27,10 +27,11 @@ [ "$insmod_cpuid" = 1 ] && rmmod cpuid 2>/dev/null [ "$insmod_msr" = 1 ] && rmmod msr 2>/dev/null [ "$kldload_cpuctl" = 1 ] && kldunload cpuctl 2>/dev/null + [ "$kldload_vmm" = 1 ] && kldunload vmm 2>/dev/null } # if we were git clone'd, adjust VERSION -if [ -d "$(dirname "$0")/.git" ] && which git >/dev/null 2>&1; then +if [ -d "$(dirname "$0")/.git" ] && command -v 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 @@ -62,6 +63,7 @@ --explain produce an additional human-readable explanation of actions to take to mitigate a vulnerability --paranoid require IBPB to deem Variant 2 as mitigated also require SMT disabled + unconditional L1D flush to deem Foreshadow-NG VMM as mitigated + also require SMT disabled to deem MDS vulnerabilities mitigated --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] @@ -75,14 +77,16 @@ --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,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 + --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 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) @@ -147,20 +151,25 @@ opt_vmm=-1 opt_explain=0 opt_paranoid=0 +opt_mock=0 global_critical=0 global_unknown=0 nrpe_vuln='' -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' +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 CVE-2018-12126 CVE-2018-12130 CVE-2018-12127 CVE-2019-11091' # find a sane command to print colored messages, we prefer `printf` over `echo` # because `printf` behavior is more standard across Linux/BSD # we'll try to avoid using shell builtins that might not take options -echo_cmd_type=echo -if which printf >/dev/null 2>&1; then - echo_cmd=$(which printf) - echo_cmd_type=printf +echo_cmd_type='echo' +# ignore SC2230 here because `which` ignores builtins while `command -v` doesn't, and +# we don't want builtins here. Even if `which` is not installed, we'll fallback to the +# `echo` builtin anyway, so this is safe. +# shellcheck disable=SC2230 +if command -v printf >/dev/null 2>&1; then + echo_cmd=$(command -v printf) + echo_cmd_type='printf' elif which echo >/dev/null 2>&1; then echo_cmd=$(which echo) else @@ -170,7 +179,7 @@ [ -x /system/bin/echo ] && echo_cmd=/system/bin/echo fi # still empty? fallback to builtin -[ -z "$echo_cmd" ] && echo_cmd=echo +[ -z "$echo_cmd" ] && echo_cmd='echo' __echo() { opt="$1" @@ -258,14 +267,19 @@ 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";; + 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";; + CVE-2018-12126) echo "Fallout, microarchitectural store buffer data sampling (MSBDS)";; + CVE-2018-12130) echo "ZombieLoad, microarchitectural fill buffer data sampling (MFBDS)";; + CVE-2018-12127) echo "RIDL, microarchitectural load port data sampling (MLPDS)";; + CVE-2019-11091) echo "RIDL, microarchitectural data sampling uncacheable memory (MDSUM)";; + *) echo "$0: error: invalid CVE '$1' passed to cve2name()" >&2; exit 255;; esac } @@ -282,9 +296,12 @@ CVE-2018-3615) return $variantl1tf_sgx;; CVE-2018-3620) return $variantl1tf;; CVE-2018-3646) return $variantl1tf;; + CVE-2018-12126) return $variant_msbds;; + CVE-2018-12130) return $variant_mfbds;; + CVE-2018-12127) return $variant_mlpds;; + CVE-2019-11091) return $variant_mdsum;; + *) echo "$0: error: invalid variant '$1' passed to is_cpu_vulnerable()" >&2; exit 255;; esac - echo "$0: error: invalid variant '$1' passed to is_cpu_vulnerable()" >&2 - exit 255 } is_cpu_vulnerable() @@ -305,6 +322,18 @@ variant3a='' variant4='' variantl1tf='' + variant_msbds='' + variant_mfbds='' + 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 @@ -313,6 +342,10 @@ variant3a=immune variant4=immune variantl1tf=immune + variant_msbds=immune + variant_mfbds=immune + variant_mlpds=immune + variant_mdsum=immune elif is_intel; then # Intel # https://github.com/crozone/SpectrePoC/issues/1 ^F E5200 => spectre 2 not vulnerable @@ -371,7 +404,7 @@ [ -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" @@ -464,7 +497,7 @@ [ -z "$variant3a" ] && variant3a=immune variant4=vuln _debug "checking cpu$i: armv8 A76 non vulnerable to variant 2, 3 & 3a" - elif [ "$cpuarch" -le 7 ] || ( [ "$cpuarch" = 8 ] && [ $(( cpupart )) -lt $(( 0xd07 )) ] ) ; then + elif [ "$cpuarch" -le 7 ] || { [ "$cpuarch" = 8 ] && [ $(( cpupart )) -lt $(( 0xd07 )) ]; } ; then [ -z "$variant1" ] && variant1=immune [ -z "$variant2" ] && variant2=immune [ -z "$variant3" ] && variant3=immune @@ -485,12 +518,16 @@ 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 + [ "$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 + [ "$variant_msbds" = "immune" ] && variant_msbds=1 || variant_msbds=0 + [ "$variant_mfbds" = "immune" ] && variant_mfbds=1 || variant_mfbds=0 + [ "$variant_mlpds" = "immune" ] && variant_mlpds=1 || variant_mlpds=0 + [ "$variant_mdsum" = "immune" ] && variant_mdsum=1 || variant_mdsum=0 variantl1tf_sgx="$variantl1tf" # 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 @@ -533,6 +570,52 @@ return 1 } +is_cpu_mds_free() +{ + # return true (0) if the CPU isn't affected by microarchitectural data sampling, 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 + #VULNWL_INTEL(ATOM_GOLDMONT, NO_MDS | NO_L1TF), + #VULNWL_INTEL(ATOM_GOLDMONT_X, NO_MDS | NO_L1TF), + #VULNWL_INTEL(ATOM_GOLDMONT_PLUS, NO_MDS | NO_L1TF), + + #/* AMD Family 0xf - 0x12 */ + #VULNWL_AMD(0x0f, NO_MELTDOWN | NO_SSB | NO_L1TF | NO_MDS), + #VULNWL_AMD(0x10, NO_MELTDOWN | NO_SSB | NO_L1TF | NO_MDS), + #VULNWL_AMD(0x11, NO_MELTDOWN | NO_SSB | NO_L1TF | NO_MDS), + #VULNWL_AMD(0x12, NO_MELTDOWN | NO_SSB | NO_L1TF | NO_MDS), + + #/* FAMILY_ANY must be last, otherwise 0x0f - 0x12 matches won't work */ + #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 [ "$cpu_family" = 6 ]; then + 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 + [ "$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 + 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 +} + is_cpu_ssb_free() { # return true (0) if the CPU isn't affected by speculative store bypass, false (1) if it does. @@ -602,11 +685,11 @@ 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 + if command -v wget >/dev/null 2>&1; then wget -q "$mcedb_url" -O "$mcedb_tmp"; ret=$? - elif which curl >/dev/null 2>&1; then + elif command -v curl >/dev/null 2>&1; then curl -sL "$mcedb_url" -o "$mcedb_tmp"; ret=$? - elif which fetch >/dev/null 2>&1; then + elif command -v 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" @@ -620,7 +703,7 @@ # now extract contents using sqlite _info_nol "Extracting data... " - if ! which sqlite3 >/dev/null 2>&1; then + if ! command -v sqlite3 >/dev/null 2>&1; then echo ERROR "please install the \`sqlite3\` program" return 1 fi @@ -735,6 +818,9 @@ 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 @@ -755,6 +841,7 @@ 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 @@ -779,6 +866,7 @@ auto) opt_vmm=-1;; yes) opt_vmm=1;; no) opt_vmm=0;; + *) echo "$0: error: expected one of (auto, yes, no) to option --vmm instead of '$2'" >&2; exit 255;; esac shift 2 elif [ "$1" = "--variant" ]; then @@ -787,14 +875,18 @@ exit 255 fi case "$2" in - 1) opt_cve_list="$opt_cve_list CVE-2017-5753"; opt_cve_all=0;; - 2) opt_cve_list="$opt_cve_list CVE-2017-5715"; opt_cve_all=0;; - 3) opt_cve_list="$opt_cve_list CVE-2017-5754"; opt_cve_all=0;; - 3a) opt_cve_list="$opt_cve_list CVE-2018-3640"; opt_cve_all=0;; - 4) opt_cve_list="$opt_cve_list CVE-2018-3639"; opt_cve_all=0;; - l1tf) opt_cve_list="$opt_cve_list CVE-2018-3615 CVE-2018-3620 CVE-2018-3646"; opt_cve_all=0;; + 1) opt_cve_list="$opt_cve_list CVE-2017-5753"; opt_cve_all=0;; + 2) opt_cve_list="$opt_cve_list CVE-2017-5715"; opt_cve_all=0;; + 3) opt_cve_list="$opt_cve_list CVE-2017-5754"; opt_cve_all=0;; + 3a) opt_cve_list="$opt_cve_list CVE-2018-3640"; opt_cve_all=0;; + 4) opt_cve_list="$opt_cve_list CVE-2018-3639"; opt_cve_all=0;; + msbds) opt_cve_list="$opt_cve_list CVE-2018-12126"; opt_cve_all=0;; + mfbds) opt_cve_list="$opt_cve_list CVE-2018-12130"; opt_cve_all=0;; + mlpds) opt_cve_list="$opt_cve_list CVE-2018-12127"; opt_cve_all=0;; + 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 @@ -863,7 +955,14 @@ CVE-2017-5754) aka="MELTDOWN";; CVE-2018-3640) aka="VARIANT 3A";; CVE-2018-3639) aka="VARIANT 4";; - CVE-2018-3615/3620/3646) aka="L1TF";; + CVE-2018-3615) aka="L1TF SGX";; + CVE-2018-3620) aka="L1TF OS";; + CVE-2018-3646) aka="L1TF VMM";; + CVE-2018-12126) aka="MSBDS";; + CVE-2018-12130) aka="MFBDS";; + CVE-2018-12127) aka="MLPDS";; + CVE-2019-11091) aka="MDSUM";; + *) echo "$0: error: invalid CVE '$1' passed to pvulnstatus()" >&2; exit 255;; esac case "$opt_batch_format" in @@ -874,6 +973,7 @@ UNK) is_vuln="null";; VULN) is_vuln="true";; OK) is_vuln="false";; + *) echo "$0: error: unknown status '$2' passed to pvulnstatus()" >&2; exit 255;; esac json_output="${json_output:-[}{\"NAME\":\"$aka\",\"CVE\":\"$1\",\"VULNERABLE\":$is_vuln,\"INFOS\":\"$3\"}," ;; @@ -882,6 +982,7 @@ prometheus) prometheus_output="${prometheus_output:+$prometheus_output\n}specex_vuln_status{name=\"$aka\",cve=\"$1\",status=\"$2\",info=\"$3\"} 1" ;; + *) echo "$0: error: invalid batch format '$opt_batch_format' specified" >&2; exit 255;; esac fi @@ -889,6 +990,8 @@ case "$2" in UNK) global_unknown="1";; VULN) global_critical="1";; + OK) ;; + *) echo "$0: error: unknown status '$2' passed to pvulnstatus()" >&2; exit 255;; esac # display info if we're not in quiet/batch mode @@ -899,6 +1002,7 @@ UNK) pstatus yellow 'UNKNOWN' "$@"; final_summary="$final_summary \033[43m\033[30m$pvulnstatus_last_cve:??\033[0m";; VULN) pstatus red 'VULNERABLE' "$@"; final_summary="$final_summary \033[41m\033[30m$pvulnstatus_last_cve:KO\033[0m";; OK) pstatus green 'NOT VULNERABLE' "$@"; final_summary="$final_summary \033[42m\033[30m$pvulnstatus_last_cve:OK\033[0m";; + *) echo "$0: error: unknown status '$vulnstatus' passed to pvulnstatus()" >&2; exit 255;; esac } @@ -963,7 +1067,7 @@ for pos in $(tr "$1\n$2" "\n$2=" < "$6" | grep -abo "^$2") do _debug "try_decompress: magic for $3 found at offset $pos" - if ! which "$3" >/dev/null 2>&1; then + if ! command -v "$3" >/dev/null 2>&1; then kernel_err="missing '$3' tool, please install it, usually it's in the '$5' package" return 0 fi @@ -1113,6 +1217,14 @@ 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"'}') @@ -1156,7 +1268,7 @@ is_coreos() { - which coreos-install >/dev/null 2>&1 && which toolbox >/dev/null 2>&1 && return 0 + command -v coreos-install >/dev/null 2>&1 && command -v toolbox >/dev/null 2>&1 && return 0 return 1 } @@ -1196,6 +1308,42 @@ 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" @@ -1223,6 +1371,14 @@ # 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") @@ -1428,6 +1584,57 @@ [ "$cpu_family" = 24 ] && return 0 return 1 } + +# Test if the current host is a Xen PV Dom0 / DomU +is_xen() { + if [ ! -d "$procfs/xen" ]; then + return 1 + fi + + # XXX do we have a better way that relying on dmesg? + dmesg_grep 'Booting paravirtualized kernel on Xen$'; ret=$? + if [ $ret -eq 2 ]; then + _warn "dmesg truncated, Xen detection will be unreliable. Please reboot and relaunch this script" + return 1 + elif [ $ret -eq 0 ]; then + return 0 + else + return 1 + fi +} + +is_xen_dom0() +{ + if ! is_xen; then + return 1 + fi + + if [ -e "$procfs/xen/capabilities" ] && grep -q "control_d" "$procfs/xen/capabilities"; then + return 0 + else + return 1 + fi +} + +is_xen_domU() +{ + if ! is_xen; then + return 1 + fi + + # PVHVM guests also print 'Booting paravirtualized kernel', so we need this check. + dmesg_grep 'Xen HVM callback vector for event delivery is enabled$'; ret=$? + if [ $ret -eq 0 ]; then + return 1 + fi + + if ! is_xen_dom0; then + return 0 + else + return 1 + fi +} + if [ -r "$mcedb_cache" ]; then mcedb_source="$mcedb_cache" mcedb_info="local MCExtractor DB "$(grep -E '^# %%% MCEDB ' "$mcedb_source" | cut -c13-) @@ -1670,7 +1877,7 @@ fi if [ -e "$opt_kernel" ]; then - if ! which "${opt_arch_prefix}readelf" >/dev/null 2>&1; then + if ! command -v "${opt_arch_prefix}readelf" >/dev/null 2>&1; then _debug "readelf not found" kernel_err="missing '${opt_arch_prefix}readelf' tool, please install it, usually it's in the 'binutils' package" elif [ "$opt_sysfs_only" = 1 ] || [ "$opt_hw_only" = 1 ]; then @@ -1723,29 +1930,59 @@ 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='.*' - 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: " if echo "$msg" | grep -qi '^not affected'; then # Not affected status=OK - pstatus green YES "$msg" + pstatus green YES "$fullmsg" elif echo "$msg" | grep -qi '^mitigation'; then # Mitigation: PTI status=OK - pstatus green YES "$msg" + pstatus green YES "$fullmsg" elif echo "$msg" | grep -qi '^vulnerable'; then # Vulnerable status=VULN - pstatus yellow NO "$msg" + pstatus yellow NO "$fullmsg" else status=UNK - pstatus yellow UNKNOWN "$msg" + pstatus yellow UNKNOWN "$fullmsg" fi _debug "sys_interface_check: $file=$msg (re=$regex)" return 0 @@ -1764,90 +2001,128 @@ 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 which wrmsr >/dev/null 2>&1 && [ "$SMC_NO_WRMSR" != 1 ]; then + 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 which perl >/dev/null 2>&1 && [ "$SMC_NO_PERL" != 1 ]; then + 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 which rdmsr >/dev/null 2>&1 && [ "$SMC_NO_RDMSR" != 1 ]; then + 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 which perl >/dev/null 2>&1 && [ "$SMC_NO_PERL" != 1 ]; then + 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 } @@ -2186,6 +2461,22 @@ fi if is_intel; then + _info " * Microarchitecture Data Sampling" + _info_nol " * VERW instruction is available: " + read_cpuid 0x7 $EDX 10 1 1; ret=$? + if [ $ret -eq 0 ]; then + cpuid_md_clear=1 + pstatus green YES "MD_CLEAR feature bit" + elif [ $ret -eq 2 ]; then + cpuid_md_clear=-1 + pstatus yellow UNKNOWN "is cpuid kernel module available?" + else + cpuid_md_clear=0 + pstatus yellow NO + fi + fi + + if is_intel; then _info " * Enhanced IBRS (IBRS_ALL)" _info_nol " * CPU indicates ARCH_CAPABILITIES MSR availability: " cpuid_arch_capabilities=-1 @@ -2202,6 +2493,7 @@ fi _info_nol " * ARCH_CAPABILITIES MSR advertises IBRS_ALL capability: " + capabilities_mds_no=-1 capabilities_rdcl_no=-1 capabilities_ibrs_all=-1 capabilities_rsba=-1 @@ -2211,6 +2503,7 @@ pstatus yellow UNKNOWN elif [ "$cpuid_arch_capabilities" != 1 ]; then capabilities_rdcl_no=0 + capabilities_mds_no=0 capabilities_ibrs_all=0 capabilities_rsba=0 capabilities_l1dflush_no=0 @@ -2242,6 +2535,7 @@ done capabilities=$val_cap_msr capabilities_rdcl_no=0 + capabilities_mds_no=0 capabilities_ibrs_all=0 capabilities_rsba=0 capabilities_l1dflush_no=0 @@ -2253,7 +2547,8 @@ [ $(( capabilities >> 2 & 1 )) -eq 1 ] && capabilities_rsba=1 [ $(( capabilities >> 3 & 1 )) -eq 1 ] && capabilities_l1dflush_no=1 [ $(( capabilities >> 4 & 1 )) -eq 1 ] && capabilities_ssb_no=1 - _debug "capabilities says rdcl_no=$capabilities_rdcl_no ibrs_all=$capabilities_ibrs_all rsba=$capabilities_rsba l1dflush_no=$capabilities_l1dflush_no ssb_no=$capabilities_ssb_no" + [ $(( capabilities >> 5 & 1 )) -eq 1 ] && capabilities_mds_no=1 + _debug "capabilities says rdcl_no=$capabilities_rdcl_no ibrs_all=$capabilities_ibrs_all rsba=$capabilities_rsba l1dflush_no=$capabilities_l1dflush_no ssb_no=$capabilities_ssb_no mds_no=$capabilities_mds_no" if [ "$capabilities_ibrs_all" = 1 ]; then if [ $cpu_mismatch -eq 0 ]; then pstatus green YES @@ -2272,7 +2567,7 @@ 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 @@ -2307,6 +2602,15 @@ else pstatus blue NO fi + + _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 _info_nol " * CPU supports Software Guard Extensions (SGX): " @@ -2367,7 +2671,7 @@ # if we were already called, don't do it again [ -n "$redhat_canonical_spectre" ] && return - if ! which "${opt_arch_prefix}strings" >/dev/null 2>&1; then + if ! command -v "${opt_arch_prefix}strings" >/dev/null 2>&1; then redhat_canonical_spectre=-1 elif [ -n "$kernel_err" ]; then redhat_canonical_spectre=-2 @@ -2451,7 +2755,7 @@ # http://git.arm.linux.org.uk/cgit/linux-arm.git/commit/?h=spectre&id=a78d156587931a2c3b354534aa772febf6c9e855 if [ -n "$kernel_err" ]; then pstatus yellow UNKNOWN "couldn't check ($kernel_err)" - elif ! which perl >/dev/null 2>&1; then + elif ! command -v perl >/dev/null 2>&1; then pstatus yellow UNKNOWN "missing 'perl' binary, please install it" else perl -ne '/\x0f\x83....\x48\x19\xd2\x48\x21\xd0/ and $found++; END { exit($found) }' "$kernel"; ret=$? @@ -2508,9 +2812,9 @@ pstatus yellow NO elif [ -n "$kernel_err" ]; then pstatus yellow UNKNOWN "couldn't check ($kernel_err)" - elif ! which perl >/dev/null 2>&1; then + elif ! command -v perl >/dev/null 2>&1; then pstatus yellow UNKNOWN "missing 'perl' binary, please install it" - elif ! which "${opt_arch_prefix}objdump" >/dev/null 2>&1; then + elif ! command -v "${opt_arch_prefix}objdump" >/dev/null 2>&1; then pstatus yellow UNKNOWN "missing '${opt_arch_prefix}objdump' tool, please install it, usually it's in the binutils package" else "${opt_arch_prefix}objdump" -d "$kernel" | perl -ne 'push @r, $_; /\s(hint|csdb)\s/ && $r[0]=~/\ssub\s+(x\d+)/ && $r[1]=~/\sbic\s+$1,\s+$1,/ && $r[2]=~/\sand\s/ && exit(9); shift @r if @r>3'; ret=$? @@ -2522,14 +2826,14 @@ fi fi - if [ "$opt_verbose" -ge 2 ] || ( [ -z "$v1_mask_nospec" ] && [ "$redhat_canonical_spectre" != 1 ] && [ "$redhat_canonical_spectre" != 2 ] ); then + if [ "$opt_verbose" -ge 2 ] || { [ -z "$v1_mask_nospec" ] && [ "$redhat_canonical_spectre" != 1 ] && [ "$redhat_canonical_spectre" != 2 ]; }; then # this is a slow heuristic and we don't need it if we already know the kernel is patched # but still show it in verbose mode _info_nol "* Checking count of LFENCE instructions following a jump in kernel... " if [ -n "$kernel_err" ]; then pstatus yellow UNKNOWN "couldn't check ($kernel_err)" else - if ! which "${opt_arch_prefix}objdump" >/dev/null 2>&1; then + if ! command -v "${opt_arch_prefix}objdump" >/dev/null 2>&1; then pstatus yellow UNKNOWN "missing '${opt_arch_prefix}objdump' tool, please install it, usually it's in the binutils package" else # here we disassemble the kernel and count the number of occurrences of the LFENCE opcode @@ -2568,7 +2872,7 @@ pvulnstatus $cve OK "Kernel source has been patched to mitigate the vulnerability (Red Hat/Ubuntu patch)" elif [ "$v1_lfence" = 1 ]; then pvulnstatus $cve OK "Kernel source has PROBABLY been patched to mitigate the vulnerability (jump-then-lfence instructions heuristic)" - elif [ "$kernel_err" ]; then + elif [ -n "$kernel_err" ]; then pvulnstatus $cve UNK "Couldn't find kernel image or tools missing to execute the checks" explain "Re-run this script with root privileges, after installing the missing tools indicated above" else @@ -2681,21 +2985,21 @@ # 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 '\' -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 @@ -2713,7 +3017,7 @@ fi fi if [ -z "$ibrs_supported" ] && [ -n "$kernel" ]; then - if ! which "${opt_arch_prefix}strings" >/dev/null 2>&1; then + if ! command -v "${opt_arch_prefix}strings" >/dev/null 2>&1; then : else ibrs_can_tell=1 @@ -2734,7 +3038,7 @@ # recent (4.15) vanilla kernels have IBPB but not IBRS, and without the debugfs tunables of Red Hat # we can detect it directly in the image if [ -z "$ibpb_supported" ] && [ -n "$kernel" ]; then - if ! which "${opt_arch_prefix}strings" >/dev/null 2>&1; then + if ! command -v "${opt_arch_prefix}strings" >/dev/null 2>&1; then : else ibpb_can_tell=1 @@ -2880,9 +3184,9 @@ # # 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 @@ -2898,7 +3202,7 @@ fi elif [ -n "$kernel" ]; then # look for the symbol - if which "${opt_arch_prefix}nm" >/dev/null 2>&1; then + if command -v "${opt_arch_prefix}nm" >/dev/null 2>&1; then # the proper way: use nm and look for the symbol if "${opt_arch_prefix}nm" "$kernel" 2>/dev/null | grep -qw 'noretpoline_setup'; then retpoline_compiler=1 @@ -2946,7 +3250,7 @@ # only for information, in verbose mode if [ "$opt_verbose" -ge 2 ]; then _info_nol " * Local gcc is retpoline-aware: " - if which gcc >/dev/null 2>&1; then + if command -v gcc >/dev/null 2>&1; then if [ -n "$(gcc -mindirect-branch=thunk-extern --version 2>&1 >/dev/null)" ]; then pstatus blue NO else @@ -2959,7 +3263,7 @@ if is_vulnerable_to_empty_rsb || [ "$opt_verbose" -ge 2 ]; then _info_nol " * Kernel supports RSB filling: " - if ! which "${opt_arch_prefix}strings" >/dev/null 2>&1; then + if ! command -v "${opt_arch_prefix}strings" >/dev/null 2>&1; then pstatus yellow UNKNOWN "missing '${opt_arch_prefix}strings' tool, please install it, usually it's in the binutils package" elif [ -z "$kernel" ]; then pstatus yellow UNKNOWN "kernel image missing" @@ -3039,7 +3343,7 @@ if [ "$opt_live" = 1 ] && [ "$vulnstatus" != "OK" ]; then _explain_hypervisor="An updated CPU microcode will have IBRS/IBPB capabilities indicated in the Hardware Check section above. If you're running under a hypervisor (KVM, Xen, VirtualBox, VMware, ...), the hypervisor needs to be up to date to be able to export the new host CPU flags to the guest. You can run this script on the host to check if the host CPU is IBRS/IBPB. If it is, and it doesn't show up in the guest, upgrade the hypervisor. You may need to reconfigure your VM to use a CPU model that has IBRS capability; in Libvirt, such CPUs are listed with an IBRS suffix." # IBPB (amd & intel) - if ( [ -z "$ibpb_enabled" ] || [ "$ibpb_enabled" = 0 ] ) && ( is_intel || is_amd || is_hygon); then + if { [ -z "$ibpb_enabled" ] || [ "$ibpb_enabled" = 0 ]; } && { is_intel || is_amd || is_hygon; }; then if [ -z "$cpuid_ibpb" ]; then explain "The microcode of your CPU needs to be upgraded to be able to use IBPB. This is usually done at boot time by your kernel (the upgrade is not persistent across reboots which is why it's done at each boot). If you're using a distro, make sure you are up to date, as microcode updates are usually shipped alongside with the distro kernel. Availability of a microcode update for you CPU model depends on your CPU vendor. You can usually find out online if a microcode update is available for your CPU by searching for your CPUID (indicated in the Hardware Check section). $_explain_hypervisor" fi @@ -3064,7 +3368,7 @@ # /IBPB # IBRS (amd & intel) - if ( [ -z "$ibrs_enabled" ] || [ "$ibrs_enabled" = 0 ] ) && ( is_intel || is_amd || is_hygon); then + if { [ -z "$ibrs_enabled" ] || [ "$ibrs_enabled" = 0 ]; } && { is_intel || is_amd || is_hygon; }; then if [ -z "$cpuid_ibrs" ]; then explain "The microcode of your CPU needs to be upgraded to be able to use IBRS. This is usually done at boot time by your kernel (the upgrade is not persistent across reboots which is why it's done at each boot). If you're using a distro, make sure you are up to date, as microcode updates are usually shipped alongside with the distro kernel. Availability of a microcode update for you CPU model depends on your CPU vendor. You can usually find out online if a microcode update is available for your CPU by searching for your CPUID (indicated in the Hardware Check section). $_explain_hypervisor" fi @@ -3140,7 +3444,7 @@ if [ -n "$kernel_err" ]; then pstatus yellow UNKNOWN "couldn't check ($kernel_err)" else - if ! which "${opt_arch_prefix}readelf" >/dev/null 2>&1; then + if ! command -v "${opt_arch_prefix}readelf" >/dev/null 2>&1; then pstatus yellow UNKNOWN "missing '${opt_arch_prefix}readelf' tool, please install it, usually it's in the binutils package" else nb_thunks=$("${opt_arch_prefix}readelf" -s "$kernel" | grep -c -e __llvm_retpoline_ -e __llvm_external_retpoline_ -e __x86_indirect_thunk_) @@ -3256,7 +3560,7 @@ # nopti option that is part of the patch (kernel command line option) # 'kpti=': arm kpti_can_tell=1 - if ! which "${opt_arch_prefix}strings" >/dev/null 2>&1; then + if ! command -v "${opt_arch_prefix}strings" >/dev/null 2>&1; then pstatus yellow UNKNOWN "missing '${opt_arch_prefix}strings' tool, please install it, usually it's in the binutils package" else kpti_support=$("${opt_arch_prefix}strings" "$kernel" | grep -w -e nopti -e kpti=) @@ -3298,6 +3602,10 @@ # Red Hat Backport creates a dedicated file, see https://access.redhat.com/articles/3311301 kpti_enabled=$(cat /sys/kernel/debug/x86/pti_enabled 2>/dev/null) _debug "kpti_enabled: file /sys/kernel/debug/x86/pti_enabled exists and says: $kpti_enabled" + elif is_xen_dom0; then + pti_xen_pv_domU=$(xl dmesg | grep 'XPTI' | grep 'DomU enabled' | head -1) + + [ -n "$pti_xen_pv_domU" ] && kpti_enabled=1 fi if [ -z "$kpti_enabled" ]; then dmesg_grep "$dmesg_grep"; ret=$? @@ -3334,24 +3642,8 @@ # Test if the current host is a Xen PV Dom0 / DomU - if [ -d "$procfs/xen" ]; then - # XXX do we have a better way that relying on dmesg? - dmesg_grep 'Booting paravirtualized kernel on Xen$'; ret=$? - if [ $ret -eq 2 ]; then - _warn "dmesg truncated, Xen detection will be unreliable. Please reboot and relaunch this script" - elif [ $ret -eq 0 ]; then - if [ -e "$procfs/xen/capabilities" ] && grep -q "control_d" "$procfs/xen/capabilities"; then - xen_pv_domo=1 - else - xen_pv_domu=1 - fi - # PVHVM guests also print 'Booting paravirtualized kernel', so we need this check. - dmesg_grep 'Xen HVM callback vector for event delivery is enabled$'; ret=$? - if [ $ret -eq 0 ]; then - xen_pv_domu=0 - fi - fi - fi + is_xen_dom0 && xen_pv_domo=1 + is_xen_domU && xen_pv_domu=1 if [ "$opt_live" = 1 ]; then # checking whether we're running under Xen PV 64 bits. If yes, we are affected by variant3 @@ -3523,7 +3815,7 @@ sys_interface_available=1 fi if [ "$opt_sysfs_only" != 1 ]; then - _info_nol "* Kernel supports speculation store bypass: " + _info_nol "* Kernel supports disabling speculative store bypass (SSB): " if [ "$opt_live" = 1 ]; then if grep -Eq 'Speculation.?Store.?Bypass:' "$procfs/self/status" 2>/dev/null; then kernel_ssb="found in $procfs/self/status" @@ -3545,6 +3837,36 @@ pstatus yellow NO fi + kernel_ssbd_enabled=-1 + if [ "$opt_live" = 1 ]; then + # https://elixir.bootlin.com/linux/v5.0/source/fs/proc/array.c#L340 + _info_nol "* SSB mitigation is enabled and active: " + if grep -Eq 'Speculation.?Store.?Bypass:[[:space:]]+thread' "$procfs/self/status" 2>/dev/null; then + kernel_ssbd_enabled=1 + pstatus green YES "per-thread through prctl" + elif grep -Eq 'Speculation.?Store.?Bypass:[[:space:]]+globally mitigated' "$procfs/self/status" 2>/dev/null; then + kernel_ssbd_enabled=2 + pstatus green YES "global" + elif grep -Eq 'Speculation.?Store.?Bypass:[[:space:]]+vulnerable' "$procfs/self/status" 2>/dev/null; then + kernel_ssbd_enabled=0 + pstatus yellow NO + elif grep -Eq 'Speculation.?Store.?Bypass:[[:space:]]+not vulnerable' "$procfs/self/status" 2>/dev/null; then + kernel_ssbd_enabled=-2 + pstatus blue NO "not vulnerable" + fi + + if [ "$kernel_ssbd_enabled" = 1 ]; then + _info_nol "* SSB mitigation currently active for selected processes: " + mitigated_processes=$(grep -El 'Speculation.?Store.?Bypass:[[:space:]]+thread (force )?mitigated' /proc/*/status \ + | sed s/status/exe/ | xargs -r -n1 readlink -f | xargs -r -n1 basename | sort -u | tr "\n" " " | sed 's/ $//') + if [ -n "$mitigated_processes" ]; then + pstatus green YES "$mitigated_processes" + else + pstatus yellow NO "no process found using SSB mitigation through prctl" + fi + 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!" @@ -3558,7 +3880,15 @@ # if msg is empty, sysfs check didn't fill it, rely on our own test if [ -n "$cpuid_ssbd" ]; then if [ -n "$kernel_ssb" ]; then - pvulnstatus $cve OK "your system provides the necessary tools for software mitigation" + if [ "$opt_live" = 1 ]; then + if [ "$kernel_ssbd_enabled" -gt 0 ]; then + pvulnstatus $cve OK "your CPU and kernel both support SSBD and mitigation is enabled" + else + pvulnstatus $cve VULN "your CPU and kernel both support SSBD but the mitigation is not active" + fi + else + pvulnstatus $cve OK "your system provides the necessary tools for software mitigation" + fi else pvulnstatus $cve VULN "your kernel needs to be updated" explain "You have a recent-enough CPU microcode but your kernel is too old to use the new features exported by your CPU's microcode. If you're using a distro kernel, upgrade your distro to get the latest kernel available. Otherwise, recompile the kernel from recent-enough sources." @@ -3677,13 +4007,13 @@ 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 if [ "$opt_sysfs_only" != 1 ]; then _info_nol "* Kernel supports PTE inversion: " - if ! which "${opt_arch_prefix}strings" >/dev/null 2>&1; then + if ! command -v "${opt_arch_prefix}strings" >/dev/null 2>&1; then pteinv_supported=-1 else if "${opt_arch_prefix}strings" "$kernel" | grep -Fq 'PTE Inversion'; then @@ -3698,8 +4028,8 @@ _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 @@ -3741,6 +4071,12 @@ check_CVE_2018_3620_bsd() { _info_nol "* Kernel reserved the memory page at physical address 0x0: " + if ! kldstat -q -m vmm; then + kldload vmm 2>/dev/null && kldload_vmm=1 + _debug "attempted to load module vmm, kldload_vmm=$kldload_vmm" + else + _debug "vmm module already loaded" + fi if sysctl hw.vmm.vmx.l1d_flush >/dev/null 2>&1; then # https://security.FreeBSD.org/patches/SA-18:09/l1tf-11.2.patch # this is very difficult to detect that the kernel reserved the 0 page, but this fix @@ -3783,44 +4119,36 @@ 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 + l1d_mode=-1 if [ "$opt_sysfs_only" != 1 ]; then _info_nol "* This system is a host running a hypervisor: " has_vmm=$opt_vmm - if [ "$has_vmm" = -1 ]; then - # Assumed to be running on bare metal unless evidence of vm is found. + if [ "$has_vmm" = -1 ] && [ "$opt_paranoid" = 1 ]; then + # In paranoid mode, if --vmm was not specified on the command-line, + # we want to be secure before everything else, so assume we're running + # a hypervisor, as this requires more mitigations + has_vmm=2 + elif [ "$has_vmm" = -1 ]; then + # Here, we want to know if we are hosting a hypervisor, and running some VMs on it. + # If we find no evidence that this is the case, assume we're not (to avoid scaring users), + # this can always be overridden with --vmm in any case. has_vmm=0 - # test for presence of hypervisor flag - definitive if set - if [ -e "$procfs/cpuinfo" ] && grep ^flags "$procfs/cpuinfo" | grep -qw hypervisor; then - has_vmm=1 - _debug "hypervisor: present - hypervisor flag set in $procfs/cpuinfo" - else - _debug "hypervisor: unknown - hypervisor flag not set in $procfs/cpuinfo" - fi - # test for kernel detected hypervisor - dmesg_grep "Hypervisor detected:" ; ret=$? - if [ $ret -eq 0 ]; then - _debug "hypervisor: present - found in dmesg: $dmesg_grepped" - has_vmm=1 - elif [ $ret -eq 2 ]; then - _debug "hypervisor: dmesg truncated" - fi - # test for kernel detected paravirtualization - dmesg_grep "Booting paravirtualized kernel on bare hardware" ; ret=$? - if [ $ret -eq 0 ]; then - _debug "hypervisor: not present (bare hardware)- found in dmesg: $dmesg_grepped" - elif [ $ret -eq 2 ]; then - _debug "hypervisor: dmesg truncated" + if command -v pgrep >/dev/null 2>&1; then + # remove xenbus and xenwatch, also present inside domU + # 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 - dmesg_grep "Booting paravirtualized kernel on" ; ret=$? - if [ $ret -eq 0 ]; then - _debug "hypervisor: present - found in dmesg: $dmesg_grepped" + # 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 '\/dev/null 2>&1; then + if ! command -v "${opt_arch_prefix}strings" >/dev/null 2>&1; then l1d_kernel_err="missing '${opt_arch_prefix}strings' tool, please install it, usually it's in the binutils package" elif [ -n "$kernel_err" ]; then l1d_kernel_err="$kernel_err" @@ -3878,23 +4208,44 @@ _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 - l1d_mode=-1 - pstatus yellow UNKNOWN "unrecognized mode" + if is_xen_dom0; then + l1d_xen_hardware=$(xl dmesg | grep 'Hardware features:' | grep 'L1D_FLUSH' | head -1) + l1d_xen_hypervisor=$(xl dmesg | grep 'Xen settings:' | grep 'L1D_FLUSH' | head -1) + l1d_xen_pv_domU=$(xl dmesg | grep 'PV L1TF shadowing:' | grep 'DomU enabled' | head -1) + + if [ -n "$l1d_xen_hardware" ] && [ -n "$l1d_xen_hypervisor" ] && [ -n "$l1d_xen_pv_domU" ]; then + l1d_mode=5 + pstatus green YES "for XEN guests" + elif [ -n "$l1d_xen_hardware" ] && [ -n "$l1d_xen_hypervisor" ]; then + l1d_mode=4 + pstatus yellow YES "for XEN guests (HVM only)" + elif [ -n "$l1d_xen_pv_domU" ]; then + l1d_mode=3 + pstatus yellow YES "for XEN guests (PV only)" + else + l1d_mode=0 + pstatus yellow NO "for XEN guests" + fi + else + l1d_mode=-1 + pstatus yellow UNKNOWN "unrecognized mode" + fi fi else l1d_mode=-1 @@ -3907,7 +4258,7 @@ _info_nol " * Hardware-backed L1D flush supported: " if [ "$opt_live" = 1 ]; then - if grep -qw flush_l1d "$procfs/cpuinfo"; then + if grep -qw flush_l1d "$procfs/cpuinfo" || [ -n "$l1d_xen_hardware" ]; then pstatus green YES "performance impact of the mitigation will be greatly reduced" else pstatus blue NO "flush will be done in software, this is slower" @@ -3936,6 +4287,9 @@ 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 @@ -3945,7 +4299,7 @@ if [ "$l1d_mode" -ge 1 ]; then pvulnstatus $cve OK "L1D flushing is enabled and mitigates the vulnerability" else - pvulnstatus $cve VULN "disable EPT or enabled L1D flushing to mitigate the vulnerability" + pvulnstatus $cve VULN "disable EPT or enable L1D flushing to mitigate the vulnerability" fi else if [ "$l1d_mode" -ge 2 ]; then @@ -3962,6 +4316,14 @@ fi fi fi + + if [ $l1d_mode -gt 3 ]; then + _warn + _warn "This host is a Xen Dom0. Please make sure that you are running your DomUs" + _warn "with a kernel which contains CVE-2018-3646 mitigations." + _warn + _warn "See https://www.suse.com/support/kb/doc/?id=7023078 and XSA-273 for details." + fi fi } @@ -3998,6 +4360,262 @@ fi } +################### +# MSBDS SECTION + +# Microarchitectural Store Buffer Data Sampling +check_CVE_2018_12126() +{ + cve='CVE-2018-12126' + check_mds $cve +} + +################### +# MFBDS SECTION + +# Microarchitectural Fill Buffer Data Sampling +check_CVE_2018_12130() +{ + cve='CVE-2018-12130' + check_mds $cve +} + +################### +# MLPDS SECTION + +# Microarchitectural Load Port Data Sampling +check_CVE_2018_12127() +{ + cve='CVE-2018-12127' + check_mds $cve +} + +################### +# MDSUM SECTION + +# Microarchitectural Data Sampling Uncacheable Memory +check_CVE_2019_11091() +{ + cve='CVE-2019-11091' + check_mds $cve +} + +# Microarchitectural Data Sampling +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='' + if sys_interface_check "/sys/devices/system/cpu/vulnerabilities/mds" '^[^;]+'; then + # this kernel has the /sys interface, trust it over everything + sys_interface_available=1 + fi + if [ "$opt_sysfs_only" != 1 ]; then + _info_nol "* Kernel supports using MD_CLEAR mitigation: " + kernel_md_clear='' + kernel_md_clear_can_tell=1 + if [ "$opt_live" = 1 ] && grep ^flags $procfs/cpuinfo | grep -qw md_clear; then + kernel_md_clear="md_clear found in $procfs/cpuinfo" + pstatus green YES "$kernel_md_clear" + fi + if [ -z "$kernel_md_clear" ]; then + if ! command -v "${opt_arch_prefix}strings" >/dev/null 2>&1; then + kernel_md_clear_can_tell=0 + elif [ -n "$kernel_err" ]; then + kernel_md_clear_can_tell=0 + elif "${opt_arch_prefix}strings" "$kernel" | grep -q 'Clear CPU buffers'; then + _debug "md_clear: found 'Clear CPU buffers' string in kernel image" + kernel_md_clear='found md_clear implementation evidence in kernel image' + pstatus green YES "$kernel_md_clear" + fi + fi + if [ -z "$kernel_md_clear" ]; then + if [ "$kernel_md_clear_can_tell" = 1 ]; then + pstatus yellow NO + else + pstatus yellow UNKNOWN + fi + fi + + if [ "$opt_live" = 1 ] && [ "$sys_interface_available" = 1 ]; then + _info_nol "* Kernel mitigation is enabled and active: " + if echo "$fullmsg" | grep -qi ^mitigation; then + mds_mitigated=1 + pstatus green YES + else + mds_mitigated=0 + pstatus yellow NO + fi + _info_nol "* SMT is either mitigated or disabled: " + if echo "$fullmsg" | grep -Eq 'SMT (disabled|mitigated)'; then + mds_smt_mitigated=1 + pstatus green YES + else + mds_smt_mitigated=0 + pstatus yellow NO + 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 + + 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 [ -z "$msg" ]; then + # if msg is empty, sysfs check didn't fill it, rely on our own test + if [ "$cpuid_md_clear" = 1 ]; then + if [ -n "$kernel_md_clear" ]; then + if [ "$opt_live" = 1 ]; then + # mitigation must also be enabled + if [ "$mds_mitigated" = 1 ]; then + if [ "$opt_paranoid" != 1 ] || [ "$mds_smt_mitigated" = 1 ]; 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 [ -n "$kernel_md_clear" ]; 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 + else + if [ "$opt_paranoid" = 1 ]; then + # in paranoid mode, we don't only need microcode + kernel update, we also want SMT mitigation + 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" + fi + else + pvulnstatus "$cve" "$status" "$fullmsg" + fi + fi +} + if [ "$opt_no_hw" = 0 ] && [ -z "$opt_arch_prefix" ]; then check_cpu check_cpu_vulnerabilities @@ -4022,15 +4640,40 @@ _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" @@ -4065,7 +4708,7 @@ # 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 v96 - 2019/01/15 +# %%% MCEDB v112 - 2019/05/22 # I,0x00000611,0x00000B27,19961218 # I,0x00000612,0x000000C6,19961210 # I,0x00000616,0x000000C6,19961210 @@ -4215,7 +4858,7 @@ # I,0x000206A4,0x00000022,20100414 # I,0x000206A5,0x00000007,20100722 # I,0x000206A6,0x90030028,20100924 -# I,0x000206A7,0x0000002E,20180410 +# I,0x000206A7,0x0000002F,20190217 # I,0x000206C0,0xFFFF001C,20091214 # I,0x000206C1,0x00000006,20091222 # I,0x000206C2,0x0000001F,20180508 @@ -4244,100 +4887,104 @@ # 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 # I,0x000306A5,0x00000009,20111110 # I,0x000306A6,0x00000004,20111114 # I,0x000306A8,0x00000010,20120220 -# I,0x000306A9,0x00000020,20180410 +# I,0x000306A9,0x00000021,20190213 # I,0x000306C0,0xFFFF0013,20111110 # I,0x000306C1,0xFFFF0014,20120725 # I,0x000306C2,0xFFFF0006,20121017 -# I,0x000306C3,0x00000025,20180402 +# I,0x000306C3,0x00000027,20190226 # I,0x000306D1,0xFFFF0009,20131015 # I,0x000306D2,0xFFFF0009,20131219 # I,0x000306D3,0xE3121338,20140825 -# I,0x000306D4,0x0000002B,20180322 +# I,0x000306D4,0x0000002D,20190307 # I,0x000306E0,0x00000008,20120726 # I,0x000306E2,0x0000020D,20130321 # I,0x000306E3,0x00000308,20130321 -# I,0x000306E4,0x0000042D,20180425 +# I,0x000306E4,0x0000042E,20190314 # I,0x000306E6,0x00000600,20130619 -# I,0x000306E7,0x00000714,20180425 +# I,0x000306E7,0x00000715,20190314 # I,0x000306F0,0xFFFF0017,20130730 # I,0x000306F1,0x00000014,20140110 -# I,0x000306F2,0x0000003D,20180420 +# I,0x000306F2,0x00000043,20190301 # I,0x000306F3,0x0000000D,20160211 -# I,0x000306F4,0x00000012,20180420 +# I,0x000306F4,0x00000014,20190301 # I,0x00040650,0xFFFF000B,20121206 -# I,0x00040651,0x00000024,20180402 +# I,0x00040651,0x00000025,20190226 # I,0x00040660,0xFFFF0011,20121012 -# I,0x00040661,0x0000001A,20180402 +# I,0x00040661,0x0000001B,20190226 # I,0x00040670,0xFFFF0006,20140304 -# I,0x00040671,0x0000001E,20180403 +# I,0x00040671,0x00000020,20190307 # I,0x000406A0,0x80124001,20130521 # I,0x000406A8,0x0000081F,20140812 # 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 # I,0x000406E2,0x0000002C,20150521 -# I,0x000406E3,0x000000C6,20180417 +# I,0x000406E3,0x000000CC,20190401 # I,0x000406E8,0x00000026,20160414 # I,0x000406F0,0x00000014,20150702 -# I,0x000406F1,0x0B000031,20180905 +# I,0x000406F1,0x0B000036,20190302 # I,0x00050650,0x8000002B,20160208 # I,0x00050651,0x8000002B,20160208 # I,0x00050652,0x80000037,20170502 # I,0x00050653,0x01000146,20180824 -# I,0x00050654,0x02000057,20181022 +# I,0x00050654,0x0200005E,20190402 # I,0x00050655,0x03000010,20181116 -# I,0x00050656,0x04000013,20181203 -# I,0x00050657,0x05000013,20181203 +# I,0x00050656,0x04000024,20190407 +# I,0x00050657,0x05000024,20190407 # I,0x00050661,0xF1000008,20150130 -# I,0x00050662,0x00000017,20180525 -# I,0x00050663,0x07000013,20180420 -# I,0x00050664,0x0F000012,20180420 -# I,0x00050665,0x0E00000A,20180420 +# I,0x00050662,0x0000001A,20190323 +# I,0x00050663,0x07000017,20190323 +# I,0x00050664,0x0F000015,20190323 +# I,0x00050665,0x0E00000D,20190323 # I,0x00050670,0xFFFF0030,20151113 # I,0x00050671,0x000001B6,20180108 # I,0x000506A0,0x00000038,20150112 # I,0x000506C2,0x00000014,20180511 # I,0x000506C8,0x90011010,20160323 -# I,0x000506C9,0x00000032,20180511 -# I,0x000506CA,0x0000000C,20180511 +# I,0x000506C9,0x00000038,20190115 +# I,0x000506CA,0x00000016,20190301 # I,0x000506D1,0x00000102,20150605 # I,0x000506E0,0x00000018,20141119 # I,0x000506E1,0x0000002A,20150602 # I,0x000506E2,0x0000002E,20150815 -# I,0x000506E3,0x000000C6,20180417 +# I,0x000506E3,0x000000CC,20190401 # I,0x000506E8,0x00000034,20160710 # I,0x000506F0,0x00000010,20160607 -# I,0x000506F1,0x00000024,20180511 +# I,0x000506F1,0x0000002E,20190321 # I,0x00060660,0x0000000C,20160821 # I,0x00060661,0x0000000E,20170128 # I,0x00060662,0x00000022,20171129 # I,0x00060663,0x0000002A,20180417 # I,0x000706A0,0x00000026,20170712 -# I,0x000706A1,0x0000002A,20180725 +# I,0x000706A1,0x0000002E,20190102 # I,0x000706E0,0x0000002A,20180528 -# I,0x000706E1,0x0000002E,20181119 -# I,0x000706E2,0x0000002E,20181119 +# I,0x000706E1,0x00000040,20190327 +# I,0x000706E2,0x00000040,20190327 +# I,0x000706E4,0x0000001A,20190403 +# I,0x000706E5,0x0000001E,20190420 # I,0x00080650,0x00000018,20180108 -# I,0x000806E9,0x0000009E,20181018 -# I,0x000806EA,0x0000009E,20181018 -# I,0x000806EB,0x000000A4,20181025 -# I,0x000906E9,0x0000009A,20180716 -# I,0x000906EA,0x000000AA,20181212 -# I,0x000906EB,0x000000AA,20181212 -# I,0x000906EC,0x000000A2,20180929 +# I,0x000806E9,0x000000B4,20190401 +# I,0x000806EA,0x000000B4,20190401 +# I,0x000806EB,0x000000B8,20190330 +# I,0x000806EC,0x000000B8,20190330 +# I,0x000906E9,0x000000B4,20190401 +# I,0x000906EA,0x000000B4,20190401 +# I,0x000906EB,0x000000B4,20190401 +# I,0x000906EC,0x000000AE,20190214 +# I,0x000906ED,0x000000BC,20190513 # A,0x00000F00,0x02000008,20070614 # A,0x00000F01,0x0000001C,20021031 # A,0x00000F10,0x00000003,20020325 @@ -4405,11 +5052,14 @@ # A,0x00730F01,0x07030106,20180209 # A,0x00800F00,0x0800002A,20161006 # A,0x00800F10,0x0800100C,20170131 -# A,0x00800F11,0x08001137,20180214 +# A,0x00800F11,0x08001138,20190204 # A,0x00800F12,0x08001230,20180804 -# A,0x00800F82,0x0800820B,20180620 +# A,0x00800F82,0x0800820C,20190204 # A,0x00810F00,0x08100004,20161120 -# A,0x00810F10,0x0810100B,20180212 +# 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,0x08701011,20190415