feat(mock): add --dump-mock-data

This commit is contained in:
Stéphane Lesimple 2019-05-24 10:15:02 +02:00
parent dfe0d10f2a
commit d69ea67101
1 changed files with 113 additions and 25 deletions

View File

@ -86,6 +86,7 @@ show_usage()
--vmm [auto,yes,no] override the detection of the presence of a hypervisor (for CVE-2018-3646), default: auto --vmm [auto,yes,no] override the detection of the presence of a hypervisor (for CVE-2018-3646), default: auto
--update-mcedb update our local copy of the CPU microcodes versions database (from the awesome MCExtractor project) --update-mcedb update our local copy of the CPU microcodes versions database (from the awesome MCExtractor project)
--update-builtin-mcedb same as --update-mcedb but update builtin DB inside the script itself --update-builtin-mcedb same as --update-mcedb but update builtin DB inside the script itself
--dump-mock-data used to mimick a CPU on an other system, mainly used to help debugging this script
Return codes: Return codes:
0 (not vulnerable), 2 (vulnerable), 3 (unknown), 255 (error) 0 (not vulnerable), 2 (vulnerable), 3 (unknown), 255 (error)
@ -150,6 +151,7 @@ opt_no_hw=0
opt_vmm=-1 opt_vmm=-1
opt_explain=0 opt_explain=0
opt_paranoid=0 opt_paranoid=0
opt_mock=0
global_critical=0 global_critical=0
global_unknown=0 global_unknown=0
@ -816,6 +818,9 @@ while [ -n "$1" ]; do
elif [ "$1" = "--update-builtin-mcedb" ]; then elif [ "$1" = "--update-builtin-mcedb" ]; then
update_mcedb builtin update_mcedb builtin
exit $? exit $?
elif [ "$1" = "--dump-mock-data" ]; then
opt_mock=1
shift
elif [ "$1" = "--explain" ]; then elif [ "$1" = "--explain" ]; then
opt_explain=1 opt_explain=1
shift shift
@ -1216,6 +1221,8 @@ read_cpuid()
_cpuid="$(eval echo \$$_mockvarname)" _cpuid="$(eval echo \$$_mockvarname)"
_debug "read_cpuid: MOCKING enabled for leaf $_leaf, will return $_cpuid" _debug "read_cpuid: MOCKING enabled for leaf $_leaf, will return $_cpuid"
mocked=1 mocked=1
else
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_CPUID_${_leaf}='$_cpuid'")
fi fi
[ -z "$_cpuid" ] && return 2 [ -z "$_cpuid" ] && return 2
# get the value of the register we want # get the value of the register we want
@ -1304,26 +1311,36 @@ parse_cpu_details()
cpu_friendly_name="$SMC_MOCK_CPU_FRIENDLY_NAME" cpu_friendly_name="$SMC_MOCK_CPU_FRIENDLY_NAME"
_debug "parse_cpu_details: MOCKING cpu friendly name to $cpu_friendly_name" _debug "parse_cpu_details: MOCKING cpu friendly name to $cpu_friendly_name"
mocked=1 mocked=1
else
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_CPU_FRIENDLY_NAME='$cpu_friendly_name'")
fi fi
if [ -n "$SMC_MOCK_CPU_VENDOR" ]; then if [ -n "$SMC_MOCK_CPU_VENDOR" ]; then
cpu_vendor="$SMC_MOCK_CPU_VENDOR" cpu_vendor="$SMC_MOCK_CPU_VENDOR"
_debug "parse_cpu_details: MOCKING cpu vendor to $cpu_vendor" _debug "parse_cpu_details: MOCKING cpu vendor to $cpu_vendor"
mocked=1 mocked=1
else
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_CPU_VENDOR='$cpu_vendor'")
fi fi
if [ -n "$SMC_MOCK_CPU_FAMILY" ]; then if [ -n "$SMC_MOCK_CPU_FAMILY" ]; then
cpu_family="$SMC_MOCK_CPU_FAMILY" cpu_family="$SMC_MOCK_CPU_FAMILY"
_debug "parse_cpu_details: MOCKING cpu family to $cpu_family" _debug "parse_cpu_details: MOCKING cpu family to $cpu_family"
mocked=1 mocked=1
else
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_CPU_FAMILY='$cpu_family'")
fi fi
if [ -n "$SMC_MOCK_CPU_MODEL" ]; then if [ -n "$SMC_MOCK_CPU_MODEL" ]; then
cpu_model="$SMC_MOCK_CPU_MODEL" cpu_model="$SMC_MOCK_CPU_MODEL"
_debug "parse_cpu_details: MOCKING cpu model to $cpu_model" _debug "parse_cpu_details: MOCKING cpu model to $cpu_model"
mocked=1 mocked=1
else
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_CPU_MODEL='$cpu_model'")
fi fi
if [ -n "$SMC_MOCK_CPU_STEPPING" ]; then if [ -n "$SMC_MOCK_CPU_STEPPING" ]; then
cpu_stepping="$SMC_MOCK_CPU_STEPPING" cpu_stepping="$SMC_MOCK_CPU_STEPPING"
_debug "parse_cpu_details: MOCKING cpu stepping to $cpu_stepping" _debug "parse_cpu_details: MOCKING cpu stepping to $cpu_stepping"
mocked=1 mocked=1
else
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_CPU_STEPPING='$cpu_stepping'")
fi fi
# get raw cpuid, it's always useful (referenced in the Intel doc for firmware updates for example) # get raw cpuid, it's always useful (referenced in the Intel doc for firmware updates for example)
@ -1357,6 +1374,8 @@ parse_cpu_details()
cpu_ucode="$SMC_MOCK_CPU_UCODE" cpu_ucode="$SMC_MOCK_CPU_UCODE"
_debug "parse_cpu_details: MOCKING cpu ucode to $cpu_ucode" _debug "parse_cpu_details: MOCKING cpu ucode to $cpu_ucode"
mocked=1 mocked=1
else
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_CPU_UCODE='$cpu_ucode'")
fi fi
echo "$cpu_ucode" | grep -q ^0x && cpu_ucode=$(( cpu_ucode )) echo "$cpu_ucode" | grep -q ^0x && cpu_ucode=$(( cpu_ucode ))
@ -1912,7 +1931,22 @@ sys_interface_check()
mode="$3" mode="$3"
msg='' msg=''
fullmsg='' fullmsg=''
[ "$opt_live" = 1 ] && [ "$opt_no_sysfs" = 0 ] && [ -r "$file" ] || return 1
if [ "$opt_live" = 1 ] && [ "$opt_no_sysfs" = 0 ] && [ -r "$file" ]; then
:
else
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_SYSFS_$(basename "$file")_RET=1")
return 1
fi
_mockvarname="SMC_MOCK_SYSFS_$(basename "$file")_RET"
# shellcheck disable=SC2086
if [ -n "$(eval echo \$$_mockvarname)" ]; then
_debug "sysfs: MOCKING enabled for $file func returns $(eval echo \$$_mockvarname)"
mocked=1
return "$(eval echo \$$_mockvarname)"
fi
[ -n "$regex" ] || regex='.*' [ -n "$regex" ] || regex='.*'
_mockvarname="SMC_MOCK_SYSFS_$(basename "$file")" _mockvarname="SMC_MOCK_SYSFS_$(basename "$file")"
# shellcheck disable=SC2086 # shellcheck disable=SC2086
@ -1924,6 +1958,7 @@ sys_interface_check()
else else
fullmsg=$(cat "$file") fullmsg=$(cat "$file")
msg=$(grep -Eo "$regex" "$file") msg=$(grep -Eo "$regex" "$file")
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_SYSFS_$(basename "$file")='$fullmsg'")
fi fi
if [ "$mode" = silent ]; then if [ "$mode" = silent ]; then
return 0 return 0
@ -1965,94 +2000,128 @@ number_of_cpus()
return "$n" return "$n"
} }
# $1 - msr number # write_msr
# $2 - cpu index # param1 (mandatory): MSR, can be in hex or decimal.
# param2 (optional): CPU index, starting from 0. Default 0.
write_msr() write_msr()
{ {
# _msr must be in hex, in the form 0x1234: _msr_dec=$(( $1 ))
_msr="$1" _msr=$(printf "0x%x" "$_msr_dec")
# cpu index, starting from 0:
_cpu="$2" _cpu="$2"
[ -z "$_cpu" ] && _cpu=0
_mockvarname="SMC_MOCK_WRMSR_${_msr}_RET"
# shellcheck disable=SC2086
if [ -n "$(eval echo \$$_mockvarname)" ]; then
_debug "write_msr: MOCKING enabled for msr $_msr func returns $(eval echo \$$_mockvarname)"
mocked=1
return "$(eval echo \$$_mockvarname)"
fi
if [ "$os" != Linux ]; then if [ "$os" != Linux ]; then
cpucontrol -m "$_msr=0" "/dev/cpuctl$_cpu" >/dev/null 2>&1; ret=$? cpucontrol -m "$_msr=0" "/dev/cpuctl$_cpu" >/dev/null 2>&1; ret=$?
else else
# for Linux # for Linux
# convert to decimal # convert to decimal
_msr=$(( _msr ))
if [ ! -w /dev/cpu/"$_cpu"/msr ]; then if [ ! -w /dev/cpu/"$_cpu"/msr ]; then
ret=200 # permission error ret=200 # permission error
# if wrmsr is available, use it # if wrmsr is available, use it
elif command -v wrmsr >/dev/null 2>&1 && [ "$SMC_NO_WRMSR" != 1 ]; then elif command -v wrmsr >/dev/null 2>&1 && [ "$SMC_NO_WRMSR" != 1 ]; then
_debug "write_msr: using wrmsr" _debug "write_msr: using wrmsr"
wrmsr $_msr 0 2>/dev/null; ret=$? wrmsr $_msr_dec 0 2>/dev/null; ret=$?
# or if we have perl, use it, any 5.x version will work # or if we have perl, use it, any 5.x version will work
elif command -v perl >/dev/null 2>&1 && [ "$SMC_NO_PERL" != 1 ]; then elif command -v perl >/dev/null 2>&1 && [ "$SMC_NO_PERL" != 1 ]; then
_debug "write_msr: using perl" _debug "write_msr: using perl"
ret=1 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 perl -e "open(M,'>','/dev/cpu/$_cpu/msr') and seek(M,$_msr_dec,0) and exit(syswrite(M,pack('H16',0)))"; [ $? -eq 8 ] && ret=0
# fallback to dd if it supports seek_bytes # 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 elif dd if=/dev/null of=/dev/null bs=8 count=1 seek="$_msr_dec" oflag=seek_bytes 2>/dev/null; then
_debug "write_msr: using dd" _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=$? dd if=/dev/zero of=/dev/cpu/"$_cpu"/msr bs=8 count=1 seek="$_msr_dec" oflag=seek_bytes 2>/dev/null; ret=$?
else else
_debug "write_msr: got no wrmsr, perl or recent enough dd!" _debug "write_msr: got no wrmsr, perl or recent enough dd!"
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_WRMSR_${_msr}_RET=201")
return 201 # missing tool error return 201 # missing tool error
fi fi
fi fi
# normalize ret # normalize ret
[ "$ret" != 0 ] && ret=1 [ "$ret" != 0 ] && ret=1
_debug "write_msr: for cpu $_cpu on msr $_msr, ret=$ret" _debug "write_msr: for cpu $_cpu on msr $_msr, ret=$ret"
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_WRMSR_${_msr}_RET=$ret")
return $ret return $ret
} }
# read_msr
# param1 (mandatory): MSR, can be in hex or decimal.
# param2 (optional): CPU index, starting from 0. Default 0.
read_msr() read_msr()
{ {
# _msr must be in hex, in the form 0x1234: _msr_dec=$(( $1 ))
_msr="$1" _msr=$(printf "0x%x" "$_msr_dec")
# cpu index, starting from 0:
_cpu="$2" _cpu="$2"
[ -z "$_cpu" ] && _cpu=0
read_msr_value='' read_msr_value=''
_mockvarname="SMC_MOCK_MSR_${_msr}"
_mockvarname="SMC_MOCK_RDMSR_${_msr}"
# shellcheck disable=SC2086 # shellcheck disable=SC2086
if [ -n "$(eval echo \$$_mockvarname)" ]; then if [ -n "$(eval echo \$$_mockvarname)" ]; then
read_msr_value="$(eval echo \$$_mockvarname)" read_msr_value="$(eval echo \$$_mockvarname)"
_debug "read_msr: MOCKING enabled for msr $_msr, returning $read_msr_value" _debug "read_msr: MOCKING enabled for msr $_msr, returning $read_msr_value"
mocked=1 mocked=1
elif [ "$os" != Linux ]; then return 0
else
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_RDMSR_${_msr}='$read_msr_value'")
fi
_mockvarname="SMC_MOCK_RDMSR_${_msr}_RET"
# shellcheck disable=SC2086
if [ -n "$(eval echo \$$_mockvarname)" ] && [ "$(eval echo \$$_mockvarname)" -ne 0 ]; then
_debug "read_msr: MOCKING enabled for msr $_msr func returns $(eval echo \$$_mockvarname)"
mocked=1
return "$(eval echo \$$_mockvarname)"
fi
if [ "$os" != Linux ]; then
# for BSD
_msr=$(cpucontrol -m "$_msr" "/dev/cpuctl$_cpu" 2>/dev/null); ret=$? _msr=$(cpucontrol -m "$_msr" "/dev/cpuctl$_cpu" 2>/dev/null); ret=$?
[ $ret -ne 0 ] && return 1 if [ $ret -ne 0 ]; then
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_RDMSR_${_msr}_RET=1")
return 1
fi
# MSR 0x10: 0x000003e1 0xb106dded # MSR 0x10: 0x000003e1 0xb106dded
_msr_h=$(echo "$_msr" | awk '{print $3}'); _msr_h=$(echo "$_msr" | awk '{print $3}');
_msr_l=$(echo "$_msr" | awk '{print $4}'); _msr_l=$(echo "$_msr" | awk '{print $4}');
read_msr_value=$(( _msr_h << 32 | _msr_l )) read_msr_value=$(( _msr_h << 32 | _msr_l ))
else else
# for Linux # for Linux
# convert to decimal
_msr=$(( _msr ))
if [ ! -r /dev/cpu/"$_cpu"/msr ]; then if [ ! -r /dev/cpu/"$_cpu"/msr ]; then
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_RDMSR_${_msr}_RET=200")
return 200 # permission error return 200 # permission error
# if rdmsr is available, use it # if rdmsr is available, use it
elif command -v rdmsr >/dev/null 2>&1 && [ "$SMC_NO_RDMSR" != 1 ]; then elif command -v rdmsr >/dev/null 2>&1 && [ "$SMC_NO_RDMSR" != 1 ]; then
_debug "read_msr: using rdmsr" _debug "read_msr: using rdmsr"
read_msr_value=$(rdmsr -r $_msr 2>/dev/null | od -t u8 -A n) read_msr_value=$(rdmsr -r $_msr_dec 2>/dev/null | od -t u8 -A n)
# or if we have perl, use it, any 5.x version will work # or if we have perl, use it, any 5.x version will work
elif command -v perl >/dev/null 2>&1 && [ "$SMC_NO_PERL" != 1 ]; then elif command -v perl >/dev/null 2>&1 && [ "$SMC_NO_PERL" != 1 ]; then
_debug "read_msr: using perl" _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) read_msr_value=$(perl -e "open(M,'<','/dev/cpu/$_cpu/msr') and seek(M,$_msr_dec,0) and read(M,\$_,8) and print" | od -t u8 -A n)
# fallback to dd if it supports skip_bytes # 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 elif dd if=/dev/null of=/dev/null bs=8 count=1 skip="$_msr_dec" iflag=skip_bytes 2>/dev/null; then
_debug "read_msr: using dd" _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) read_msr_value=$(dd if=/dev/cpu/"$_cpu"/msr bs=8 count=1 skip="$_msr_dec" iflag=skip_bytes 2>/dev/null | od -t u8 -A n)
else else
_debug "read_msr: got no rdmsr, perl or recent enough dd!" _debug "read_msr: got no rdmsr, perl or recent enough dd!"
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_RDMSR_${_msr}_RET=201")
return 201 # missing tool error return 201 # missing tool error
fi fi
if [ -z "$read_msr_value" ]; then if [ -z "$read_msr_value" ]; then
# MSR doesn't exist, don't check for $? because some versions of dd still return 0! # MSR doesn't exist, don't check for $? because some versions of dd still return 0!
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_RDMSR_${_msr}_RET=1")
return 1 return 1
fi fi
fi fi
_debug "read_msr: MSR=$1 value is $read_msr_value" _debug "read_msr: MSR=$_msr value is $read_msr_value"
return 0 return 0
} }
@ -4463,9 +4532,28 @@ if [ "$bad_accuracy" = 1 ]; then
_warn "We're missing some kernel info (see -v), accuracy might be reduced" _warn "We're missing some kernel info (see -v), accuracy might be reduced"
fi fi
_vars=$(set | grep -Ev '^[A-Z_[:space:]]' | sort | tr "\n" '|') _vars=$(set | grep -Ev '^[A-Z_[:space:]]' | grep -v -F 'mockme=' | sort | tr "\n" '|')
_debug "variables at end of script: $_vars" _debug "variables at end of script: $_vars"
if [ -n "$mockme" ] && [ "$opt_mock" = 1 ]; then
if command -v "gzip" >/dev/null 2>&1; then
# not a useless use of cat: gzipping cpuinfo directly doesn't work well
# shellcheck disable=SC2002
if command -v "base64" >/dev/null 2>&1; then
mock_cpuinfo="$(cat /proc/cpuinfo | gzip -c | base64 -w0)"
elif command -v "uuencode" >/dev/null 2>&1; then
mock_cpuinfo="$(cat /proc/cpuinfo | gzip -c | uuencode -m - | grep -Fv 'begin-base64' | grep -Fxv -- '====' | tr -d "\n")"
fi
fi
if [ -n "$mock_cpuinfo" ]; then
mockme=$(printf "%b\n%b" "$mockme" "SMC_MOCK_CPUINFO='$mock_cpuinfo'")
unset mock_cpuinfo
fi
_info ""
# shellcheck disable=SC2046
_warn "To mock this CPU, set those vars: "$(echo "$mockme" | sort -u)
fi
if [ "$opt_explain" = 0 ]; then if [ "$opt_explain" = 0 ]; then
_info "Need more detailed information about mitigation options? Use --explain" _info "Need more detailed information about mitigation options? Use --explain"
fi fi