mirror of
https://github.com/speed47/spectre-meltdown-checker.git
synced 2025-07-15 15:21:23 +02:00
Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
360be7b35f | |||
5f59257826 | |||
92d59cbdc1 | |||
4747b932e7 | |||
860023a806 | |||
ab67a9221d | |||
f4592bf3a8 | |||
be15e47671 | |||
d3481d9524 |
@ -9,7 +9,7 @@
|
||||
#
|
||||
# Stephane Lesimple
|
||||
#
|
||||
VERSION='0.38'
|
||||
VERSION='0.39'
|
||||
|
||||
trap 'exit_cleanup' EXIT
|
||||
trap '_warn "interrupted, cleaning up..."; exit_cleanup; exit 1' INT
|
||||
@ -150,12 +150,12 @@ if which printf >/dev/null 2>&1; then
|
||||
elif which echo >/dev/null 2>&1; then
|
||||
echo_cmd=$(which echo)
|
||||
else
|
||||
# which command is broken?
|
||||
# maybe the `which` command is broken?
|
||||
[ -x /bin/echo ] && echo_cmd=/bin/echo
|
||||
# for Android
|
||||
[ -x /system/bin/echo ] && echo_cmd=/system/bin/echo
|
||||
fi
|
||||
# still empty ? fallback to builtin
|
||||
# still empty? fallback to builtin
|
||||
[ -z "$echo_cmd" ] && echo_cmd=echo
|
||||
__echo()
|
||||
{
|
||||
@ -245,15 +245,13 @@ is_cpu_vulnerable_cached=0
|
||||
_is_cpu_vulnerable_cached()
|
||||
{
|
||||
# shellcheck disable=SC2086
|
||||
[ "$1" = 1 ] && return $variant1
|
||||
# shellcheck disable=SC2086
|
||||
[ "$1" = 2 ] && return $variant2
|
||||
# shellcheck disable=SC2086
|
||||
[ "$1" = 3 ] && return $variant3
|
||||
# shellcheck disable=SC2086
|
||||
[ "$1" = 3a ] && return $variant3a
|
||||
# shellcheck disable=SC2086
|
||||
[ "$1" = 4 ] && return $variant4
|
||||
{
|
||||
[ "$1" = 1 ] && return $variant1
|
||||
[ "$1" = 2 ] && return $variant2
|
||||
[ "$1" = 3 ] && return $variant3
|
||||
[ "$1" = 3a ] && return $variant3a
|
||||
[ "$1" = 4 ] && return $variant4
|
||||
}
|
||||
echo "$0: error: invalid variant '$1' passed to is_cpu_vulnerable()" >&2
|
||||
exit 255
|
||||
}
|
||||
@ -889,9 +887,7 @@ load_cpuid()
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
{
|
||||
EAX=1; EBX=2; ECX=3; EDX=4;
|
||||
}
|
||||
read_cpuid()
|
||||
{
|
||||
# leaf is the value of the eax register when calling the cpuid instruction:
|
||||
@ -1210,6 +1206,17 @@ is_skylake_cpu()
|
||||
return 1
|
||||
}
|
||||
|
||||
is_vulnerable_to_empty_rsb()
|
||||
{
|
||||
if is_intel && [ -z "$capabilities_rsba" ]; then
|
||||
_warn "is_vulnerable_to_empty_rsb() called before ARCH CAPABILITIES MSR was read"
|
||||
fi
|
||||
if is_skylake_cpu || [ "$capabilities_rsba" = 1 ]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
is_zen_cpu()
|
||||
{
|
||||
# is this CPU from the AMD ZEN family ? (ryzen, epyc, ...)
|
||||
@ -1247,6 +1254,8 @@ if [ "$opt_hw_only" = 1 ]; then
|
||||
opt_variant1=0
|
||||
opt_variant2=0
|
||||
opt_variant3=0
|
||||
opt_variant3a=0
|
||||
opt_variant4=0
|
||||
fi
|
||||
fi
|
||||
|
||||
@ -1309,6 +1318,9 @@ if [ "$opt_live" = 1 ]; then
|
||||
if [ -r /proc/cmdline ] && grep -q 'BOOT_IMAGE=' /proc/cmdline; then
|
||||
opt_kernel=$(grep -Eo 'BOOT_IMAGE=[^ ]+' /proc/cmdline | cut -d= -f2)
|
||||
_debug "found opt_kernel=$opt_kernel in /proc/cmdline"
|
||||
# if the boot partition is within a btrfs subvolume, strip the subvolume name
|
||||
# if /boot is a separate subvolume, the remainder of the code in this section should handle it
|
||||
if echo "$opt_kernel" | grep -q "^/@"; then opt_kernel=$(echo "$opt_kernel" | sed "s:/@[^/]*::"); fi
|
||||
# if we have a dedicated /boot partition, our bootloader might have just called it /
|
||||
# so try to prepend /boot and see if we find anything
|
||||
[ -e "/boot/$opt_kernel" ] && opt_kernel="/boot/$opt_kernel"
|
||||
@ -1327,6 +1339,8 @@ if [ "$opt_live" = 1 ]; then
|
||||
[ -e "/boot/vmlinuz-linux" ] && opt_kernel="/boot/vmlinuz-linux"
|
||||
# Arch aarch64:
|
||||
[ -e "/boot/Image" ] && opt_kernel="/boot/Image"
|
||||
# Arch armv5/armv7:
|
||||
[ -e "/boot/zImage" ] && opt_kernel="/boot/zImage"
|
||||
# Linux-Libre:
|
||||
[ -e "/boot/vmlinuz-linux-libre" ] && opt_kernel="/boot/vmlinuz-linux-libre"
|
||||
# pine64
|
||||
@ -1494,18 +1508,39 @@ number_of_cpus()
|
||||
# $2 - cpu index
|
||||
write_msr()
|
||||
{
|
||||
# _msr must be in hex, in the form 0x1234:
|
||||
_msr="$1"
|
||||
# cpu index, starting from 0:
|
||||
_cpu="$2"
|
||||
if [ "$os" != Linux ]; then
|
||||
cpucontrol -m "$1=0" "/dev/cpuctl$2" >/dev/null 2>&1; ret=$?
|
||||
cpucontrol -m "$_msr=0" "/dev/cpuctl$_cpu" >/dev/null 2>&1; ret=$?
|
||||
else
|
||||
# for Linux
|
||||
# convert to decimal
|
||||
_msrindex=$(( $1 ))
|
||||
if [ ! -w /dev/cpu/"$2"/msr ]; then
|
||||
_msr=$(( _msr ))
|
||||
if [ ! -w /dev/cpu/"$_cpu"/msr ]; then
|
||||
ret=200 # permission error
|
||||
# if wrmsr is available, use it
|
||||
elif which wrmsr >/dev/null 2>&1 && [ "$SMC_NO_WRMSR" != 1 ]; then
|
||||
_debug "write_msr: using wrmsr"
|
||||
wrmsr $_msr 0 2>/dev/null; ret=$?
|
||||
# or if we have perl, use it, any 5.x version will work
|
||||
elif which perl >/dev/null 2>&1 && [ "$SMC_NO_PERL" != 1 ]; then
|
||||
_debug "write_msr: using perl"
|
||||
ret=1
|
||||
perl -e "open(M,'>','/dev/cpu/$_cpu/msr') and seek(M,$_msr,0) and exit(syswrite(M,pack('H16',0)))"; [ $? -eq 8 ] && ret=0
|
||||
# fallback to dd if it supports seek_bytes
|
||||
elif dd if=/dev/null of=/dev/null bs=8 count=1 seek="$_msr" oflag=seek_bytes 2>/dev/null; then
|
||||
_debug "write_msr: using dd"
|
||||
dd if=/dev/zero of=/dev/cpu/"$_cpu"/msr bs=8 count=1 seek="$_msr" oflag=seek_bytes 2>/dev/null; ret=$?
|
||||
else
|
||||
dd if=/dev/zero of=/dev/cpu/"$2"/msr bs=8 count=1 seek="$_msrindex" oflag=seek_bytes 2>/dev/null; ret=$?
|
||||
_debug "write_msr: got no wrmsr, perl or recent enough dd!"
|
||||
return 201 # missing tool error
|
||||
fi
|
||||
fi
|
||||
_debug "write_msr: for cpu $2 on msr $1 ($_msrindex), ret=$ret"
|
||||
# normalize ret
|
||||
[ "$ret" != 0 ] && ret=1
|
||||
_debug "write_msr: for cpu $_cpu on msr $_msr, ret=$ret"
|
||||
return $ret
|
||||
}
|
||||
|
||||
@ -1526,12 +1561,27 @@ read_msr()
|
||||
_msr_l="$(( _msr_l >> 24 & 0xFF )) $(( _msr_l >> 16 & 0xFF )) $(( _msr_l >> 8 & 0xFF )) $(( _msr_l & 0xFF ))"
|
||||
read_msr_value="$_msr_h $_msr_l"
|
||||
else
|
||||
# for Linux
|
||||
# convert to decimal
|
||||
_msr=$(( _msr ))
|
||||
if [ ! -r /dev/cpu/"$_cpu"/msr ]; then
|
||||
return 200 # permission error
|
||||
# if rdmsr is available, use it
|
||||
elif which rdmsr >/dev/null 2>&1 && [ "$SMC_NO_RDMSR" != 1 ]; then
|
||||
_debug "read_msr: using rdmsr"
|
||||
read_msr_value=$(rdmsr -r $_msr 2>/dev/null | od -t u8 -A n)
|
||||
# or if we have perl, use it, any 5.x version will work
|
||||
elif which perl >/dev/null 2>&1 && [ "$SMC_NO_PERL" != 1 ]; then
|
||||
_debug "read_msr: using perl"
|
||||
read_msr_value=$(perl -e "open(M,'<','/dev/cpu/$_cpu/msr') and seek(M,$_msr,0) and read(M,\$_,8) and print" | od -t u8 -A n)
|
||||
# fallback to dd if it supports skip_bytes
|
||||
elif dd if=/dev/null of=/dev/null bs=8 count=1 skip="$_msr" iflag=skip_bytes 2>/dev/null; then
|
||||
_debug "read_msr: using dd"
|
||||
read_msr_value=$(dd if=/dev/cpu/"$_cpu"/msr bs=8 count=1 skip="$_msr" iflag=skip_bytes 2>/dev/null | od -t u8 -A n)
|
||||
else
|
||||
_debug "read_msr: got no rdmsr, perl or recent enough dd!"
|
||||
return 201 # missing tool error
|
||||
fi
|
||||
read_msr_value=$(dd if=/dev/cpu/"$_cpu"/msr bs=8 count=1 skip="$_msr" iflag=skip_bytes 2>/dev/null | od -t u1 -A n)
|
||||
if [ -z "$read_msr_value" ]; then
|
||||
# MSR doesn't exist, don't check for $? because some versions of dd still return 0!
|
||||
return 1
|
||||
@ -1564,9 +1614,7 @@ check_cpu()
|
||||
pstatus yellow UNKNOWN "is msr kernel module available?"
|
||||
else
|
||||
# the new MSR 'SPEC_CTRL' is at offset 0x48
|
||||
# here we use dd, it's the same as using 'rdmsr 0x48' but without needing the rdmsr tool
|
||||
# if we get a read error, the MSR is not there. bs has to be 8 for msr
|
||||
# skip=9 because 8*9=72=0x48
|
||||
# we check if we have it for all cpus
|
||||
val=0
|
||||
cpu_mismatch=0
|
||||
for i in $(seq 0 "$idx_max_cpu")
|
||||
@ -1593,6 +1641,9 @@ check_cpu()
|
||||
elif [ $val -eq 200 ]; then
|
||||
pstatus yellow UNKNOWN "is msr kernel module available?"
|
||||
spec_ctrl_msr=-1
|
||||
elif [ $val -eq 201 ]; then
|
||||
pstatus yellow UNKNOWN "missing tool, install either msr-tools or perl"
|
||||
spec_ctrl_msr=-1
|
||||
else
|
||||
spec_ctrl_msr=0
|
||||
pstatus yellow NO
|
||||
@ -1654,8 +1705,7 @@ check_cpu()
|
||||
pstatus yellow UNKNOWN "is msr kernel module available?"
|
||||
else
|
||||
# the new MSR 'PRED_CTRL' is at offset 0x49, write-only
|
||||
# here we use dd, it's the same as using 'wrmsr 0x49 0' but without needing the wrmsr tool
|
||||
# if we get a write error, the MSR is not there
|
||||
# we test if of all cpus
|
||||
val=0
|
||||
cpu_mismatch=0
|
||||
for i in $(seq 0 "$idx_max_cpu")
|
||||
@ -1816,27 +1866,28 @@ check_cpu()
|
||||
capabilities_rdcl_no=-1
|
||||
capabilities_ibrs_all=-1
|
||||
capabilities_ssb_no=-1
|
||||
capabilities_rsba=-1
|
||||
if [ "$cpuid_arch_capabilities" = -1 ]; then
|
||||
pstatus yellow UNKNOWN
|
||||
elif [ "$cpuid_arch_capabilities" != 1 ]; then
|
||||
capabilities_rdcl_no=0
|
||||
capabilities_ibrs_all=0
|
||||
capabilities_ssb_no=0
|
||||
capabilities_rsba=0
|
||||
pstatus yellow NO
|
||||
elif [ ! -e /dev/cpu/0/msr ] && [ ! -e /dev/cpuctl0 ]; then
|
||||
spec_ctrl_msr=-1
|
||||
pstatus yellow UNKNOWN "is msr kernel module available?"
|
||||
else
|
||||
# the new MSR 'ARCH_CAPABILITIES' is at offset 0x10a
|
||||
# here we use dd, it's the same as using 'rdmsr 0x10a' but without needing the rdmsr tool
|
||||
# if we get a read error, the MSR is not there. bs has to be 8 for msr
|
||||
# we check if we have it for all cpus
|
||||
val=0
|
||||
val_cap_msr=0
|
||||
cpu_mismatch=0
|
||||
for i in $(seq 0 "$idx_max_cpu")
|
||||
do
|
||||
read_msr 0x10a "$i"; ret=$?
|
||||
capabilities=$(echo "$read_msr_value" | awk '{print $8}')
|
||||
capabilities=$read_msr_value
|
||||
if [ "$i" -eq 0 ]; then
|
||||
val=$ret
|
||||
val_cap_msr=$capabilities
|
||||
@ -1852,12 +1903,14 @@ check_cpu()
|
||||
capabilities_rdcl_no=0
|
||||
capabilities_ibrs_all=0
|
||||
capabilities_ssb_no=0
|
||||
capabilities_rsba=0
|
||||
if [ $val -eq 0 ]; then
|
||||
_debug "capabilities MSR lower byte is $capabilities (decimal)"
|
||||
[ $(( capabilities & 1 )) -eq 1 ] && capabilities_rdcl_no=1
|
||||
[ $(( capabilities & 2 )) -eq 2 ] && capabilities_ibrs_all=1
|
||||
[ $(( capabilities & 16 )) -eq 16 ] && capabilities_ssb_no=1
|
||||
_debug "capabilities says rdcl_no=$capabilities_rdcl_no ibrs_all=$capabilities_ibrs_all ssb_no=$capabilities_ssb_no"
|
||||
_debug "capabilities MSR is $capabilities (decimal)"
|
||||
[ $(( capabilities >> 0 & 1 )) -eq 1 ] && capabilities_rdcl_no=1
|
||||
[ $(( capabilities >> 1 & 1 )) -eq 1 ] && capabilities_ibrs_all=1
|
||||
[ $(( capabilities >> 2 & 1 )) -eq 1 ] && capabilities_rsba=1
|
||||
[ $(( capabilities >> 4 & 1 )) -eq 1 ] && capabilities_ssb_no=1
|
||||
_debug "capabilities says rdcl_no=$capabilities_rdcl_no ibrs_all=$capabilities_ibrs_all ssb_no=$capabilities_ssb_no rsba=$capabilities_rsba"
|
||||
if [ "$capabilities_ibrs_all" = 1 ]; then
|
||||
if [ $cpu_mismatch -eq 0 ]; then
|
||||
pstatus green YES
|
||||
@ -1869,6 +1922,8 @@ check_cpu()
|
||||
fi
|
||||
elif [ $val -eq 200 ]; then
|
||||
pstatus yellow UNKNOWN "is msr kernel module available?"
|
||||
elif [ $val -eq 201 ]; then
|
||||
pstatus yellow UNKNOWN "missing tool, install either msr-tools or perl"
|
||||
else
|
||||
pstatus yellow NO
|
||||
fi
|
||||
@ -1893,6 +1948,15 @@ check_cpu()
|
||||
pstatus yellow NO
|
||||
fi
|
||||
|
||||
_info_nol " * Hypervisor indicates host CPU might be vulnerable to RSB underflow (RSBA): "
|
||||
if [ "$capabilities_rsba" = -1 ]; then
|
||||
pstatus yellow UNKNOWN
|
||||
elif [ "$capabilities_rsba" = 1 ]; then
|
||||
pstatus yellow YES
|
||||
else
|
||||
pstatus blue NO
|
||||
fi
|
||||
|
||||
_info_nol " * CPU microcode is known to cause stability problems: "
|
||||
if is_ucode_blacklisted; then
|
||||
pstatus red YES "$ucode_found"
|
||||
@ -2514,7 +2578,7 @@ check_variant2_linux()
|
||||
fi
|
||||
fi
|
||||
|
||||
if is_skylake_cpu || [ "$opt_verbose" -ge 2 ]; then
|
||||
if is_vulnerable_to_empty_rsb || [ "$opt_verbose" -ge 2 ]; then
|
||||
_info_nol " * Kernel supports RSB filling: "
|
||||
if ! which "${opt_arch_prefix}strings" >/dev/null 2>&1; then
|
||||
pstatus yellow UNKNOWN "missing '${opt_arch_prefix}strings' tool, please install it, usually it's in the binutils package"
|
||||
@ -2541,9 +2605,9 @@ check_variant2_linux()
|
||||
# override status & msg in case CPU is not vulnerable after all
|
||||
pvulnstatus $cve OK "your CPU vendor reported your CPU model as not vulnerable"
|
||||
else
|
||||
if [ "$retpoline" = 1 ] && [ "$retpoline_compiler" = 1 ] && [ "$retp_enabled" != 0 ] && [ -n "$ibpb_enabled" ] && [ "$ibpb_enabled" -ge 1 ] && ( ! is_skylake_cpu || [ -n "$rsb_filling" ] ); then
|
||||
if [ "$retpoline" = 1 ] && [ "$retpoline_compiler" = 1 ] && [ "$retp_enabled" != 0 ] && [ -n "$ibpb_enabled" ] && [ "$ibpb_enabled" -ge 1 ] && ( ! is_vulnerable_to_empty_rsb || [ -n "$rsb_filling" ] ); then
|
||||
pvulnstatus $cve OK "Full retpoline + IBPB are mitigating the vulnerability"
|
||||
elif [ "$retpoline" = 1 ] && [ "$retpoline_compiler" = 1 ] && [ "$retp_enabled" != 0 ] && [ "$opt_paranoid" = 0 ] && ( ! is_skylake_cpu || [ -n "$rsb_filling" ] ); then
|
||||
elif [ "$retpoline" = 1 ] && [ "$retpoline_compiler" = 1 ] && [ "$retp_enabled" != 0 ] && [ "$opt_paranoid" = 0 ] && ( ! is_vulnerable_to_empty_rsb || [ -n "$rsb_filling" ] ); then
|
||||
pvulnstatus $cve OK "Full retpoline is mitigating the vulnerability"
|
||||
if [ -n "$cpuid_ibpb" ]; then
|
||||
_warn "You should enable IBPB to complete retpoline as a Variant 2 mitigation"
|
||||
@ -2573,7 +2637,7 @@ check_variant2_linux()
|
||||
# if we arrive here and didn't already call pvulnstatus, then it's VULN, let's explain why
|
||||
if [ "$pvulnstatus_last_cve" != "$cve" ]; then
|
||||
# explain what's needed for this CPU
|
||||
if is_skylake_cpu; then
|
||||
if is_vulnerable_to_empty_rsb; then
|
||||
pvulnstatus $cve VULN "IBRS+IBPB or retpoline+IBPB+RBS filling, is needed to mitigate the vulnerability"
|
||||
explain "To mitigate this vulnerability, you need either IBRS + IBPB, both requiring hardware support from your CPU microcode in addition to kernel support, or a kernel compiled with retpoline and IBPB, with retpoline requiring a retpoline-aware compiler (re-run this script with -v to know if your version of gcc is retpoline-aware) and IBPB requiring hardware support from your CPU microcode. You also need a recent-enough kernel that supports RSB filling if you plan to use retpoline. For Skylake+ CPUs, the IBRS + IBPB approach is generally preferred as it guarantees complete protection, and the performance impact is not as high as with older CPUs in comparison with retpoline. More information about how to enable the missing bits for those two possible mitigations on your system follow. You only need to take one of the two approaches."
|
||||
elif is_zen_cpu; then
|
||||
|
Reference in New Issue
Block a user