1
0
mirror of https://github.com/speed47/spectre-meltdown-checker synced 2024-11-06 22:23:41 +01:00

feat(variant1): detect vanilla mitigation

Implement detection of mitigation for Variant 1 that is
being pushed on vanilla kernel.
Current name of the patch:
"spectre variant1 mitigations for tip/x86/pti" (v6)
Also detect some distros that already backported this
patch without modifying the vulnerabilities sysfs hierarchy.
This detection is more reliable than the LFENCE one, trust
it and skip the LFENCE heuristic if a match is found.
This commit is contained in:
Stéphane Lesimple 2018-01-30 12:13:39 +01:00
parent 6e544d6055
commit e05ec5c85f

View File

@ -1140,51 +1140,101 @@ check_variant1()
msg='' msg=''
if sys_interface_check "/sys/devices/system/cpu/vulnerabilities/spectre_v1"; then if sys_interface_check "/sys/devices/system/cpu/vulnerabilities/spectre_v1"; then
# this kernel has the /sys interface, trust it over everything # this kernel has the /sys interface, trust it over everything
# v0.33+: don't. some kernels have backported the array_index_mask_nospec() workaround without
# modifying the vulnerabilities/spectre_v1 file. that's bad. we can't trust it when it says Vulnerable :(
# see "silent backport" detection at the bottom of this func
sys_interface_available=1 sys_interface_available=1
elif [ "$opt_sysfs_only" != 1 ]; then fi
if [ "$opt_sysfs_only" != 1 ]; then
# no /sys interface (or offline mode), fallback to our own ways # no /sys interface (or offline mode), fallback to our own ways
_info_nol "* Checking count of LFENCE opcodes in kernel: " _info_nol "* Kernel has array_index_mask_nospec: "
# vanilla: look for the Linus' mask aka array_index_mask_nospec()
# that is inlined at least in raw_copy_from_user (__get_user_X symbols)
#mov PER_CPU_VAR(current_task), %_ASM_DX
#cmp TASK_addr_limit(%_ASM_DX),%_ASM_AX
#jae bad_get_user
# /* array_index_mask_nospec() are the 2 opcodes that follow */
#+sbb %_ASM_DX, %_ASM_DX
#+and %_ASM_DX, %_ASM_AX
#ASM_STAC
# x86 64bits: jae(0x0f 0x83 0x?? 0x?? 0x?? 0x??) sbb(0x48 0x19 0xd2) and(0x48 0x21 0xd0)
# x86 32bits: cmp(0x3b 0x82 0x?? 0x?? 0x00 0x00) jae(0x73 0x??) sbb(0x19 0xd2) and(0x21 0xd0)
if [ -n "$vmlinux_err" ]; then if [ -n "$vmlinux_err" ]; then
msg="couldn't check ($vmlinux_err)" pstatus yellow UNKNOWN "couldn't check ($vmlinux_err)"
status=UNK elif ! which perl >/dev/null 2>&1; then
pstatus yellow UNKNOWN pstatus yellow UNKNOWN "missing 'perl' binary, please install it"
else else
if ! which objdump >/dev/null 2>&1; then perl -ne '/\x0f\x83....\x48\x19\xd2\x48\x21\xd0/ and $found++; END { exit($found) }' "$vmlinux"; ret=$?
msg="missing 'objdump' tool, please install it, usually it's in the binutils package" if [ $ret -gt 0 ]; then
status=UNK pstatus green YES "$ret occurence(s) found of 64 bits array_index_mask_nospec()"
pstatus yellow UNKNOWN v1_mask_nospec=1
else else
# here we disassemble the kernel and count the number of occurrences of the LFENCE opcode perl -ne '/\x3b\x82..\x00\x00\x73.\x19\xd2\x21\xd0/ and $found++; END { exit($found) }' "$vmlinux"; ret=$?
# in non-patched kernels, this has been empirically determined as being around 40-50 if [ $ret -gt 0 ]; then
# in patched kernels, this is more around 70-80, sometimes way higher (100+) pstatus green YES "$ret occurence(s) found of 32 bits array_index_mask_nospec()"
# v0.13: 68 found in a 3.10.23-xxxx-std-ipv6-64 (with lots of modules compiled-in directly), which doesn't have the LFENCE patches, v1_mask_nospec=1
# so let's push the threshold to 70.
nb_lfence=$(objdump -d "$vmlinux" | grep -wc lfence)
if [ "$nb_lfence" -lt 70 ]; then
msg="only $nb_lfence opcodes found, should be >= 70, heuristic to be improved when official patches become available"
status=VULN
pstatus red NO
else else
msg="$nb_lfence opcodes found, which is >= 70, heuristic to be improved when official patches become available" pstatus red NO
status=OK
pstatus green YES
fi fi
fi fi
fi fi
if [ "$opt_verbose" -ge 2 ] || [ "$v1_mask_nospec" != 1 ]; 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 opcodes in kernel: "
if [ -n "$vmlinux_err" ]; then
pstatus yellow UNKNOWN "couldn't check ($vmlinux_err)"
else
if ! which objdump >/dev/null 2>&1; then
pstatus yellow UNKNOWN "missing '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
# in non-patched kernels, this has been empirically determined as being around 40-50
# in patched kernels, this is more around 70-80, sometimes way higher (100+)
# v0.13: 68 found in a 3.10.23-xxxx-std-ipv6-64 (with lots of modules compiled-in directly), which doesn't have the LFENCE patches,
# so let's push the threshold to 70.
nb_lfence=$(objdump -d "$vmlinux" | grep -wc 'lfence')
if [ "$nb_lfence" -lt 70 ]; then
pstatus red NO "only $nb_lfence opcodes found, should be >= 70, heuristic to be improved when official patches become available"
else
v1_lfence=1
pstatus green YES "$nb_lfence opcodes found, which is >= 70, heuristic to be improved when official patches become available"
fi
fi
fi
fi
else else
# 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!"
status=UNK status=UNK
fi fi
# report status
cve='CVE-2017-5753'
if ! is_cpu_vulnerable 1; then if ! is_cpu_vulnerable 1; then
# override status & msg in case CPU is not vulnerable after all # override status & msg in case CPU is not vulnerable after all
msg="your CPU vendor reported your CPU model as not vulnerable" pvulnstatus $cve OK "your CPU vendor reported your CPU model as not vulnerable"
status=OK elif [ -z "$msg" ]; then
# if msg is empty, sysfs check didn't fill it, rely on our own test
if [ "$v1_mask_nospec" = 1 ]; then
pvulnstatus $cve OK "Kernel source has been patched to mitigate the vulnerability (array_index_mask_nospec)"
elif [ "$v1_lfence" = 1 ]; then
pvulnstatus $cve OK "Kernel source has PROBABLY been patched to mitigate the vulnerability (LFENCE opcodes heuristic)"
elif [ "$vmlinux_err" ]; then
pvulnstatus $cve UNK "Couldn't find kernel image or tools missing to execute the checks"
else
pvulnstatus $cve VULN "Kernel source needs to be patched to mitigate the vulnerability"
fi
else
if [ "$msg" = "Vulnerable" ] && [ "$v1_mask_nospec" = 1 ]; then
pvulnstatus $cve OK "Kernel source has been patched to mitigate the vulnerability (silent backport of array_index_mask_nospec)"
else
[ "$msg" = "Vulnerable" ] && msg="Kernel source needs to be patched to mitigate the vulnerability"
pvulnstatus $cve "$status" "$msg"
fi
fi fi
# report status
pvulnstatus CVE-2017-5753 "$status" "$msg"
} }
################### ###################