diff options
author | Stéphane Lesimple <speed47_github@speed47.net> | 2018-01-09 18:12:39 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-01-09 18:12:39 +0100 |
commit | 1c3d349667b6da8aa07595bd6f66906da15d6d2b (patch) | |
tree | 51f9d7adb608f47d6531c1d66f238d05355ca9b0 | |
parent | b93b13263d57a54cfb338bcedbbc6b89dd22a3af (diff) | |
parent | ad342cab06f3d32da3ef10ee1384ac1f417b0631 (diff) | |
download | spectre-meltdown-checker-1c3d349667b6da8aa07595bd6f66906da15d6d2b.tar.lz spectre-meltdown-checker-1c3d349667b6da8aa07595bd6f66906da15d6d2b.tar.xz spectre-meltdown-checker-1c3d349667b6da8aa07595bd6f66906da15d6d2b.zip |
Merge pull request #31 from Feandil/batch
Add a "batch" and "verbose" mode
-rwxr-xr-x | spectre-meltdown-checker.sh | 362 |
1 files changed, 213 insertions, 149 deletions
diff --git a/spectre-meltdown-checker.sh b/spectre-meltdown-checker.sh index 76eff5c..2a9a874 100755 --- a/spectre-meltdown-checker.sh +++ b/spectre-meltdown-checker.sh @@ -10,93 +10,7 @@ # VERSION=0.20 -# print status function -pstatus() -{ - if [ "$opt_no_color" = 1 ]; then - _echo_nol "$2" - else - case "$1" in - red) col="\033[101m\033[30m";; - green) col="\033[102m\033[30m";; - yellow) col="\033[103m\033[30m";; - blue) col="\033[104m\033[30m";; - *) col="";; - esac - _echo_nol "$col $2 \033[0m" - fi - [ -n "$3" ] && _echo_nol " ($3)" - _echo -} - -# The 3 below functions are taken from the extract-linux script, available here: -# https://github.com/torvalds/linux/blob/master/scripts/extract-vmlinux -# The functions have been modified for better integration to this script -# The original header of the file has been retained below - -# ---------------------------------------------------------------------- -# extract-vmlinux - Extract uncompressed vmlinux from a kernel image -# -# Inspired from extract-ikconfig -# (c) 2009,2010 Dick Streefland <dick@streefland.net> -# -# (c) 2011 Corentin Chary <corentin.chary@gmail.com> -# -# Licensed under the GNU General Public License, version 2 (GPLv2). -# ---------------------------------------------------------------------- - -vmlinux='' -vmlinux_err='' -check_vmlinux() -{ - readelf -h $1 > /dev/null 2>&1 || return 1 - return 0 -} - -try_decompress() -{ - # The obscure use of the "tr" filter is to work around older versions of - # "grep" that report the byte offset of the line instead of the pattern. - - # Try to find the header ($1) and decompress from here - for pos in `tr "$1\n$2" "\n$2=" < "$5" | grep -abo "^$2"` - do - if ! which $3 >/dev/null 2>&1; then - vmlinux_err="missing '$3' tool, please install it, usually it's in the '$4' package" - return 0 - fi - pos=${pos%%:*} - tail -c+$pos "$5" | $3 > $vmlinuxtmp 2> /dev/null - check_vmlinux "$vmlinuxtmp" && vmlinux=$vmlinuxtmp && return 0 - done - return 1 -} - -extract_vmlinux() -{ - [ -n "$1" ] || return 1 - # Prepare temp files: - vmlinuxtmp="$(mktemp /tmp/vmlinux-XXXXXX)" - trap "rm -f $vmlinuxtmp" EXIT - - # Initial attempt for uncompressed images or objects: - if check_vmlinux "$1"; then - cat "$1" > "$vmlinuxtmp" - vmlinux=$vmlinuxtmp - return 0 - fi - - # That didn't work, so retry after decompression. - try_decompress '\037\213\010' xy gunzip gunzip "$1" && return 0 - try_decompress '\3757zXZ\000' abcde unxz xz-utils "$1" && return 0 - try_decompress 'BZh' xy bunzip2 bzip2 "$1" && return 0 - try_decompress '\135\0\0\0' xxx unlzma xz-utils "$1" && return 0 - try_decompress '\211\114\132' xy 'lzop -d' lzop "$1" && return 0 - return 1 -} - -# end of extract-vmlinux functions - +# Script configuration show_usage() { cat <<EOF @@ -119,10 +33,22 @@ show_usage() Options: --no-color Don't use color codes + -v, --verbose Increase verbosity level + --batch Produce machine readable output EOF } +# parse options +opt_kernel='' +opt_config='' +opt_map='' +opt_live_explicit=0 +opt_live=1 +opt_no_color=0 +opt_batch=0 +opt_verbose=1 + __echo() { opt="$1" @@ -130,7 +56,7 @@ __echo() msg="$@" if [ "$opt_no_color" = 1 ] ; then # strip ANSI color codes - msg=$(echo "$msg" | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g") + msg=$(/bin/echo -e "$msg" | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g") fi # explicitely call /bin/echo to avoid shell builtins that might not take options /bin/echo $opt -e "$msg" @@ -138,12 +64,38 @@ __echo() _echo() { - __echo '' "$@" + if [ $opt_verbose -ge $1 ]; then + shift + __echo '' "$@" + fi } _echo_nol() { - __echo -n "$@" + if [ $opt_verbose -ge $1 ]; then + shift + __echo -n "$@" + fi +} + +_warn() +{ + _echo 0 "\033[31m${@}\033[0m" +} + +_info() +{ + _echo 1 "$@" +} + +_info_nol() +{ + _echo_nol 1 "$@" +} + +_verbose() +{ + _echo 2 "$@" } is_cpu_vulnerable() @@ -196,16 +148,11 @@ is_cpu_vulnerable() return 255 } -_echo "\033[1;34mSpectre and Meltdown mitigation detection tool v$VERSION\033[0m" -_echo - -# parse options -opt_kernel='' -opt_config='' -opt_map='' -opt_live_explicit=0 -opt_live=1 -opt_no_color=0 +show_header() +{ + _info "\033[1;34mSpectre and Meltdown mitigation detection tool v$VERSION\033[0m" + _info +} parse_opt_file() { @@ -213,16 +160,20 @@ parse_opt_file() option_name="$1" option_value="$2" if [ -z "$option_value" ]; then + show_header show_usage echo "$0: error: --$option_name expects one parameter (a file)" >&2 exit 1 elif [ ! -e "$option_value" ]; then + show_header echo "$0: error: couldn't find file $option_value" >&2 exit 1 elif [ ! -f "$option_value" ]; then + show_header echo "$0: error: $option_value is not a file" >&2 exit 1 elif [ ! -e "$option_value" ]; then + show_header echo "$0: error: couldn't read $option_value (are you root?)" >&2 exit 1 fi @@ -252,16 +203,130 @@ while [ -n "$1" ]; do elif [ "$1" = "--no-color" ]; then opt_no_color=1 shift + elif [ "$1" = "--batch" ]; then + opt_batch=1 + opt_verbose=0 + shift + elif [ "$1" = "-v" -o "$1" = "--verbose" ]; then + opt_verbose=$(expr $opt_verbose + 1) + shift elif [ "$1" = "-h" -o "$1" = "--help" ]; then + show_header show_usage exit 0 else + show_header show_usage echo "$0: error: unknown option '$1'" exit 1 fi done +show_header + +# print status function +pstatus() +{ + if [ "$opt_no_color" = 1 ]; then + _info_nol "$2" + else + case "$1" in + red) col="\033[101m\033[30m";; + green) col="\033[102m\033[30m";; + yellow) col="\033[103m\033[30m";; + blue) col="\033[104m\033[30m";; + *) col="";; + esac + _info_nol "$col $2 \033[0m" + fi + [ -n "$3" ] && _info_nol " ($3)" + _info +} + +# Print the final status of a vulnerability (incl. batch mode) +# Arguments are: CVE UNK/OK/VULN description +pvulnstatus() +{ + [ "$opt_batch" = 1 ] && _echo 0 "$1: $2 ($3)" + _info_nol "> \033[46m\033[30mSTATUS:\033[0m " + vulnstatus="$2" + shift 2 + case "$vulnstatus" in + UNK) pstatus yellow UNKNOWN "$@";; + VULN) pstatus red 'VULNERABLE' "$@";; + OK) pstatus green 'NOT VULNERABLE' "$@";; + esac +} + + +# The 3 below functions are taken from the extract-linux script, available here: +# https://github.com/torvalds/linux/blob/master/scripts/extract-vmlinux +# The functions have been modified for better integration to this script +# The original header of the file has been retained below + +# ---------------------------------------------------------------------- +# extract-vmlinux - Extract uncompressed vmlinux from a kernel image +# +# Inspired from extract-ikconfig +# (c) 2009,2010 Dick Streefland <dick@streefland.net> +# +# (c) 2011 Corentin Chary <corentin.chary@gmail.com> +# +# Licensed under the GNU General Public License, version 2 (GPLv2). +# ---------------------------------------------------------------------- + +vmlinux='' +vmlinux_err='' +check_vmlinux() +{ + readelf -h $1 > /dev/null 2>&1 || return 1 + return 0 +} + +try_decompress() +{ + # The obscure use of the "tr" filter is to work around older versions of + # "grep" that report the byte offset of the line instead of the pattern. + + # Try to find the header ($1) and decompress from here + for pos in `tr "$1\n$2" "\n$2=" < "$5" | grep -abo "^$2"` + do + if ! which $3 >/dev/null 2>&1; then + vmlinux_err="missing '$3' tool, please install it, usually it's in the '$4' package" + return 0 + fi + pos=${pos%%:*} + tail -c+$pos "$5" | $3 > $vmlinuxtmp 2> /dev/null + check_vmlinux "$vmlinuxtmp" && vmlinux=$vmlinuxtmp && return 0 + done + return 1 +} + +extract_vmlinux() +{ + [ -n "$1" ] || return 1 + # Prepare temp files: + vmlinuxtmp="$(mktemp /tmp/vmlinux-XXXXXX)" + trap "rm -f $vmlinuxtmp" EXIT + + # Initial attempt for uncompressed images or objects: + if check_vmlinux "$1"; then + cat "$1" > "$vmlinuxtmp" + vmlinux=$vmlinuxtmp + return 0 + fi + + # That didn't work, so retry after decompression. + try_decompress '\037\213\010' xy gunzip gunzip "$1" && return 0 + try_decompress '\3757zXZ\000' abcde unxz xz-utils "$1" && return 0 + try_decompress 'BZh' xy bunzip2 bzip2 "$1" && return 0 + try_decompress '\135\0\0\0' xxx unlzma xz-utils "$1" && return 0 + try_decompress '\211\114\132' xy 'lzop -d' lzop "$1" && return 0 + return 1 +} + +# end of extract-vmlinux functions + # check for mode selection inconsistency if [ "$opt_live_explicit" = 1 ]; then if [ -n "$opt_kernel" -o -n "$opt_config" -o -n "$opt_map" ]; then @@ -275,12 +340,12 @@ fi if [ "$opt_live" = 1 ]; then if [ "$(id -u)" -ne 0 ]; then - _echo "\033[31mNote that you should launch this script with root privileges to get accurate information.\033[0m" - _echo "\033[31mWe'll proceed but you might see permission denied errors.\033[0m" - _echo "\033[31mTo run it as root, you can try the following command: sudo $0\033[0m" - _echo + _warn "Note that you should launch this script with root privileges to get accurate information." + _warn "We'll proceed but you might see permission denied errors." + _warn "To run it as root, you can try the following command: sudo $0" + _warn fi - _echo "Checking for vulnerabilities against live running kernel \033[35m"$(uname -s) $(uname -r) $(uname -v) $(uname -m)"\033[0m" + _info "Checking for vulnerabilities against live running kernel \033[35m"$(uname -s) $(uname -r) $(uname -v) $(uname -m)"\033[0m" # try to find the image of the current running kernel [ -e /boot/vmlinuz-linux ] && opt_kernel=/boot/vmlinuz-linux @@ -307,24 +372,24 @@ if [ "$opt_live" = 1 ]; then opt_config=/boot/config-$(uname -r) fi else - _echo "Checking for vulnerabilities against specified kernel" + _info "Checking for vulnerabilities against specified kernel" fi if [ -n "$opt_kernel" ]; then - _echo "Will use vmlinux image \033[35m$opt_kernel\033[0m" + _verbose "Will use vmlinux image \033[35m$opt_kernel\033[0m" else - _echo "Will use no vmlinux image (accuracy might be reduced)" + _verbose "Will use no vmlinux image (accuracy might be reduced)" fi if [ -n "$dumped_config" ]; then - _echo "Will use kconfig \033[35m/proc/config.gz\033[0m" + _verbose "Will use kconfig \033[35m/proc/config.gz\033[0m" elif [ -n "$opt_config" ]; then - _echo "Will use kconfig \033[35m$opt_config\033[0m" + _verbose "Will use kconfig \033[35m$opt_config\033[0m" else - _echo "Will use no kconfig (accuracy might be reduced)" + _verbose "Will use no kconfig (accuracy might be reduced)" fi if [ -n "$opt_map" ]; then - _echo "Will use System.map file \033[35m$opt_map\033[0m" + _verbose "Will use System.map file \033[35m$opt_map\033[0m" else - _echo "Will use no System.map file (accuracy might be reduced)" + _verbose "Will use no System.map file (accuracy might be reduced)" fi if [ -e "$opt_kernel" ]; then @@ -340,12 +405,12 @@ if [ -z "$vmlinux" -o ! -r "$vmlinux" ]; then [ -z "$vmlinux_err" ] && vmlinux_err="couldn't extract your kernel from $opt_kernel" fi -_echo +_info ########### # SPECTRE 1 -_echo "\033[1;34mCVE-2017-5753 [bounds check bypass] aka 'Spectre Variant 1'\033[0m" -_echo_nol "* Checking count of LFENCE opcodes in kernel: " +_info "\033[1;34mCVE-2017-5753 [bounds check bypass] aka 'Spectre Variant 1'\033[0m" +_info_nol "* Checking count of LFENCE opcodes in kernel: " status=0 if [ -n "$vmlinux_err" ]; then @@ -371,21 +436,22 @@ else fi fi -_echo_nol "> \033[46m\033[30mSTATUS:\033[0m " if ! is_cpu_vulnerable 1; then - pstatus green 'NOT VULNERABLE' "your CPU vendor reported your CPU model as not vulnerable" + pvulnstatus CVE-2017-5753 OK "your CPU vendor reported your CPU model as not vulnerable" else - [ "$status" = 0 ] && pstatus yellow UNKNOWN - [ "$status" = 1 ] && pstatus red 'VULNERABLE' 'heuristic to be improved when official patches become available' - [ "$status" = 2 ] && pstatus green 'NOT VULNERABLE' 'heuristic to be improved when official patches become available' + case "$status" in + 0) pvulnstatus CVE-2017-5753 UNK "impossible to check ${vmlinux}";; + 1) pvulnstatus CVE-2017-5753 VULN 'heuristic to be improved when official patches become available';; + 2) pvulnstatus CVE-2017-5753 OK 'heuristic to be improved when official patches become available';; + esac fi ########### # VARIANT 2 -_echo -_echo "\033[1;34mCVE-2017-5715 [branch target injection] aka 'Spectre Variant 2'\033[0m" -_echo "* Mitigation 1" -_echo_nol "* Hardware (CPU microcode) support for mitigation: " +_info +_info "\033[1;34mCVE-2017-5715 [branch target injection] aka 'Spectre Variant 2'\033[0m" +_info "* Mitigation 1" +_info_nol "* Hardware (CPU microcode) support for mitigation: " if [ ! -e /dev/cpu/0/msr ]; then # try to load the module ourselves (and remember it so we can rmmod it afterwards) modprobe msr 2>/dev/null && insmod_msr=1 @@ -409,7 +475,7 @@ if [ "$insmod_msr" = 1 ]; then rmmod msr 2>/dev/null fi -_echo_nol "* Kernel support for IBRS: " +_info_nol "* Kernel support for IBRS: " if [ "$opt_live" = 1 ]; then if [ ! -e /sys/kernel/debug/sched_features ]; then # try to mount the debugfs hierarchy ourselves and remember it to umount afterwards @@ -437,7 +503,7 @@ if [ "$ibrs_supported" != 1 ]; then pstatus red NO fi -_echo_nol "* IBRS enabled for Kernel space: " +_info_nol "* IBRS enabled for Kernel space: " if [ "$opt_live" = 1 ]; then # 0 means disabled # 1 is enabled only for kernel space @@ -452,7 +518,7 @@ else pstatus blue N/A "not testable in offline mode" fi -_echo_nol "* IBRS enabled for User space: " +_info_nol "* IBRS enabled for User space: " if [ "$opt_live" = 1 ]; then case "$ibrs_enabled" in "") [ "$ibrs_supported" = 1 ] && pstatus yellow UNKNOWN || pstatus red NO;; @@ -464,8 +530,8 @@ else pstatus blue N/A "not testable in offline mode" fi -_echo "* Mitigation 2" -_echo_nol "* Kernel compiled with retpoline option: " +_info "* Mitigation 2" +_info_nol "* Kernel compiled with retpoline option: " # We check the RETPOLINE kernel options if [ -r "$opt_config" ]; then if grep -q '^CONFIG_RETPOLINE=y' "$opt_config"; then @@ -478,7 +544,7 @@ else pstatus yellow UNKNOWN "couldn't read your kernel configuration" fi -_echo_nol "* Kernel compiled with a retpoline-aware compiler: " +_info_nol "* Kernel compiled with a retpoline-aware compiler: " # Now check if the compiler used to compile the kernel knows how to insert retpolines in generated asm # For gcc, this is -mindirect-branch=thunk-extern (detected by the kernel makefiles) # See gcc commit https://github.com/hjl-tools/gcc/commit/23b517d4a67c02d3ef80b6109218f2aadad7bd79 @@ -514,30 +580,29 @@ else pstatus yellow UNKNOWN "couldn't find your kernel image or System.map" fi -_echo_nol "> \033[46m\033[30mSTATUS:\033[0m " if ! is_cpu_vulnerable 2; then - pstatus green 'NOT VULNERABLE' "your CPU vendor reported your CPU model as not vulnerable" + pvulnstatus CVE-2017-5715 OK "your CPU vendor reported your CPU model as not vulnerable" elif [ "$retpoline" = 1 -a "$retpoline_compiler" = 1 ]; then - pstatus green "NOT VULNERABLE" "retpoline mitigate the vulnerability" + pvulnstatus CVE-2017-5715 OK "retpoline mitigate the vulnerability" elif [ "$opt_live" = 1 ]; then if [ "$ibrs_enabled" = 1 -o "$ibrs_enabled" = 2 ]; then - pstatus green "NOT VULNERABLE" "IBRS mitigates the vulnerability" + pvulnstatus CVE-2017-5715 OK "IBRS mitigates the vulnerability" else - pstatus red VULNERABLE "IBRS hardware + kernel support OR kernel with retpoline are needed to mitigate the vulnerability" + pvulnstatus CVE-2017-5715 VULN "IBRS hardware + kernel support OR kernel with retpoline are needed to mitigate the vulnerability" fi else if [ "$ibrs_supported" = 1 ]; then - pstatus green "NOT VULNERABLE" "offline mode: IBRS will mitigate the vulnerability if enabled at runtime" + pvulnstatus CVE-2017-5715 OK "offline mode: IBRS will mitigate the vulnerability if enabled at runtime" else - pstatus red VULNERABLE "IBRS hardware + kernel support OR kernel with retpoline are needed to mitigate the vulnerability" + pvulnstatus CVE-2017-5715 VULN "IBRS hardware + kernel support OR kernel with retpoline are needed to mitigate the vulnerability" fi fi ########## # MELTDOWN -_echo -_echo "\033[1;34mCVE-2017-5754 [rogue data cache load] aka 'Meltdown' aka 'Variant 3'\033[0m" -_echo_nol "* Kernel supports Page Table Isolation (PTI): " +_info +_info "\033[1;34mCVE-2017-5754 [rogue data cache load] aka 'Meltdown' aka 'Variant 3'\033[0m" +_info_nol "* Kernel supports Page Table Isolation (PTI): " kpti_support=0 kpti_can_tell=0 if [ -n "$opt_config" ]; then @@ -575,7 +640,7 @@ else pstatus yellow UNKNOWN "couldn't read your kernel configuration nor System.map file" fi -_echo_nol "* PTI enabled and active: " +_info_nol "* PTI enabled and active: " if [ "$opt_live" = 1 ]; then if grep ^flags /proc/cpuinfo | grep -qw pti; then # vanilla PTI patch sets the 'pti' flag in cpuinfo @@ -606,23 +671,22 @@ if [ "$mounted_debugfs" = 1 ]; then umount /sys/kernel/debug fi -_echo_nol "> \033[46m\033[30mSTATUS:\033[0m " if ! is_cpu_vulnerable 3; then - pstatus green 'NOT VULNERABLE' "your CPU vendor reported your CPU model as not vulnerable" + pvulnstatus CVE-2017-5754 OK "your CPU vendor reported your CPU model as not vulnerable" elif [ "$opt_live" = 1 ]; then if [ "$kpti_enabled" = 1 ]; then - pstatus green "NOT VULNERABLE" "PTI mitigates the vulnerability" + pvulnstatus CVE-2017-5754 OK "PTI mitigates the vulnerability" else - pstatus red "VULNERABLE" "PTI is needed to mitigate the vulnerability" + pvulnstatus CVE-2017-5754 VULN "PTI is needed to mitigate the vulnerability" fi else if [ "$kpti_support" = 1 ]; then - pstatus green "NOT VULNERABLE" "offline mode: PTI will mitigate the vulnerability if enabled at runtime" + pvulnstatus CVE-2017-5754 OK "offline mode: PTI will mitigate the vulnerability if enabled at runtime" else - pstatus red "VULNERABLE" "PTI is needed to mitigate the vulnerability" + pvulnstatus CVE-2017-5754 VULN "PTI is needed to mitigate the vulnerability" fi fi -_echo +_info [ -n "$dumped_config" ] && rm -f "$dumped_config" |