feat: rework Spectre V2 mitigations detection w/ latest vanilla & Red Hat 7 kernels

This commit is contained in:
Stéphane Lesimple 2018-04-05 00:00:07 +02:00
parent 07484d0ea7
commit de02dad909
1 changed files with 138 additions and 68 deletions

View File

@ -1831,11 +1831,18 @@ check_variant2_linux()
sys_interface_available=1 sys_interface_available=1
fi fi
if [ "$opt_sysfs_only" != 1 ]; then if [ "$opt_sysfs_only" != 1 ]; then
_info "* Mitigation 1" _info "* Mitigation 1"
_info_nol " * Kernel is compiled with IBRS/IBPB support: "
ibrs_can_tell=0 ibrs_can_tell=0
ibrs_supported=''
ibrs_enabled=''
ibpb_can_tell=0
ibpb_supported=''
ibpb_enabled=''
if [ "$opt_live" = 1 ]; then 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 ibrs_can_tell=1
mount_debugfs mount_debugfs
for dir in \ for dir in \
@ -1847,17 +1854,18 @@ check_variant2_linux()
# /sys/kernel/debug/ibrs_enabled: vanilla # /sys/kernel/debug/ibrs_enabled: vanilla
# /sys/kernel/debug/x86/ibrs_enabled: Red Hat (see https://access.redhat.com/articles/3311301) # /sys/kernel/debug/x86/ibrs_enabled: Red Hat (see https://access.redhat.com/articles/3311301)
# /proc/sys/kernel/ibrs_enabled: OpenSUSE tumbleweed # /proc/sys/kernel/ibrs_enabled: OpenSUSE tumbleweed
pstatus green YES specex_knob_dir=$dir
ibrs_knob_dir=$dir ibrs_supported="$dir/ibrs_enabled exists"
ibrs_supported=1
ibrs_enabled=$(cat "$dir/ibrs_enabled" 2>/dev/null) ibrs_enabled=$(cat "$dir/ibrs_enabled" 2>/dev/null)
_debug "ibrs: found $dir/ibrs_enabled=$ibrs_enabled" _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 [ -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) ibpb_enabled=$(cat "$dir/ibpb_enabled" 2>/dev/null)
_debug "ibpb: found $dir/ibpb_enabled=$ibpb_enabled" _debug "ibpb: found $dir/ibpb_enabled=$ibpb_enabled"
else else
ibpb_enabled=-1 _debug "ibpb: $dir/ibpb_enabled file doesn't exist"
_debug "ibpb: no ibpb_enabled file in $dir"
fi fi
break break
else else
@ -1868,42 +1876,93 @@ check_variant2_linux()
# is set when ibrs has been administratively enabled (usually from cmdline) # is set when ibrs has been administratively enabled (usually from cmdline)
# which in that case means ibrs is supported *and* enabled for kernel & user # which in that case means ibrs is supported *and* enabled for kernel & user
# as per the ibrs patch series v3 # 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 if grep ^flags "$procfs/cpuinfo" | grep -qw spec_ctrl_ibrs; then
_debug "ibrs: found spec_ctrl_ibrs flag in $procfs/cpuinfo" _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 # enabled=2 -> kernel & user
ibrs_enabled=2 ibrs_enabled=2
# XXX and what about ibpb ? # XXX and what about ibpb ?
fi fi
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 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 ibrs_can_tell=1
if grep -q spec_ctrl "$opt_map"; then if grep -q spec_ctrl "$opt_map"; then
pstatus green YES ibrs_supported="found spec_ctrl in symbols file"
ibrs_supported=1
_debug "ibrs: found '*spec_ctrl*' symbol in $opt_map" _debug "ibrs: found '*spec_ctrl*' symbol in $opt_map"
fi fi
fi fi
if [ "$ibrs_supported" != 1 ]; then # recent (4.15) vanilla kernels have IBPB but not IBRS, and without the debugfs tunables of Red Hat
check_redhat_canonical_spectre # we can detect it directly in the image
if [ "$redhat_canonical_spectre" = 1 ]; then if [ -z "$ibpb_supported" ] && [ -n "$kernel" ]; then
pstatus green YES "Red Hat/Ubuntu patch" if ! which "${opt_arch_prefix}strings" >/dev/null 2>&1; then
ibrs_supported=1 :
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
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 if [ "$ibrs_can_tell" = 1 ]; then
pstatus yellow NO pstatus yellow NO
else else
# if we're in offline mode without System.map, we can't really know # 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 fi
else
pstatus green YES "$ibrs_supported"
fi fi
_info " * Currently enabled features" _info_nol " * IBRS enabled and active: "
_info_nol " * IBRS enabled for Kernel space: "
if [ "$opt_live" = 1 ]; then if [ "$opt_live" = 1 ]; then
if [ "$ibpb_enabled" = 2 ]; then if [ "$ibpb_enabled" = 2 ]; then
# if ibpb=2, ibrs is forcefully=0 # if ibpb=2, ibrs is forcefully=0
@ -1912,53 +1971,41 @@ check_variant2_linux()
# 0 means disabled # 0 means disabled
# 1 is enabled only for kernel space # 1 is enabled only for kernel space
# 2 is enabled for kernel and user space # 2 is enabled for kernel and user space
# 3 is enabled
case "$ibrs_enabled" in case "$ibrs_enabled" in
"")
if [ "$ibrs_supported" = 1 ]; then
pstatus yellow UNKNOWN
else
pstatus yellow NO
fi
;;
0) 0)
pstatus yellow NO if [ "$ibrs_fw_enabled" = 1 ]; then
_verbose " - To enable, \`echo 1 > $ibrs_knob_dir/ibrs_enabled' as root. If you don't have hardware support, you'll get an error." pstatus green YES "for firmware code"
;;
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
else else
pstatus yellow NO 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 fi
;; ;;
0 | 1) 1) pstatus green YES "for kernel space";;
pstatus yellow NO 2) pstatus green YES "for both kernel and user space";;
_verbose " - To enable, \`echo 2 > $ibrs_knob_dir/ibrs_enabled' as root. If you don't have hardware support, you'll get an error." 3) if [ "$ibrs_fw_enabled" = 1 ]; then pstatus green YES "for kernel and firmware code"; else pstatus green YES; fi;;
;; *) pstatus yellow UNKNOWN;;
2) pstatus green YES;;
*) pstatus yellow UNKNOWN;;
esac esac
fi fi
else else
pstatus blue N/A "not testable in offline mode" pstatus blue N/A "not testable in offline mode"
fi 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 if [ "$opt_live" = 1 ]; then
case "$ibpb_enabled" in case "$ibpb_enabled" in
"") "")
@ -1970,7 +2017,7 @@ check_variant2_linux()
;; ;;
0) 0)
pstatus yellow NO 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;; 1) pstatus green YES;;
2) pstatus green YES "IBPB used instead of IBRS in all kernel entrypoints";; 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" pstatus yellow UNKNOWN "couldn't read your kernel configuration"
fi 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 # 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) # For gcc, this is -mindirect-branch=thunk-extern (detected by the kernel makefiles)
# See gcc commit https://github.com/hjl-tools/gcc/commit/23b517d4a67c02d3ef80b6109218f2aadad7bd79 # 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 # 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 # *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 [ "$opt_live" = 1 ] && [ -e "/sys/devices/system/cpu/vulnerabilities/spectre_v2" ]; then
if grep -qw Minimal /sys/devices/system/cpu/vulnerabilities/spectre_v2; then if grep -qwi retpoline /sys/devices/system/cpu/vulnerabilities/spectre_v2; then
pstatus yellow NO "kernel reports minimal retpoline compilation" if grep -qwi minimal /sys/devices/system/cpu/vulnerabilities/spectre_v2; then
elif grep -qw Full /sys/devices/system/cpu/vulnerabilities/spectre_v2; then pstatus yellow NO "kernel reports minimal retpoline compilation"
retpoline_compiler=1 else
pstatus green YES "kernel reports full retpoline compilation" retpoline_compiler=1
pstatus green YES "kernel reports full retpoline compilation"
fi
else else
if [ "$retpoline" = 1 ]; then if [ "$retpoline" = 1 ]; then
pstatus yellow UNKNOWN pstatus yellow UNKNOWN
@ -2085,6 +2137,22 @@ check_variant2_linux()
pstatus yellow NO pstatus yellow NO
fi fi
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 elif [ "$sys_interface_available" = 0 ]; then
# we have no sysfs but were asked to use it only! # we have no sysfs but were asked to use it only!
msg="/sys vulnerability interface use forced, but it's not available!" 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" pvulnstatus $cve OK "your CPU vendor reported your CPU model as not vulnerable"
elif [ -z "$msg" ]; then elif [ -z "$msg" ]; then
# if msg is empty, sysfs check didn't fill it, rely on our own test # 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" pvulnstatus $cve OK "retpoline mitigates the vulnerability"
elif [ -n "$bp_harden" ]; then elif [ -n "$bp_harden" ]; then
pvulnstatus $cve OK 'branch predictor hardening mitigates the vulnerability for ARM' 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" pvulnstatus $cve VULN "IBRS hardware + kernel support OR kernel with retpoline are needed to mitigate the vulnerability"
fi fi
else 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" 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 elif [ "$ibrs_can_tell" = 1 ]; then
pvulnstatus $cve VULN "IBRS hardware + kernel support OR kernel with retpoline are needed to mitigate the vulnerability" pvulnstatus $cve VULN "IBRS hardware + kernel support OR kernel with retpoline are needed to mitigate the vulnerability"
else else
@ -2251,7 +2321,7 @@ check_variant3_linux()
fi fi
mount_debugfs mount_debugfs
_info_nol "* PTI enabled and active: " _info_nol " * PTI enabled and active: "
if [ "$opt_live" = 1 ]; then if [ "$opt_live" = 1 ]; then
dmesg_grep="Kernel/User page tables isolation: enabled" dmesg_grep="Kernel/User page tables isolation: enabled"
dmesg_grep="$dmesg_grep|Kernel page table isolation enabled" dmesg_grep="$dmesg_grep|Kernel page table isolation enabled"
@ -2291,7 +2361,7 @@ check_variant3_linux()
pstatus yellow NO pstatus yellow NO
fi fi
else 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 fi
# no security impact but give a hint to the user in verbose mode # no security impact but give a hint to the user in verbose mode
@ -2413,7 +2483,7 @@ check_variant3_bsd()
pstatus green YES pstatus green YES
fi fi
_info_nol "* PTI enabled and active: " _info_nol " * PTI enabled and active: "
if [ "$kpti_enabled" = 1 ]; then if [ "$kpti_enabled" = 1 ]; then
pstatus green YES pstatus green YES
else else