From d73a24cb5b2e3ebb2667de59ab818e6e03bd58fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lesimple?= Date: Mon, 8 Jan 2018 20:50:42 +0100 Subject: [PATCH] implement offline mode and help --- README.md | 83 ++++++-- spectre-meltdown-checker.sh | 401 ++++++++++++++++++++++++------------ 2 files changed, 334 insertions(+), 150 deletions(-) diff --git a/README.md b/README.md index fa1562f..42e3715 100644 --- a/README.md +++ b/README.md @@ -1,51 +1,96 @@ Spectre & Meltdown Checker ========================== -A simple shell script to tell if your Linux installation is vulnerable -against the 3 "speculative execution" CVEs: +A simple shell script to tell if your Linux installation is vulnerable against the 3 "speculative execution" CVEs. -CVE-2017-5753 bounds check bypass (Spectre Variant 1) +Without options, it'll inspect you currently running kernel. +You can also specify a kernel image on the command line, if you'd like to inspect a kernel you're not running. + +The script will do its best to detect mitigations, including backported non-vanilla patches, regardless of the advertised kernel version number. + +## Quick summary of the CVEs + +**CVE-2017-5753** bounds check bypass (Spectre Variant 1) - Impact: Kernel & all software - Mitigation: recompile software *and* kernel with a modified compiler that introduces the LFENCE opcode at the proper positions in the resulting code - Performance impact of the mitigation: negligible -CVE-2017-5715: branch target injection (Spectre Variant 2) +**CVE-2017-5715** branch target injection (Spectre Variant 2) - Impact: Kernel - Mitigation 1: new opcode via microcode update that should be used by up to date compilers to protect the BTB (by flushing indirect branch predictors) - Mitigation 2: introducing "retpoline" into compilers, and recompile software/OS with it - Performance impact of the mitigation: high for mitigation 1, medium for mitigation 2, depending on your CPU -CVE-2017-5754: rogue data cache load (Meltdown) +**CVE-2017-5754** rogue data cache load (Meltdown) - Impact: Kernel - Mitigation: updated kernel (with PTI/KPTI patches), updating the kernel is enough - Performance impact of the mitigation: low to medium -Example of the output of the script: +## Example of script output +### Ubuntu LTS (before official patches) ``` -$ sudo ./spectre-meltdown-checker.sh -Spectre and Meltdown mitigation detection tool v0.07 +$ sudo ./spectre-and-meltdown.sh +Spectre and Meltdown mitigation detection tool v0.16 + +Checking for vulnerabilities against live running kernel Linux 4.4.0-104-generic #127-Ubuntu SMP Mon Dec 11 12:16:42 UTC 2017 x86_64 +Will use vmlinux image /boot/vmlinuz-4.4.0-104-generic +Will use kconfig /boot/config-4.4.0-104-generic +Will use System.map file /boot/System.map-4.4.0-104-generic CVE-2017-5753 [bounds check bypass] aka 'Spectre Variant 1' -* Kernel compiled with LFENCE opcode inserted at the proper places: NO (only 38 opcodes found, should be >= 60) -> STATUS: VULNERABLE +* Kernel compiled with LFENCE opcode inserted at the proper places: NO (only 38 opcodes found, should be >= 70) +> STATUS: VULNERABLE CVE-2017-5715 [branch target injection] aka 'Spectre Variant 2' * Mitigation 1 -* Hardware (CPU microcode) support for mitigation: NO -* Kernel support for IBRS: NO -* IBRS enabled for Kernel space: NO -* IBRS enabled for User space: NO +* Hardware (CPU microcode) support for mitigation: NO +* Kernel support for IBRS: NO +* IBRS enabled for Kernel space: NO +* IBRS enabled for User space: NO * Mitigation 2 -* Kernel compiled with retpolines: NO -> STATUS: VULNERABLE (IBRS hardware + kernel support OR kernel with retpolines are needed to mitigate the vulnerability) +* Kernel compiled with retpoline option: NO +* Kernel compiled with a retpoline-aware compiler: NO +> STATUS: VULNERABLE (IBRS hardware + kernel support OR kernel with retpoline are needed to mitigate the vulnerability) CVE-2017-5754 [rogue data cache load] aka 'Meltdown' aka 'Variant 3' -* Kernel supports Page Table Isolation (PTI): YES -* PTI enabled and active: YES -> STATUS: NOT VULNERABLE (PTI mitigates the vulnerability) +* Kernel supports Page Table Isolation (PTI): NO +* PTI enabled and active: NO +> STATUS: VULNERABLE (PTI is needed to mitigate the vulnerability) +``` + +## #First patched kernel of RHEL6 + +``` +$ sudo ./spectre-meltdown-checker.sh --kernel /tmp/vmlinuz-2.6.32-696.18.7.el6.x86_64 --config /tmp/config-2.6.32-696.18.7.el6.x86_64 --map /tmp/System.map-2.6.32-696.18.7.el6.x86_64 +Spectre and Meltdown mitigation detection tool v0.16 + +Checking for vulnerabilities against specified kernel +Will use vmlinux image /tmp/vmlinuz-2.6.32-696.18.7.el6.x86_64 +Will use kconfig /tmp/config-2.6.32-696.18.7.el6.x86_64 +Will use System.map file /tmp/System.map-2.6.32-696.18.7.el6.x86_64 + +CVE-2017-5753 [bounds check bypass] aka 'Spectre Variant 1'm +* Kernel compiled with LFENCE opcode inserted at the proper places: YES (84 opcodes found, which is >= 70) +> STATUS: NOT VULNERABLE + +CVE-2017-5715 [branch target injection] aka 'Spectre Variant 2' +* Mitigation 1 +* Hardware (CPU microcode) support for mitigation: NO +* Kernel support for IBRS: YES +* IBRS enabled for Kernel space: N/A (not testable in offline mode) +* IBRS enabled for User space: N/A (not testable in offline mode) +* Mitigation 2 +* Kernel compiled with retpoline option: NO +* Kernel compiled with a retpoline-aware compiler: NO +> STATUS: NOT VULNERABLE (offline mode: IBRS will mitigate the vulnerability if enabled at runtime) + +CVE-2017-5754 [rogue data cache load] aka 'Meltdown' aka 'Variant 3' +* Kernel supports Page Table Isolation (PTI): YES +* PTI enabled and active: N/A (can't verify if PTI is enabled in offline mode) +> STATUS: NOT VULNERABLE (offline mode: PTI will mitigate the vulnerability if enabled at runtime) ``` diff --git a/spectre-meltdown-checker.sh b/spectre-meltdown-checker.sh index b455949..46ed23e 100755 --- a/spectre-meltdown-checker.sh +++ b/spectre-meltdown-checker.sh @@ -10,6 +10,7 @@ pstatus() red) col="\033[101m\033[30m";; green) col="\033[102m\033[30m";; yellow) col="\033[103m\033[30m";; + blue) col="\033[104m\033[30m";; *) col="";; esac /bin/echo -ne "$col $2 \033[0m" @@ -65,11 +66,12 @@ extract_vmlinux() [ -n "$1" ] || return 1 # Prepare temp files: vmlinuxtmp="$(mktemp /tmp/vmlinux-XXX)" + trap "rm -f $vmlinuxtmp" EXIT # Initial attempt for uncompressed images or objects: if check_vmlinux "$1"; then cat "$1" > "$vmlinuxtmp" - echo "$vmlinuxtmp" + vmlinux=$vmlinuxtmp return 0 fi @@ -84,19 +86,149 @@ extract_vmlinux() # end of extract-vmlinux functions +show_usage() +{ + cat <] [--config ] [--map ] + Options: + Two modes are available. + + First mode is the "live" mode (default), it does its best to find information about the currently running kernel. + To run under this mode, just start the script without any option. + + Second mode is the "offline" mode, where you can inspect a non-running kernel. + You'll need to specify the location of the vmlinux file, and if possible, the corresponding config and System.map files: + + --kernel vmlinux_file Specify a (possibly compressed) vmlinux file + --config kernel_config Specify a kernel config file + --map kernel_map_file Specify a kernel System.map file + +EOF +} + /bin/echo -e "\033[1;34mSpectre and Meltdown mitigation detection tool v$VERSION\033[0m" /bin/echo -# root check +# parse options +opt_kernel='' +opt_config='' +opt_map='' +opt_live=1 -if [ "$(id -u)" -ne 0 ]; then - /bin/echo -e "\033[31mNote that you should launch this script with root privileges to get accurate information.\033[0m" - /bin/echo -e "\033[31mWe'll proceed but you might see permission denied errors.\033[0m" - /bin/echo -e "\033[31mTo run it as root, you can try the following command: sudo $0\033[0m" - /bin/echo +parse_opt_file() +{ + # parse_opt_file option_name option_value + option_name="$1" + option_value="$2" + if [ -z "$option_value" ]; then + show_usage + echo "$0: error: --$option_name expects one parameter (a file)" >&2 + exit 1 + elif [ ! -e "$option_value" ]; then + echo "$0: error: couldn't find file $option_value" >&2 + exit 1 + elif [ ! -f "$option_value" ]; then + echo "$0: error: $option_value is not a file" >&2 + exit 1 + elif [ ! -e "$option_value" ]; then + echo "$0: error: couldn't read $option_value (are you root?)" >&2 + exit 1 + fi + echo "$option_value" + exit 0 +} + +while [ -n "$1" ]; do + if [ "$1" = "--kernel" ]; then + opt_kernel=$(parse_opt_file kernel "$2") + [ $? -ne 0 ] && exit $? + shift 2 + opt_live=0 + elif [ "$1" = "--config" ]; then + opt_config=$(parse_opt_file config "$2") + [ $? -ne 0 ] && exit $? + shift 2 + opt_live=0 + elif [ "$1" = "--map" ]; then + opt_map=$(parse_opt_file map "$2") + [ $? -ne 0 ] && exit $? + shift 2 + opt_live=0 + elif [ "$1" = "-h" -o "$1" = "--help" ]; then + show_usage + exit 0 + else + show_usage + echo "$0: error: unknown option '$1'" + exit 1 + fi +done + +# root check (only for live mode, for offline mode, we already checked if we could read the files) + +if [ "$opt_live" = 1 ]; then + if [ "$(id -u)" -ne 0 ]; then + /bin/echo -e "\033[31mNote that you should launch this script with root privileges to get accurate information.\033[0m" + /bin/echo -e "\033[31mWe'll proceed but you might see permission denied errors.\033[0m" + /bin/echo -e "\033[31mTo run it as root, you can try the following command: sudo $0\033[0m" + /bin/echo + fi + /bin/echo -e "Checking for vulnerabilities against live running kernel \033[35m"$(uname -s) $(uname -r) $(uname -v) $(uname -m)"\033[0m" + + # try to find the image of the current running kernel + [ -e /boot/vmlinuz-linux ] && opt_kernel=/boot/vmlinuz-linux + [ -e /boot/vmlinuz-linux-libre ] && opt_kernel=/boot/vmlinuz-linux-libre + [ -e /boot/vmlinuz-$(uname -r) ] && opt_kernel=/boot/vmlinuz-$(uname -r) + [ -e /boot/kernel-$( uname -r) ] && opt_kernel=/boot/kernel-$( uname -r) + [ -e /boot/bzImage-$(uname -r) ] && opt_kernel=/boot/bzImage-$(uname -r) + [ -e /boot/kernel-genkernel-$(uname -m)-$(uname -r) ] && opt_kernel=/boot/kernel-genkernel-$(uname -m)-$(uname -r) + + # system.map + [ -e /boot/System.map-$(uname -r) ] && opt_map=/boot/System.map-$(uname -r) + + # config + if [ -e /proc/config.gz ] ; then + dumped_config="$(mktemp /tmp/config-XXX)" + gunzip -c /proc/config.gz > $dumped_config + # dumped_config will be deleted at the end of the script + opt_config=$dumped_config + elif [ -e /boot/config-$(uname -r) ]; then + opt_config=/boot/config-$(uname -r) + fi +else + /bin/echo "Checking for vulnerabilities against specified kernel" +fi +if [ -n "$opt_kernel" ]; then + /bin/echo -e "Will use vmlinux image \033[35m$opt_kernel\033[0m" +else + /bin/echo "Will use no vmlinux image (accuracy might be reduced" +fi +if [ -n "$opt_config" ]; then + /bin/echo -e "Will use kconfig \033[35m$opt_config\033[0m" +else + /bin/echo "Will use no kconfig (accuracy might be reduced)" +fi +if [ -n "$opt_map" ]; then + /bin/echo -e "Will use System.map file \033[35m$opt_map\033[0m" +else + /bin/echo "Will use no System.map file (accuracy might be reduced)" +fi + +if [ -e "$opt_kernel" ]; then + if ! which readelf >/dev/null 2>&1; then + vmlinux_err="missing 'readelf' tool, please install it, usually it's in the 'binutils' package" + else + extract_vmlinux "$opt_kernel" + fi +else + vmlinux_err="couldn't find your kernel image in /boot, if you used neboot, this is normal" +fi +if [ -z "$vmlinux" -o ! -r "$vmlinux" ]; then + [ -z "$vmlinux_err" ] && vmlinux_err="couldn't extract your kernel from $opt_kernel" fi -/bin/echo -e "Checking vulnerabilities against \033[35m"$(uname -s) $(uname -r) $(uname -v) $(uname -m)"\033[0m" /bin/echo ########### @@ -105,42 +237,25 @@ fi /bin/echo -n "* Kernel compiled with LFENCE opcode inserted at the proper places: " status=0 -img='' -# try to find the image of the current running kernel -[ -e /boot/vmlinuz-linux ] && img=/boot/vmlinuz-linux -[ -e /boot/vmlinuz-linux-libre ] && img=/boot/vmlinuz-linux-libre -[ -e /boot/vmlinuz-$(uname -r) ] && img=/boot/vmlinuz-$(uname -r) -[ -e /boot/kernel-$( uname -r) ] && img=/boot/kernel-$( uname -r) -[ -e /boot/bzImage-$(uname -r) ] && img=/boot/bzImage-$(uname -r) -[ -e /boot/kernel-genkernel-$(uname -m)-$(uname -r) ] && img=/boot/kernel-genkernel-$(uname -m)-$(uname -r) -if [ -z "$img" ]; then - pstatus yellow UNKNOWN "couldn't find your kernel image in /boot, if you used netboot, this is normal" +if [ -n "$vmlinux_err" ]; then + pstatus yellow UNKNOWN "$vmlinux_err" else - if ! which readelf >/dev/null 2>&1; then - pstatus yellow UNKNOWN "missing 'readelf' tool, please install it, usually it's in the 'binutils' package" + 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 - 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" - elif ! which objdump >/dev/null 2>&1; then - pstatus yellow UNKNOWN "missing 'objdump' tool, please install it, usually it's in the binutils package" + # 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 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) + if [ "$nb_lfence" -lt 70 ]; then + pstatus red NO "only $nb_lfence opcodes found, should be >= 70" + status=1 else - # 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 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) - if [ "$nb_lfence" -lt 70 ]; then - pstatus red NO "only $nb_lfence opcodes found, should be >= 70" - status=1 - else - pstatus green YES "$nb_lfence opcodes found, which is >= 70" - status=2 - fi + pstatus green YES "$nb_lfence opcodes found, which is >= 70" + status=2 fi fi fi @@ -180,57 +295,65 @@ if [ "$insmod_msr" = 1 ]; then fi /bin/echo -n "* Kernel support for IBRS: " -if [ ! -e /sys/kernel/debug/sched_features ]; then - # 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 +if [ "$opt_live" = 1 ]; then + if [ ! -e /sys/kernel/debug/sched_features ]; then + # 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 + fi + if [ -e /sys/kernel/debug/ibrs_enabled ]; then + # if the file is there, we have IBRS compiled-in + pstatus green YES + 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) + fi fi -if [ -e /sys/kernel/debug/ibrs_enabled ]; then - # if the file is there, we have IBRS compiled-in - pstatus green YES - 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 +if [ "$ibrs_supported " != 1 -a -n "$opt_map" ]; then + if grep -q spec_ctrl "$opt_map"; then + pstatus green YES + ibrs_supported=1 + fi +fi +if [ "$ibrs_supported" != 1 ]; then pstatus red NO fi /bin/echo -n "* IBRS enabled for Kernel space: " -# 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 [ "$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 +else + pstatus blue N/A "not testable in offline mode" +fi /bin/echo -n "* IBRS enabled for User space: " -case "$ibrs_enabled" in - "") [ "$ibrs_supported" = 1 ] && pstatus yellow UNKNOWN || pstatus red NO;; - 0 | 1) pstatus red NO;; - 2) pstatus green YES;; - *) pstatus yellow UNKNOWN;; -esac +if [ "$opt_live" = 1 ]; then + case "$ibrs_enabled" in + "") [ "$ibrs_supported" = 1 ] && pstatus yellow UNKNOWN || pstatus red NO;; + 0 | 1) pstatus red NO;; + 2) pstatus green YES;; + *) pstatus yellow UNKNOWN;; + esac +else + pstatus blue N/A "not testable in offline mode" +fi /bin/echo "* Mitigation 2" /bin/echo -n "* Kernel compiled with retpoline option: " # We check the RETPOLINE kernel options -if [ -e /proc/config.gz ]; then - # either the running kernel exports his own config - if zgrep -q '^CONFIG_RETPOLINE=y' /proc/config.gz; then - pstatus green YES - retpoline=1 - else - pstatus red NO - fi -elif [ -e /boot/config-$(uname -r) ]; then - # or we can find a config file in /root with the kernel release name - if grep -q '^CONFIG_RETPOLINE=y' /boot/config-$(uname -r); then +if [ -r "$opt_config" ]; then + if grep -q '^CONFIG_RETPOLINE=y' "$opt_config"; then pstatus green YES retpoline=1 else @@ -246,42 +369,53 @@ fi # 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 +if [ -n "$opt_map" ]; 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 + if grep -qw noretpoline_setup "$opt_map"; then + retpoline_compiler=1 + pstatus green YES "noretpoline_setup symbol found in System.map" + else + pstatus red NO + fi +elif [ -n "$vmlinux" ]; then + # look for the symbol + if 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" + pstatus green YES "noretpoline_setup found in vmlinux symbols" + else + pstatus red NO 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 green YES "noretpoline_setup found in vmlinux" + else pstatus red NO fi else - pstatus yellow UNKNOWN "couldn't find your kernel image" + pstatus yellow UNKNOWN "couldn't find your kernel image or System.map" fi - /bin/echo -ne "> \033[46m\033[30mSTATUS:\033[0m " if grep -q AMD /proc/cpuinfo; then pstatus green "NOT VULNERABLE" "your CPU is not vulnerable as per the vendor" -elif [ "$ibrs_enabled" = 1 -o "$ibrs_enabled" = 2 ]; then - pstatus green "NOT VULNERABLE" "IBRS mitigates the vulnerability" elif [ "$retpoline" = 1 -a "$retpoline_compiler" = 1 ]; then pstatus green "NOT VULNERABLE" "retpoline mitigate the vulnerability" +elif [ "$opt_live" = 1 ]; then + if [ "$ibrs_enabled" = 1 -o "$ibrs_enabled" = 2 ]; then + pstatus green "NOT VULNERABLE" "IBRS mitigates the vulnerability" + else + pstatus red VULNERABLE "IBRS hardware + kernel support OR kernel with retpoline are needed to mitigate the vulnerability" + fi else - pstatus red VULNERABLE "IBRS hardware + kernel support OR kernel with retpoline are needed to mitigate the vulnerability" + if [ "$ibrs_supported" = 1 ]; then + pstatus green "NOT VULNERABLE" "offline mode: IBRS will mitigate the vulnerability if enabled at runtime" + else + pstatus red VULNERABLE "IBRS hardware + kernel support OR kernel with retpoline are needed to mitigate the vulnerability" + fi fi ########## @@ -291,24 +425,17 @@ fi /bin/echo -n "* Kernel supports Page Table Isolation (PTI): " kpti_support=0 kpti_can_tell=0 -if [ -e /proc/config.gz ]; then - # either the running kernel exports his own config +if [ -n "$opt_config" ]; then kpti_can_tell=1 - if zgrep -q '^\(CONFIG_PAGE_TABLE_ISOLATION=y\|CONFIG_KAISER=y\)' /proc/config.gz; then - kpti_support=1 - fi -elif [ -e /boot/config-$(uname -r) ]; then - # or we can find a config file in /root with the kernel release name - kpti_can_tell=1 - if grep -q '^\(CONFIG_PAGE_TABLE_ISOLATION=y\|CONFIG_KAISER=y\)' /boot/config-$(uname -r); then + if grep -Eq '^\(CONFIG_PAGE_TABLE_ISOLATION\|CONFIG_KAISER\)=y' "$opt_config"; then kpti_support=1 fi fi -if [ "$kpti_support" = 0 -a -e /boot/System.map-$(uname -r) ]; then +if [ "$kpti_support" = 0 -a -n "$opt_map" ]; then # 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 kpti_can_tell=1 - if grep -qw kpti_force_enabled /boot/System.map-$(uname -r); then + if grep -qw kpti_force_enabled "$opt_map"; then kpti_support=1 fi fi @@ -330,29 +457,33 @@ if [ "$kpti_support" = 1 ]; then elif [ "$kpti_can_tell" = 1 ]; then pstatus red NO else - pstatus yellow UNKNOWN "couldn't read your kernel configuration" + pstatus yellow UNKNOWN "couldn't read your kernel configuration nor System.map file" fi /bin/echo -n "* PTI enabled and active: " -if grep ^flags /proc/cpuinfo | grep -qw pti; then - # vanilla PTI patch sets the 'pti' flag in cpuinfo - 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 - # if we can't find the flag, grep in dmesg - kpti_enabled=1 +if [ "$opt_live" = 1 ]; then + if grep ^flags /proc/cpuinfo | grep -qw pti; then + # vanilla PTI patch sets the 'pti' flag in cpuinfo + 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 + # if we can't find the flag, grep in dmesg + kpti_enabled=1 + else + kpti_enabled=0 + fi + if [ "$kpti_enabled" = 1 ]; then + pstatus green YES + else + pstatus red NO + fi else - kpti_enabled=0 -fi -if [ "$kpti_enabled" = 1 ]; then - pstatus green YES -else - pstatus red NO + pstatus blue N/A "can't verify if PTI is enabled in offline mode" fi if [ "$mounted_debugfs" = 1 ]; then @@ -363,12 +494,20 @@ fi /bin/echo -ne "> \033[46m\033[30mSTATUS:\033[0m " if grep -q AMD /proc/cpuinfo; then pstatus green "NOT VULNERABLE" "your CPU is not vulnerable as per the vendor" -elif [ "$kpti_enabled" = 1 ]; then - pstatus green "NOT VULNERABLE" "PTI mitigates the vulnerability" +elif [ "$opt_live" = 1 ]; then + if [ "$kpti_enabled" = 1 ]; then + pstatus green "NOT VULNERABLE" "PTI mitigates the vulnerability" + else + pstatus red "VULNERABLE" "PTI is needed to mitigate the vulnerability" + fi else - pstatus red "VULNERABLE" "PTI is needed to mitigate the vulnerability" + if [ "$kpti_support" = 1 ]; then + pstatus green "NOT VULNERABLE" "offline mode: PTI will mitigate the vulnerability if enabled at runtime" + else + pstatus red "VULNERABLE" "PTI is needed to mitigate the vulnerability" + fi fi /bin/echo -[ -n "$vmlinux" -a -f "$vmlinux" ] && rm -f "$vmlinux" +[ -n "$dumped_config" ] && rm -f "$dumped_config"