24 Commits
v0.12 ... v0.16

Author SHA1 Message Date
8d4d295309 bump to v0.16 to reflect changes 2018-01-08 17:48:20 +01:00
1ff437edbb Merge pull request #16 from Alkorin/fixes
Fixes
2018-01-08 17:45:59 +01:00
34656827f5 detect retpoline-compliant compiler from latest LKML patches 2018-01-08 17:32:19 +01:00
8c8a8d35fd Detect if 'readelf' is present 2018-01-08 16:52:09 +01:00
debd10b517 Detect if 'strings' is present 2018-01-08 16:51:20 +01:00
21f81ff5c9 Detect if uncompress binaries are present 2018-01-08 16:51:14 +01:00
206e4b7fbc add detection of retpoline-aware compiler 2018-01-08 16:28:00 +01:00
1a14483c98 Use 'readelf' instead of 'file' to detect kernel 2018-01-08 15:56:19 +01:00
26564206db Do not execute checks if we already found that PTI is enabled 2018-01-08 15:56:19 +01:00
207168e097 detect if the used compiler supports retpoline (WIP) 2018-01-08 15:45:09 +01:00
f8ca11e56a Merge pull request #12 from sebastianw/fix-double-print
Remove superfluous 'YES' output when checking cpuinfo
2018-01-08 15:05:15 +01:00
c88acdd31d Remove superfluous 'YES' output when checking cpuinfo 2018-01-08 14:50:59 +01:00
88df48f4a7 Merge pull request #11 from sebastianw/kaiser-cpu-flag
Recognize 'kaiser' flag in /proc/cpuinfo
2018-01-08 14:45:40 +01:00
124ce8e27a Recognize 'kaiser' flag in /proc/cpuinfo 2018-01-08 14:38:43 +01:00
7bbcfe0df7 Merge pull request #7 from Feandil/redhat
Redhat support
2018-01-08 14:17:33 +01:00
a792348928 RedHat uses a different configuration name 2018-01-08 12:59:12 +01:00
66f7708095 Refactor RedHat support:
- Isolate file check to different elif (allowing to add more)
- Do the PTI debugfs check first (faster and supposed to be dynamic)
- If pti_enable is 0, don't trust dmesg (supposed to be dynamic)
2018-01-08 12:59:03 +01:00
34ef5ef21b Delay umount (for RedHat access to pti_enable) 2018-01-08 12:58:22 +01:00
edbdf0da1f push the lfence opcodes threshold to 70 2018-01-08 12:49:23 +01:00
68adbfdf14 Merge pull request #10 from Alkorin/permissionDenied
Avoid 'cat: /sys/kernel/debug/x86/pti_enabled: Permission denied'
2018-01-08 12:44:09 +01:00
47c30babf1 Avoid 'cat: /sys/kernel/debug/x86/pti_enabled: Permission denied' 2018-01-08 12:41:28 +01:00
ef7a5c4cf6 adding uname -v to get potential additional vendor information 2018-01-08 12:22:56 +01:00
4406910bea Merge pull request #8 from Feandil/debugfs
Fix debugfs mount check
2018-01-08 12:19:23 +01:00
b7197d6f54 Fix debugfs mount check 2018-01-08 12:15:51 +01:00

View File

@ -1,7 +1,7 @@
#! /bin/sh #! /bin/sh
# Spectre & Meltdown checker # Spectre & Meltdown checker
# Stephane Lesimple # Stephane Lesimple
VERSION=0.12 VERSION=0.16
# print status function # print status function
pstatus() pstatus()
@ -33,9 +33,11 @@ pstatus()
# Licensed under the GNU General Public License, version 2 (GPLv2). # Licensed under the GNU General Public License, version 2 (GPLv2).
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
vmlinux=''
vmlinux_err=''
check_vmlinux() check_vmlinux()
{ {
file "$1" 2>/dev/null | grep -q ELF || return 1 readelf -h $1 > /dev/null 2>&1 || return 1
return 0 return 0
} }
@ -45,11 +47,15 @@ try_decompress()
# "grep" that report the byte offset of the line instead of the pattern. # "grep" that report the byte offset of the line instead of the pattern.
# Try to find the header ($1) and decompress from here # Try to find the header ($1) and decompress from here
for pos in `tr "$1\n$2" "\n$2=" < "$4" | grep -abo "^$2"` for pos in `tr "$1\n$2" "\n$2=" < "$5" | grep -abo "^$2"`
do 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%%:*} pos=${pos%%:*}
tail -c+$pos "$4" | $3 > $vmlinuxtmp 2> /dev/null tail -c+$pos "$5" | $3 > $vmlinuxtmp 2> /dev/null
check_vmlinux "$vmlinuxtmp" && echo "$vmlinuxtmp" && return 0 check_vmlinux "$vmlinuxtmp" && vmlinux=$vmlinuxtmp && return 0
done done
return 1 return 1
} }
@ -68,11 +74,11 @@ extract_vmlinux()
fi fi
# That didn't work, so retry after decompression. # That didn't work, so retry after decompression.
try_decompress '\037\213\010' xy gunzip "$1" && return 0 try_decompress '\037\213\010' xy gunzip gunzip "$1" && return 0
try_decompress '\3757zXZ\000' abcde unxz "$1" && return 0 try_decompress '\3757zXZ\000' abcde unxz xz-utils "$1" && return 0
try_decompress 'BZh' xy bunzip2 "$1" && return 0 try_decompress 'BZh' xy bunzip2 bzip2 "$1" && return 0
try_decompress '\135\0\0\0' xxx unlzma "$1" && return 0 try_decompress '\135\0\0\0' xxx unlzma xz-utils "$1" && return 0
try_decompress '\211\114\132' xy 'lzop -d' "$1" && return 0 try_decompress '\211\114\132' xy 'lzop -d' lzop "$1" && return 0
return 1 return 1
} }
@ -90,7 +96,7 @@ if [ "$(id -u)" -ne 0 ]; then
/bin/echo /bin/echo
fi fi
/bin/echo -e "Checking vulnerabilities against \033[35m"$(uname -s) $(uname -r) $(uname -m)"\033[0m" /bin/echo -e "Checking vulnerabilities against \033[35m"$(uname -s) $(uname -r) $(uname -v) $(uname -m)"\033[0m"
/bin/echo /bin/echo
########### ###########
@ -109,25 +115,34 @@ img=''
if [ -z "$img" ]; then if [ -z "$img" ]; then
pstatus yellow UNKNOWN "couldn't find your kernel image in /boot, if you used netboot, this is normal" pstatus yellow UNKNOWN "couldn't find your kernel image in /boot, if you used netboot, this is normal"
else else
vmlinux=$(extract_vmlinux $img) if ! which readelf >/dev/null 2>&1; then
if [ -z "$vmlinux" -o ! -r "$vmlinux" ]; then pstatus yellow UNKNOWN "missing 'readelf' tool, please install it, usually it's in the 'binutils' package"
else
extract_vmlinux $img
if [ "$vmlinux_err" != "" ]; then
pstatus yellow UNKNOWN "couldn't extract your kernel from $img: $vmlinux_err"
elif [ -z "$vmlinux" -o ! -r "$vmlinux" ]; then
pstatus yellow UNKNOWN "couldn't extract your kernel from $img" pstatus yellow UNKNOWN "couldn't extract your kernel from $img"
elif ! which objdump >/dev/null 2>&1; then elif ! which objdump >/dev/null 2>&1; then
pstatus yellow UNKNOWN "missing 'objdump' tool, please install it, usually it's in the binutils package" pstatus yellow UNKNOWN "missing 'objdump' tool, please install it, usually it's in the binutils package"
else else
# here we disassemble the kernel and count the number of occurences of the LFENCE opcode # here we disassemble the kernel and count the number of occurences of the LFENCE opcode
# in non-patched kernels, this has been empirically determined as being around 40-50 # in non-patched kernels, this has been empirically determined as being around 40-50
# in patched kernels, this is more around 70-80 # 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.
# TODO LKML patch is starting to dump LFENCE in favor of the PAUSE opcode, we might need to check that (patch not stabilized yet)
nb_lfence=$(objdump -D "$vmlinux" | grep -wc lfence) nb_lfence=$(objdump -D "$vmlinux" | grep -wc lfence)
if [ "$nb_lfence" -lt 60 ]; then if [ "$nb_lfence" -lt 70 ]; then
pstatus red NO "only $nb_lfence opcodes found, should be >= 60" pstatus red NO "only $nb_lfence opcodes found, should be >= 70"
status=1 status=1
else else
pstatus green YES "$nb_lfence opcodes found, which is >= 60" pstatus green YES "$nb_lfence opcodes found, which is >= 70"
status=2 status=2
fi fi
fi fi
fi fi
fi
/bin/echo -ne "> \033[46m\033[30mSTATUS:\033[0m " /bin/echo -ne "> \033[46m\033[30mSTATUS:\033[0m "
[ "$status" = 0 ] && pstatus yellow UNKNOWN [ "$status" = 0 ] && pstatus yellow UNKNOWN
@ -164,19 +179,24 @@ if [ "$insmod_msr" = 1 ]; then
fi fi
/bin/echo -n "* Kernel support for IBRS: " /bin/echo -n "* Kernel support for IBRS: "
if [ -e /sys/kernel/debug/sched_features ]; then if [ ! -e /sys/kernel/debug/sched_features ]; then
# try to mount the debugfs hierarchy ourselves and remember it to umount afterwards # try to mount the debugfs hierarchy ourselves and remember it to umount afterwards
mount -t debugfs debugfs /sys/kernel/debug 2>/dev/null && mounted_debugfs=1 mount -t debugfs debugfs /sys/kernel/debug 2>/dev/null && mounted_debugfs=1
fi fi
if [ -e /sys/kernel/debug/ibrs_enabled -o -e /sys/kernel/debug/x86/ibrs_enabled ]; then if [ -e /sys/kernel/debug/ibrs_enabled ]; then
# if the file is there, we have IBRS compiled-in # if the file is there, we have IBRS compiled-in
pstatus green YES pstatus green YES
ibrs_supported=1 ibrs_supported=1
ibrs_enabled=$(cat /sys/kernel/debug/ibrs_enabled 2>/dev/null)
elif [ -e /sys/kernel/debug/x86/ibrs_enabled ]; then
# RedHat uses a different path (see https://access.redhat.com/articles/3311301)
pstatus green YES
ibrs_supported=1
ibrs_enabled=$(cat /sys/kernel/debug/x86/ibrs_enabled 2>/dev/null)
else else
pstatus red NO pstatus red NO
fi fi
[ -f /sys/kernel/debug/ibrs_enabled ] && ibrs_enabled=$(cat /sys/kernel/debug/ibrs_enabled 2>/dev/null) || ibrs_enabled=$(cat /sys/kernel/debug/x86/ibrs_enabled 2>/dev/null)
/bin/echo -n "* IBRS enabled for Kernel space: " /bin/echo -n "* IBRS enabled for Kernel space: "
# 0 means disabled # 0 means disabled
# 1 is enabled only for kernel space # 1 is enabled only for kernel space
@ -193,19 +213,12 @@ case "$ibrs_enabled" in
"") [ "$ibrs_supported" = 1 ] && pstatus yellow UNKNOWN || pstatus red NO;; "") [ "$ibrs_supported" = 1 ] && pstatus yellow UNKNOWN || pstatus red NO;;
0 | 1) pstatus red NO;; 0 | 1) pstatus red NO;;
2) pstatus green YES;; 2) pstatus green YES;;
*) pstatus yellow unknown;; *) pstatus yellow UNKNOWN;;
esac esac
if [ "$mounted_debugfs" = 1 ]; then
# umount debugfs if we did mount it ourselves
umount /sys/kernel/debug
fi
/bin/echo "* Mitigation 2" /bin/echo "* Mitigation 2"
/bin/echo -n "* Kernel compiled with retpolines: " /bin/echo -n "* Kernel compiled with retpoline option: "
# We check the RETPOLINE kernel options # We check the RETPOLINE kernel options
# XXX this doesn't mean the kernel has been compiled with a retpoline-aware gcc
# still looking for a way do detect that ...
if [ -e /proc/config.gz ]; then if [ -e /proc/config.gz ]; then
# either the running kernel exports his own config # either the running kernel exports his own config
if zgrep -q '^CONFIG_RETPOLINE=y' /proc/config.gz; then if zgrep -q '^CONFIG_RETPOLINE=y' /proc/config.gz; then
@ -226,15 +239,48 @@ else
pstatus yellow UNKNOWN "couldn't read your kernel configuration" pstatus yellow UNKNOWN "couldn't read your kernel configuration"
fi fi
/bin/echo -n "* 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 [ -n "$vmlinux" ]; then
# look for the symbol
if [ -e /boot/System.map-$(uname -r) ]; then
if grep -qw noretpoline_setup /boot/System.map-$(uname -r); then
retpoline_compiler=1
pstatus green YES "noretpoline_setup symbol found in System.map"
fi
elif which nm >/dev/null 2>&1; then
# the proper way: use nm and look for the symbol
if nm "$vmlinux" 2>/dev/null | grep -qw 'noretpoline_setup'; then
retpoline_compiler=1
pstatus green YES "noretpoline_setup symbol found in vmlinux"
fi
elif grep -q noretpoline_setup "$vmlinux"; then
# if we don't have nm, nevermind, the symbol name is long enough to not have
# any false positive using good old grep directly on the binary
retpoline_compiler=1
pstatus green YES "noretpoline_setup symbol found in vmlinux"
fi
if [ "$retpoline_compiler" != 1 ]; then
pstatus red NO
fi
else
pstatus yellow UNKNOWN "couldn't find your kernel image"
fi
/bin/echo -ne "> \033[46m\033[30mSTATUS:\033[0m " /bin/echo -ne "> \033[46m\033[30mSTATUS:\033[0m "
if grep -q AMD /proc/cpuinfo; then if grep -q AMD /proc/cpuinfo; then
pstatus green "NOT VULNERABLE" "your CPU is not vulnerable as per the vendor" pstatus green "NOT VULNERABLE" "your CPU is not vulnerable as per the vendor"
elif [ "$ibrs_enabled" = 1 -o "$ibrs_enabled" = 2 ]; then elif [ "$ibrs_enabled" = 1 -o "$ibrs_enabled" = 2 ]; then
pstatus green "NOT VULNERABLE" "IBRS mitigates the vulnerability" pstatus green "NOT VULNERABLE" "IBRS mitigates the vulnerability"
elif [ "$retpoline" = 1 ]; then elif [ "$retpoline" = 1 -a "$retpoline_compiler" = 1 ]; then
pstatus green "NOT VULNERABLE" "retpolines mitigate the vulnerability" pstatus green "NOT VULNERABLE" "retpoline mitigate the vulnerability"
else else
pstatus red VULNERABLE "IBRS hardware + kernel support OR kernel with retpolines are needed to mitigate the vulnerability" pstatus red VULNERABLE "IBRS hardware + kernel support OR kernel with retpoline are needed to mitigate the vulnerability"
fi fi
########## ##########
@ -247,17 +293,17 @@ kpti_can_tell=0
if [ -e /proc/config.gz ]; then if [ -e /proc/config.gz ]; then
# either the running kernel exports his own config # either the running kernel exports his own config
kpti_can_tell=1 kpti_can_tell=1
if zgrep -q '^CONFIG_PAGE_TABLE_ISOLATION=y' /proc/config.gz; then if zgrep -q '^\(CONFIG_PAGE_TABLE_ISOLATION=y\|CONFIG_KAISER=y\)' /proc/config.gz; then
kpti_support=1 kpti_support=1
fi fi
elif [ -e /boot/config-$(uname -r) ]; then elif [ -e /boot/config-$(uname -r) ]; then
# or we can find a config file in /root with the kernel release name # or we can find a config file in /root with the kernel release name
kpti_can_tell=1 kpti_can_tell=1
if grep -q '^CONFIG_PAGE_TABLE_ISOLATION=y' /boot/config-$(uname -r); then if grep -q '^\(CONFIG_PAGE_TABLE_ISOLATION=y\|CONFIG_KAISER=y\)' /boot/config-$(uname -r); then
kpti_support=1 kpti_support=1
fi fi
fi fi
if [ -e /boot/System.map-$(uname -r) ]; then if [ "$kpti_support" = 0 -a -e /boot/System.map-$(uname -r) ]; then
# it's not an elif: some backports don't have the PTI config but still include the patch # it's not an elif: some backports don't have the PTI config but still include the patch
# so we try to find an exported symbol that is part of the PTI patch in System.map # so we try to find an exported symbol that is part of the PTI patch in System.map
kpti_can_tell=1 kpti_can_tell=1
@ -265,14 +311,18 @@ if [ -e /boot/System.map-$(uname -r) ]; then
kpti_support=1 kpti_support=1
fi fi
fi fi
if [ -n "$vmlinux" ]; then if [ "$kpti_support" = 0 -a -n "$vmlinux" ]; then
# same as above but in case we don't have System.map and only vmlinux, look for the # same as above but in case we don't have System.map and only vmlinux, look for the
# nopti option that is part of the patch (kernel command line option) # nopti option that is part of the patch (kernel command line option)
kpti_can_tell=1 kpti_can_tell=1
if ! which strings >/dev/null 2>&1; then
pstatus yellow UNKNOWN "missing 'strings' tool, please install it, usually it's in the binutils package"
else
if strings "$vmlinux" | grep -qw nopti; then if strings "$vmlinux" | grep -qw nopti; then
kpti_support=1 kpti_support=1
fi fi
fi fi
fi
if [ "$kpti_support" = 1 ]; then if [ "$kpti_support" = 1 ]; then
pstatus green YES pstatus green YES
@ -285,19 +335,30 @@ fi
/bin/echo -n "* PTI enabled and active: " /bin/echo -n "* PTI enabled and active: "
if grep ^flags /proc/cpuinfo | grep -qw pti; then if grep ^flags /proc/cpuinfo | grep -qw pti; then
# vanilla PTI patch sets the 'pti' flag in cpuinfo # vanilla PTI patch sets the 'pti' flag in cpuinfo
pstatus green YES
kpti_enabled=1 kpti_enabled=1
elif grep ^flags /proc/cpuinfo | grep -qw kaiser; then
# kernel line 4.9 sets the 'kaiser' flag in cpuinfo
kpti_enabled=1
elif [ -e /sys/kernel/debug/x86/pti_enabled ]; then
# RedHat Backport creates a dedicated file, see https://access.redhat.com/articles/3311301
kpti_enabled=$(cat /sys/kernel/debug/x86/pti_enabled 2>/dev/null)
elif dmesg | grep -Eq 'Kernel/User page tables isolation: enabled|Kernel page table isolation enabled'; then elif dmesg | grep -Eq 'Kernel/User page tables isolation: enabled|Kernel page table isolation enabled'; then
# if we can't find the flag, grep in dmesg # if we can't find the flag, grep in dmesg
pstatus green YES
kpti_enabled=1
elif [ -e /sys/kernel/debug/x86/pti_enabled -a "$(cat /sys/kernel/debug/x86/pti_enabled)" = 1 ]; then
pstatus green YES
kpti_enabled=1 kpti_enabled=1
else
kpti_enabled=0
fi
if [ "$kpti_enabled" = 1 ]; then
pstatus green YES
else else
pstatus red NO pstatus red NO
fi fi
if [ "$mounted_debugfs" = 1 ]; then
# umount debugfs if we did mount it ourselves
umount /sys/kernel/debug
fi
/bin/echo -ne "> \033[46m\033[30mSTATUS:\033[0m " /bin/echo -ne "> \033[46m\033[30mSTATUS:\033[0m "
if grep -q AMD /proc/cpuinfo; then if grep -q AMD /proc/cpuinfo; then
pstatus green "NOT VULNERABLE" "your CPU is not vulnerable as per the vendor" pstatus green "NOT VULNERABLE" "your CPU is not vulnerable as per the vendor"