From de02dad90945930e0ec489dca6257fd59e060b55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lesimple?= Date: Thu, 5 Apr 2018 00:00:07 +0200 Subject: [PATCH] feat: rework Spectre V2 mitigations detection w/ latest vanilla & Red Hat 7 kernels --- spectre-meltdown-checker.sh | 206 ++++++++++++++++++++++++------------ 1 file changed, 138 insertions(+), 68 deletions(-) diff --git a/spectre-meltdown-checker.sh b/spectre-meltdown-checker.sh index 4285470..836bf7d 100755 --- a/spectre-meltdown-checker.sh +++ b/spectre-meltdown-checker.sh @@ -1831,11 +1831,18 @@ check_variant2_linux() sys_interface_available=1 fi if [ "$opt_sysfs_only" != 1 ]; then - _info "* Mitigation 1" - _info_nol " * Kernel is compiled with IBRS/IBPB support: " + _info "* Mitigation 1" + ibrs_can_tell=0 + ibrs_supported='' + ibrs_enabled='' + ibpb_can_tell=0 + ibpb_supported='' + ibpb_enabled='' if [ "$opt_live" = 1 ]; then + # in live mode, we can check for the ibrs_enabled file in debugfs + # all versions of the patches have it (NOT the case of IBPB or KPTI) ibrs_can_tell=1 mount_debugfs for dir in \ @@ -1847,17 +1854,18 @@ check_variant2_linux() # /sys/kernel/debug/ibrs_enabled: vanilla # /sys/kernel/debug/x86/ibrs_enabled: Red Hat (see https://access.redhat.com/articles/3311301) # /proc/sys/kernel/ibrs_enabled: OpenSUSE tumbleweed - pstatus green YES - ibrs_knob_dir=$dir - ibrs_supported=1 + specex_knob_dir=$dir + ibrs_supported="$dir/ibrs_enabled exists" ibrs_enabled=$(cat "$dir/ibrs_enabled" 2>/dev/null) _debug "ibrs: found $dir/ibrs_enabled=$ibrs_enabled" + # if ibrs_enabled is there, ibpb_enabled will be in the same dir if [ -e "$dir/ibpb_enabled" ]; then + # if the file is there, we have IBPB compiled-in (see note above for IBRS) + ibpb_supported="$dir/ibpb_enabled exists" ibpb_enabled=$(cat "$dir/ibpb_enabled" 2>/dev/null) _debug "ibpb: found $dir/ibpb_enabled=$ibpb_enabled" else - ibpb_enabled=-1 - _debug "ibpb: no ibpb_enabled file in $dir" + _debug "ibpb: $dir/ibpb_enabled file doesn't exist" fi break else @@ -1868,42 +1876,93 @@ check_variant2_linux() # is set when ibrs has been administratively enabled (usually from cmdline) # which in that case means ibrs is supported *and* enabled for kernel & user # as per the ibrs patch series v3 - if [ "$ibrs_supported" = 0 ]; then + if [ -z "$ibrs_supported" ]; then if grep ^flags "$procfs/cpuinfo" | grep -qw spec_ctrl_ibrs; then _debug "ibrs: found spec_ctrl_ibrs flag in $procfs/cpuinfo" - ibrs_supported=1 + ibrs_supported="spec_ctrl_ibrs flag in $procfs/cpuinfo" # enabled=2 -> kernel & user ibrs_enabled=2 # XXX and what about ibpb ? fi fi + if [ -e "/sys/devices/system/cpu/vulnerabilities/spectre_v2" ]; 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 + _debug "ibpb: found enabled in sysfs" + ibpb_supported='IBPB found enabled in sysfs' + 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 + _debug "ibrs: found IBRS_FW in sysfs" + 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 'Indirect Branch Restricted Speculation' "/sys/devices/system/cpu/vulnerabilities/spectre_v2"; then + _debug "ibrs: found IBRS in sysfs" + ibrs_supported='found IBRS in sysfs' + ibrs_enabled=3 + fi + fi + # in live mode, if ibrs or ibpb is supported and we didn't find these are enabled, then they are not + [ -n "$ibrs_supported" ] && [ -z "$ibrs_enabled" ] && ibrs_enabled=0 + [ -n "$ibpb_supported" ] && [ -z "$ibpb_enabled" ] && ibpb_enabled=0 fi - if [ "$ibrs_supported" != 1 ] && [ -n "$opt_map" ]; then + if [ -z "$ibrs_supported" ]; then + check_redhat_canonical_spectre + if [ "$redhat_canonical_spectre" = 1 ]; then + ibrs_supported="Red Hat/Ubuntu variant" + ibpb_supported="Red Hat/Ubuntu variant" + fi + fi + if [ -z "$ibrs_supported" ] && [ -n "$kernel" ]; then + if ! which "${opt_arch_prefix}strings" >/dev/null 2>&1; then + : + else + ibrs_can_tell=1 + ibrs_supported=$("${opt_arch_prefix}strings" "$kernel" | grep -Fw -e ', IBRS_FW' | head -1) + if [ -n "$ibrs_supported" ]; then + _debug "ibrs: found ibrs evidence in kernel image ($ibrs_supported)" + ibrs_supported="found '$ibrs_supported' in kernel image" + fi + fi + fi + if [ -z "$ibrs_supported" ] && [ -n "$opt_map" ]; then ibrs_can_tell=1 if grep -q spec_ctrl "$opt_map"; then - pstatus green YES - ibrs_supported=1 + ibrs_supported="found spec_ctrl in symbols file" _debug "ibrs: found '*spec_ctrl*' symbol in $opt_map" fi fi - if [ "$ibrs_supported" != 1 ]; then - check_redhat_canonical_spectre - if [ "$redhat_canonical_spectre" = 1 ]; then - pstatus green YES "Red Hat/Ubuntu patch" - ibrs_supported=1 + # 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 + : + else + ibpb_can_tell=1 + ibpb_supported=$("${opt_arch_prefix}strings" "$kernel" | grep -Fw -e 'ibpb' -e ', IBPB' | head -1) + if [ -n "$ibpb_supported" ]; then + _debug "ibpb: found ibpb evidence in kernel image ($ibpb_supported)" + ibpb_supported="found '$ibpb_supported' in kernel image" + fi fi fi - if [ "$ibrs_supported" != 1 ]; then + + _info_nol " * Kernel is compiled with IBRS support: " + if [ -z "$ibrs_supported" ]; then if [ "$ibrs_can_tell" = 1 ]; then pstatus yellow NO else # if we're in offline mode without System.map, we can't really know - pstatus yellow UNKNOWN "in offline mode, we need System.map to be able to tell" + pstatus yellow UNKNOWN "in offline mode, we need the kernel image and System.map to be able to tell" fi + else + pstatus green YES "$ibrs_supported" fi - _info " * Currently enabled features" - _info_nol " * IBRS enabled for Kernel space: " + _info_nol " * IBRS enabled and active: " if [ "$opt_live" = 1 ]; then if [ "$ibpb_enabled" = 2 ]; then # if ibpb=2, ibrs is forcefully=0 @@ -1912,53 +1971,41 @@ check_variant2_linux() # 0 means disabled # 1 is enabled only for kernel space # 2 is enabled for kernel and user space + # 3 is enabled case "$ibrs_enabled" in - "") - if [ "$ibrs_supported" = 1 ]; then - pstatus yellow UNKNOWN - else - pstatus yellow NO - fi - ;; 0) - pstatus yellow NO - _verbose " - To enable, \`echo 1 > $ibrs_knob_dir/ibrs_enabled' as root. If you don't have hardware support, you'll get an error." - ;; - 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: " - if [ "$opt_live" = 1 ]; then - 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 - "") - if [ "$ibrs_supported" = 1 ]; then - pstatus yellow UNKNOWN + if [ "$ibrs_fw_enabled" = 1 ]; then + pstatus green YES "for firmware code" else pstatus yellow NO + if [ -e "$specex_knob_dir/ibrs_enabled" ]; then + _verbose " - To enable, \`echo 1 > $specex_knob_dir/ibrs_enabled' as root. If you don't have hardware support, you'll get an error." + fi fi ;; - 0 | 1) - pstatus yellow NO - _verbose " - To enable, \`echo 2 > $ibrs_knob_dir/ibrs_enabled' as root. If you don't have hardware support, you'll get an error." - ;; - 2) pstatus green YES;; - *) pstatus yellow UNKNOWN;; + 1) pstatus green YES "for kernel space";; + 2) pstatus green YES "for both kernel and user space";; + 3) if [ "$ibrs_fw_enabled" = 1 ]; then pstatus green YES "for kernel and firmware code"; else pstatus green YES; fi;; + *) pstatus yellow UNKNOWN;; esac fi else pstatus blue N/A "not testable in offline mode" fi - _info_nol " * IBPB enabled: " + _info_nol " * Kernel is compiled with IBPB support: " + if [ -z "$ibpb_supported" ]; then + if [ "$ibpb_can_tell" = 1 ]; then + pstatus yellow NO + else + # if we're in offline mode without System.map, we can't really know + pstatus yellow UNKNOWN "in offline mode, we need the kernel image to be able to tell" + fi + else + pstatus green YES "$ibpb_supported" + fi + + _info_nol " * IBPB enabled and active: " if [ "$opt_live" = 1 ]; then case "$ibpb_enabled" in "") @@ -1970,7 +2017,7 @@ check_variant2_linux() ;; 0) pstatus yellow NO - _verbose " - To enable, \`echo 1 > $ibrs_knob_dir/ibpb_enabled' as root. If you don't have hardware support, you'll get an error." + _verbose " - To enable, \`echo 1 > $specex_knob_dir/ibpb_enabled' as root. If you don't have hardware support, you'll get an error." ;; 1) pstatus green YES;; 2) pstatus green YES "IBPB used instead of IBRS in all kernel entrypoints";; @@ -2021,18 +2068,23 @@ check_variant2_linux() pstatus yellow UNKNOWN "couldn't read your kernel configuration" fi - _info_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 # In latest retpoline LKML patches, the noretpoline_setup symbol exists only if CONFIG_RETPOLINE is set # *AND* if the compiler is retpoline-compliant, so look for that symbol + # + # 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 -qw Minimal /sys/devices/system/cpu/vulnerabilities/spectre_v2; then - pstatus yellow NO "kernel reports minimal retpoline compilation" - elif grep -qw Full /sys/devices/system/cpu/vulnerabilities/spectre_v2; then - retpoline_compiler=1 - pstatus green YES "kernel reports full retpoline compilation" + if grep -qwi retpoline /sys/devices/system/cpu/vulnerabilities/spectre_v2; then + if grep -qwi minimal /sys/devices/system/cpu/vulnerabilities/spectre_v2; then + pstatus yellow NO "kernel reports minimal retpoline compilation" + else + retpoline_compiler=1 + pstatus green YES "kernel reports full retpoline compilation" + fi else if [ "$retpoline" = 1 ]; then pstatus yellow UNKNOWN @@ -2085,6 +2137,22 @@ check_variant2_linux() pstatus yellow NO fi fi + + # only Red Hat has a tunable to disable it on runtime + if [ "$opt_live" = 1 ]; then + if [ -e "$specex_knob_dir/retp_enabled" ]; then + retp_enabled=$(cat "$specex_knob_dir/retp_enabled" 2>/dev/null) + _debug "retpoline: found $specex_knob_dir/retp_enabled=$retp_enabled" + _info_nol " * Retpoline is enabled: " + if [ "$retp_enabled" = 1 ]; then + pstatus green YES + else + pstatus yellow NO + _verbose " - To enable, \`echo 1 > $specex_knob_dir/retp_enabled' as root." + 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!" @@ -2097,7 +2165,7 @@ check_variant2_linux() 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 [ "$retpoline" = 1 ] && [ "$retpoline_compiler" = 1 ]; then + if [ "$retpoline" = 1 ] && [ "$retpoline_compiler" = 1 ] && [ "$retp_enabled" != 0 ]; then pvulnstatus $cve OK "retpoline mitigates the vulnerability" elif [ -n "$bp_harden" ]; then pvulnstatus $cve OK 'branch predictor hardening mitigates the vulnerability for ARM' @@ -2115,8 +2183,10 @@ check_variant2_linux() pvulnstatus $cve VULN "IBRS hardware + kernel support OR kernel with retpoline are needed to mitigate the vulnerability" fi else - if [ "$ibrs_supported" = 1 ]; then + if [ -n "$ibrs_supported" ]; then pvulnstatus $cve OK "offline mode: IBRS/IBPB will mitigate the vulnerability if enabled at runtime" + elif [ "$retpoline" = 1 ]; then + pvulnstatus $cve OK "retpoline mitigates the vulnerability" elif [ "$ibrs_can_tell" = 1 ]; then pvulnstatus $cve VULN "IBRS hardware + kernel support OR kernel with retpoline are needed to mitigate the vulnerability" else @@ -2251,7 +2321,7 @@ check_variant3_linux() fi mount_debugfs - _info_nol "* PTI enabled and active: " + _info_nol " * PTI enabled and active: " if [ "$opt_live" = 1 ]; then dmesg_grep="Kernel/User page tables isolation: enabled" dmesg_grep="$dmesg_grep|Kernel page table isolation enabled" @@ -2291,7 +2361,7 @@ check_variant3_linux() pstatus yellow NO fi else - pstatus blue N/A "can't verify if PTI is enabled in offline mode" + pstatus blue N/A "not testable in offline mode" fi # no security impact but give a hint to the user in verbose mode @@ -2413,7 +2483,7 @@ check_variant3_bsd() pstatus green YES fi - _info_nol "* PTI enabled and active: " + _info_nol " * PTI enabled and active: " if [ "$kpti_enabled" = 1 ]; then pstatus green YES else