From 443d9a2ae9819ac5c8219b2cc2bee7c70055bdae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lesimple?= Date: Sun, 21 Jan 2018 13:48:05 +0100 Subject: [PATCH] feat(ibpb): now also check for IBPB on variant 2 In addition to IBRS (and microcode support), IBPB must be used to mitigate variant 2, if retpoline support is not available. The vulnerability status of a system will be defined as "non vulnerable" if IBRS and IBPB are both enabled, or if IBPB is enabled with a value of 2 for RedHat kernels, see https://access.redhat.com/articles/3311301 --- spectre-meltdown-checker.sh | 165 +++++++++++++++++++++++++++--------- 1 file changed, 124 insertions(+), 41 deletions(-) diff --git a/spectre-meltdown-checker.sh b/spectre-meltdown-checker.sh index aa1b43b..74514e2 100755 --- a/spectre-meltdown-checker.sh +++ b/spectre-meltdown-checker.sh @@ -861,9 +861,10 @@ check_variant2() # this kernel has the /sys interface, trust it over everything sys_interface_available=1 else - _info "* Mitigation 1" - _info "* Hardware (CPU microcode) support for mitigation" - _info_nol "* The SPEC_CTRL MSR is available: " + _info "* Mitigation 1" + _info " * Hardware support (CPU microcode)" + _info " * Indirect Branch Restricted Speculation (IBRS)" + _info_nol " * SPEC_CTRL MSR is available: " if [ ! -e /dev/cpu/0/msr ]; then # try to load the module ourselves (and remember it so we can rmmod it afterwards) load_msr @@ -873,7 +874,8 @@ check_variant2() else # the new MSR 'SPEC_CTRL' is at offset 0x48 # here we use dd, it's the same as using 'rdmsr 0x48' but without needing the rdmsr tool - # if we get a read error, the MSR is not there + # if we get a read error, the MSR is not there. bs has to be 8 for msr + # skip=9 because 8*9=72=0x48 dd if=/dev/cpu/0/msr of=/dev/null bs=8 count=1 skip=9 2>/dev/null if [ $? -eq 0 ]; then pstatus green YES @@ -882,23 +884,20 @@ check_variant2() fi fi - unload_msr - - # CPUID test - _info_nol "* The SPEC_CTRL CPUID feature bit is set: " + _info_nol " * CPU indicates IBRS capability: " if [ ! -e /dev/cpu/0/cpuid ]; then # try to load the module ourselves (and remember it so we can rmmod it afterwards) load_cpuid fi if [ ! -e /dev/cpu/0/cpuid ]; then - pstatus yellow UNKNOWN "couldn't read /dev/cpu/0/cpuidr, is cpuid support enabled in your kernel?" + pstatus yellow UNKNOWN "couldn't read /dev/cpu/0/cpuid, is cpuid support enabled in your kernel?" else # from kernel src: { X86_FEATURE_SPEC_CTRL, CPUID_EDX,26, 0x00000007, 0 }, if [ "$opt_verbose" -ge 3 ]; then dd if=/dev/cpu/0/cpuid bs=16 skip=7 iflag=skip_bytes count=1 >/dev/null 2>/dev/null _debug "cpuid: reading leaf7 of cpuid on cpu0, ret=$?" - _debug "cpuid: leaf7 eax-ebx-ecd-edx: "$(dd if=/dev/cpu/0/cpuid bs=16 skip=7 iflag=skip_bytes count=1 2>/dev/null | od -x -A n) - _debug "cpuid: leaf7 edx higher-half is: "$(dd if=/dev/cpu/0/cpuid bs=16 skip=7 iflag=skip_bytes count=1 2>/dev/null | dd bs=1 skip=15 count=1 2>/dev/null | od -x -A n) + _debug "cpuid: leaf7 eax-ebx-ecx-edx: "$(dd if=/dev/cpu/0/cpuid bs=16 skip=7 iflag=skip_bytes count=1 2>/dev/null | od -x -A n) + _debug "cpuid: leaf7 edx higher byte is: "$(dd if=/dev/cpu/0/cpuid bs=16 skip=7 iflag=skip_bytes count=1 2>/dev/null | dd bs=1 skip=15 count=1 2>/dev/null | od -x -A n) fi # getting high byte of edx on leaf7 of cpuinfo in decimal edx_hb=$(dd if=/dev/cpu/0/cpuid bs=16 skip=7 iflag=skip_bytes count=1 2>/dev/null | dd bs=1 skip=15 count=1 2>/dev/null | od -t u -A n | awk '{print $1}') @@ -906,48 +905,102 @@ check_variant2() edx_bit26=$(( edx_hb & 8 )) _debug "cpuid: edx_bit26=$edx_bit26" if [ "$edx_bit26" -eq 8 ]; then - pstatus green YES + pstatus green YES "SPEC_CTRL feature bit" + cpuid_spec_ctrl=1 else pstatus red NO fi fi - unload_cpuid # hardware support according to kernel if [ "$opt_verbose" -ge 2 ]; then - _verbose_nol "* The kernel has set the spec_ctrl flag in cpuinfo: " + # the spec_ctrl flag in cpuinfo is set if and only if the kernel sees + # that the spec_ctrl cpuinfo bit set. we already check that ourselves above + # but let's check it anyway (in verbose mode only) + _verbose_nol " * Kernel has set the spec_ctrl flag in cpuinfo: " if [ "$opt_live" = 1 ]; then if grep ^flags /proc/cpuinfo | grep -qw spec_ctrl; then pstatus green YES else - pstatus red NO + pstatus blue NO fi else pstatus blue N/A "not testable in offline mode" fi fi - _info_nol "* Kernel support for IBRS: " + # IBPB + _info " * Indirect Branch Prediction Barrier (IBPB)" + _info_nol " * PRED_CMD MSR is available: " + if [ ! -e /dev/cpu/0/msr ]; then + pstatus yellow UNKNOWN "couldn't read /dev/cpu/0/msr, is msr support enabled in your kernel?" + else + # the new MSR 'PRED_CTRL' is at offset 0x49, write-only + # here we use dd, it's the same as using 'wrmsr 0x49 0' but without needing the wrmsr tool + # if we get a write error, the MSR is not there + $echo_cmd -ne "\0\0\0\0\0\0\0\0" | dd of=/dev/cpu/0/msr bs=8 count=1 seek=73 oflag=seek_bytes 2>/dev/null + if [ $? -eq 0 ]; then + pstatus green YES + else + pstatus red NO + fi + fi + + _info_nol " * CPU indicates IBPB capability: " + if [ ! -e /dev/cpu/0/cpuid ]; then + pstatus yellow UNKNOWN "couldn't read /dev/cpu/0/cpuidr, is cpuid support enabled in your kernel?" + else + # CPUID EAX=0x80000008, ECX=0x00 return EBX[12] indicates support for just IBPB. + if [ "$opt_verbose" -ge 3 ]; then + dd if=/dev/cpu/0/cpuid bs=16 skip=2147483656 iflag=skip_bytes count=1 >/dev/null 2>/dev/null + _debug "cpuid: reading leaf80000008 of cpuid on cpu0, ret=$?" + _debug "cpuid: leaf80000008 eax-ebx-ecx-edx: "$(dd if=/dev/cpu/0/cpuid bs=16 skip=2147483656 iflag=skip_bytes count=1 2>/dev/null | od -x -A n) + _debug "cpuid: leaf80000008 ebx 3rd byte is: "$(dd if=/dev/cpu/0/cpuid bs=16 skip=2147483656 iflag=skip_bytes count=1 2>/dev/null | dd bs=1 skip=5 count=1 2>/dev/null | od -x -A n) + fi + # getting high byte of edx on leaf7 of cpuinfo in decimal + ebx_b3=$(dd if=/dev/cpu/0/cpuid bs=16 skip=2147483656 iflag=skip_bytes count=1 2>/dev/null | dd bs=1 skip=5 count=1 2>/dev/null | od -t u -A n | awk '{print $1}') + _debug "cpuid: leaf80000008 ebx 3rd byte: $ebx_b3 (decimal)" + ebx_bit12=$(( ebx_b3 & 16 )) + _debug "cpuid: ebx_bit12=$ebx_bit12" + if [ "$ebx_bit12" -eq 16 ]; then + pstatus green YES "IBPB_SUPPORT feature bit" + cpuid_ibpb=1 + elif [ "$cpuid_spec_ctrl" = 1 ]; then + pstatus green YES "SPEC_CTRL feature bit" + else + pstatus red NO + fi + fi + + _info_nol " * Kernel is compiled with IBRS/IBPB support: " ibrs_can_tell=0 + if [ "$opt_live" = 1 ]; then ibrs_can_tell=1 mount_debugfs - for ibrs_file in \ - /sys/kernel/debug/ibrs_enabled \ - /sys/kernel/debug/x86/ibrs_enabled \ - /proc/sys/kernel/ibrs_enabled; do - if [ -e "$ibrs_file" ]; then + for dir in \ + /sys/kernel/debug \ + /sys/kernel/debug/x86 \ + /proc/sys/kernel; do + if [ -e "$dir/ibrs_enabled" ]; then # if the file is there, we have IBRS compiled-in # /sys/kernel/debug/ibrs_enabled: vanilla # /sys/kernel/debug/x86/ibrs_enabled: RedHat (see https://access.redhat.com/articles/3311301) # /proc/sys/kernel/ibrs_enabled: OpenSUSE tumbleweed pstatus green YES + ibrs_knob_dir=$dir ibrs_supported=1 - ibrs_enabled=$(cat "$ibrs_file" 2>/dev/null) - _debug "ibrs: found $ibrs_file=$ibrs_enabled" + ibrs_enabled=$(cat "$dir/ibrs_enabled" 2>/dev/null) + _debug "ibrs: found $dir/ibrs_enabled=$ibrs_enabled" + if [ -e "$dir/ibpb_enabled" ]; then + ibpb_enabled=$(cat "$dir/ibpb_enabled" 2>/dev/null) + _debug "ibpb: found $dir/ibpb_enabled=$ibpb_enabled" + else + _debug "ibpb: no ibpb_enabled file in $dir" + fi break else - _debug "ibrs: file $ibrs_file doesn't exist" + _debug "ibrs: $dir/ibrs_enabled file doesn't exist" fi done # on some newer kernels, the spec_ctrl_ibrs flag in /proc/cpuinfo @@ -960,6 +1013,7 @@ check_variant2() ibrs_supported=1 # enabled=2 -> kernel & user ibrs_enabled=2 + # XXX and what about ibpb ? fi fi fi @@ -980,33 +1034,60 @@ check_variant2() fi fi - _info_nol "* IBRS enabled for Kernel space: " + _info " * Currently enabled features" + _info_nol " * IBRS enabled for Kernel space: " if [ "$opt_live" = 1 ]; then - # 0 means disabled - # 1 is enabled only for kernel space - # 2 is enabled for kernel and user space - case "$ibrs_enabled" in - "") [ "$ibrs_supported" = 1 ] && pstatus yellow UNKNOWN || pstatus red NO;; - 0) pstatus red NO;; - 1 | 2) pstatus green YES;; - *) pstatus yellow UNKNOWN;; - esac + if [ "$ibpb_enabled" = 2 ]; then + # if ibpb=2, ibrs is forcefully=0 + pstatus blue NO "IBPB used instead of IBRS in all kernel entrypoints" + else + # 0 means disabled + # 1 is enabled only for kernel space + # 2 is enabled for kernel and user space + case "$ibrs_enabled" in + "") [ "$ibrs_supported" = 1 ] && pstatus yellow UNKNOWN || pstatus red NO;; + 0) pstatus red NO "echo 1 > $ibrs_knob_dir/ibrs_enabled";; + 1 | 2) pstatus green YES;; + *) pstatus yellow UNKNOWN;; + esac + fi else pstatus blue N/A "not testable in offline mode" fi - _info_nol "* IBRS enabled for User space: " + _info_nol " * IBRS enabled for User space: " if [ "$opt_live" = 1 ]; then - case "$ibrs_enabled" in + if [ "$ibpb_enabled" = 2 ]; then + # if ibpb=2, ibrs is forcefully=0 + pstatus blue NO "IBPB used instead of IBRS in all kernel entrypoints" + else + case "$ibrs_enabled" in + "") [ "$ibrs_supported" = 1 ] && pstatus yellow UNKNOWN || pstatus red NO;; + 0 | 1) pstatus red NO "echo 2 > $ibrs_knob_dir/ibrs_enabled";; + 2) pstatus green YES;; + *) pstatus yellow UNKNOWN;; + esac + fi + else + pstatus blue N/A "not testable in offline mode" + fi + + _info_nol " * IBPB enabled: " + if [ "$opt_live" = 1 ]; then + case "$ibpb_enabled" in "") [ "$ibrs_supported" = 1 ] && pstatus yellow UNKNOWN || pstatus red NO;; - 0 | 1) pstatus red NO;; - 2) pstatus green YES;; + 0) pstatus red NO "echo 1 > $ibrs_knob_dir/ibpb_enabled";; + 1) pstatus green YES;; + 2) pstatus green YES "IBPB used instead of IBRS in all kernel entrypoints";; *) pstatus yellow UNKNOWN;; esac else pstatus blue N/A "not testable in offline mode" fi + unload_msr + unload_cpuid + _info "* Mitigation 2" _info_nol "* Kernel compiled with retpoline option: " # We check the RETPOLINE kernel options @@ -1068,14 +1149,16 @@ check_variant2() if [ "$retpoline" = 1 -a "$retpoline_compiler" = 1 ]; then pvulnstatus CVE-2017-5715 OK "retpoline mitigate the vulnerability" elif [ "$opt_live" = 1 ]; then - if [ "$ibrs_enabled" = 1 -o "$ibrs_enabled" = 2 ]; then - pvulnstatus CVE-2017-5715 OK "IBRS mitigates the vulnerability" + if [ "$ibrs_enabled" = 1 -o "$ibrs_enabled" = 2 ] && [ "$ibpb_enabled" = 1 ]; then + pvulnstatus CVE-2017-5715 OK "IBRS/IBPB are mitigating the vulnerability" + elif [ "$ibpb_enabled" = 2 ]; then + pvulnstatus CVE-2017-5715 OK "Full IBPB is mitigating the vulnerability" else 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 - pvulnstatus CVE-2017-5715 OK "offline mode: IBRS will mitigate the vulnerability if enabled at runtime" + pvulnstatus CVE-2017-5715 OK "offline mode: IBRS/IBPB will mitigate the vulnerability if enabled at runtime" elif [ "$ibrs_can_tell" = 1 ]; then pvulnstatus CVE-2017-5715 VULN "IBRS hardware + kernel support OR kernel with retpoline are needed to mitigate the vulnerability" else