From 8caeb440a176cb7f8908403da51106c74e2b5cb8 Mon Sep 17 00:00:00 2001 From: Hilton Chain <hako@ultrarare.space> Date: Thu, 27 Jul 2023 14:45:14 +0800 Subject: [PATCH] Replace fwdb downloader with a local file option. Also warn about non-free software. --- spectre-meltdown-checker.sh | 253 +++--------------------------------- 1 file changed, 17 insertions(+), 236 deletions(-) diff --git a/spectre-meltdown-checker.sh b/spectre-meltdown-checker.sh index e7b6b33..33bdf71 100755 --- a/spectre-meltdown-checker.sh +++ b/spectre-meltdown-checker.sh @@ -23,9 +23,6 @@ exit_cleanup() [ -n "${dumped_config:-}" ] && [ -f "$dumped_config" ] && rm -f "$dumped_config" [ -n "${kerneltmp:-}" ] && [ -f "$kerneltmp" ] && rm -f "$kerneltmp" [ -n "${kerneltmp2:-}" ] && [ -f "$kerneltmp2" ] && rm -f "$kerneltmp2" - [ -n "${mcedb_tmp:-}" ] && [ -f "$mcedb_tmp" ] && rm -f "$mcedb_tmp" - [ -n "${intel_tmp:-}" ] && [ -d "$intel_tmp" ] && rm -rf "$intel_tmp" - [ -n "${linuxfw_tmp:-}" ] && [ -f "$linuxfw_tmp" ] && rm -f "$linuxfw_tmp" [ "${mounted_debugfs:-}" = 1 ] && umount /sys/kernel/debug 2>/dev/null [ "${mounted_procfs:-}" = 1 ] && umount "$procfs" 2>/dev/null [ "${insmod_cpuid:-}" = 1 ] && rmmod cpuid 2>/dev/null @@ -97,9 +94,9 @@ show_usage() --vmm [auto,yes,no] override the detection of the presence of a hypervisor, default: auto --allow-msr-write allow probing for write-only MSRs, this might produce kernel logs or be blocked by your system --cpu [#,all] interact with CPUID and MSR of CPU core number #, or all (default: CPU core 0) - --update-fwdb update our local copy of the CPU microcodes versions database (using the awesome - MCExtractor project and the Intel firmwares GitHub repository) - --update-builtin-fwdb same as --update-fwdb but update builtin DB inside the script itself + --with-fwdb FILE read CPU microcode version information from FILE + Note that most CPU microcode is distributed as binaries without source -- relying on + such non-free firmware as sole protection against security vulnerabilities is ill-advised. --dump-mock-data used to mimick a CPU on an other system, mainly used to help debugging this script Return codes: @@ -858,217 +855,6 @@ show_header() _info } -# Family-Model-Stepping to CPUID -# prints CPUID in base-10 to stdout -fms2cpuid() -{ - _family="$1" - _model="$2" - _stepping="$3" - - if [ "$(( _family ))" -le 15 ]; then - _extfamily=0 - _lowfamily=$(( _family )) - else - # when we have a family > 0xF, then lowfamily is stuck at 0xF - # and extfamily is ADDED to it (as in "+"), to ensure old software - # never sees a lowfamily < 0xF for newer families - _lowfamily=15 - _extfamily=$(( (_family) - 15 )) - fi - _extmodel=$(( (_model & 0xF0 ) >> 4 )) - _lowmodel=$(( (_model & 0x0F ) >> 0 )) - echo $(( (_stepping & 0x0F) | (_lowmodel << 4) | (_lowfamily << 8) | (_extmodel << 16) | (_extfamily << 20) )) -} - -[ -z "$HOME" ] && HOME="$(getent passwd "$(whoami)" | cut -d: -f6)" -mcedb_cache="$HOME/.mcedb" -update_fwdb() -{ - show_header - - set -e - - if [ -r "$mcedb_cache" ]; then - previous_dbversion=$(awk '/^# %%% MCEDB / { print $4 }' "$mcedb_cache") - fi - - # first, download the MCE.db from the excellent platomav's MCExtractor project - mcedb_tmp="$(mktemp -t smc-mcedb-XXXXXX)" - mcedb_url='https://github.com/platomav/MCExtractor/raw/master/MCE.db' - _info_nol "Fetching MCE.db from the MCExtractor project... " - if command -v wget >/dev/null 2>&1; then - wget -q "$mcedb_url" -O "$mcedb_tmp"; ret=$? - elif command -v curl >/dev/null 2>&1; then - curl -sL "$mcedb_url" -o "$mcedb_tmp"; ret=$? - elif command -v fetch >/dev/null 2>&1; then - fetch -q "$mcedb_url" -o "$mcedb_tmp"; ret=$? - else - echo ERROR "please install one of \`wget\`, \`curl\` of \`fetch\` programs" - return 1 - fi - if [ "$ret" != 0 ]; then - echo ERROR "error $ret while downloading MCE.db" - return $ret - fi - echo DONE - - # second, get the Intel firmwares from GitHub - intel_tmp="$(mktemp -d -t smc-intelfw-XXXXXX)" - intel_url="https://github.com/intel/Intel-Linux-Processor-Microcode-Data-Files/archive/main.zip" - _info_nol "Fetching Intel firmwares... " - ## https://github.com/intel/Intel-Linux-Processor-Microcode-Data-Files.git - if command -v wget >/dev/null 2>&1; then - wget -q "$intel_url" -O "$intel_tmp/fw.zip"; ret=$? - elif command -v curl >/dev/null 2>&1; then - curl -sL "$intel_url" -o "$intel_tmp/fw.zip"; ret=$? - elif command -v fetch >/dev/null 2>&1; then - fetch -q "$intel_url" -o "$intel_tmp/fw.zip"; ret=$? - else - echo ERROR "please install one of \`wget\`, \`curl\` of \`fetch\` programs" - return 1 - fi - if [ "$ret" != 0 ]; then - echo ERROR "error $ret while downloading Intel firmwares" - return $ret - fi - echo DONE - - # now extract MCEdb contents using sqlite - _info_nol "Extracting MCEdb data... " - if ! command -v sqlite3 >/dev/null 2>&1; then - echo ERROR "please install the \`sqlite3\` program" - return 1 - fi - mcedb_revision=$(sqlite3 "$mcedb_tmp" "SELECT \"revision\" from \"MCE\"") - if [ -z "$mcedb_revision" ]; then - echo ERROR "downloaded file seems invalid" - return 1 - fi - sqlite3 "$mcedb_tmp" "ALTER TABLE \"Intel\" ADD COLUMN \"origin\" TEXT" - sqlite3 "$mcedb_tmp" "ALTER TABLE \"AMD\" ADD COLUMN \"origin\" TEXT" - sqlite3 "$mcedb_tmp" "UPDATE \"Intel\" SET \"origin\"='mce'" - sqlite3 "$mcedb_tmp" "UPDATE \"AMD\" SET \"origin\"='mce'" - - echo OK "MCExtractor database revision $mcedb_revision" - - # parse Intel firmwares to get their versions - _info_nol "Integrating Intel firmwares data to db... " - if ! command -v unzip >/dev/null 2>&1; then - echo ERROR "please install the \`unzip\` program" - return 1 - fi - ( cd "$intel_tmp" && unzip fw.zip >/dev/null; ) - if ! [ -d "$intel_tmp/Intel-Linux-Processor-Microcode-Data-Files-main/intel-ucode" ]; then - echo ERROR "expected the 'intel-ucode' folder in the downloaded zip file" - return 1 - fi - - if ! command -v iucode_tool >/dev/null 2>&1; then - if ! command -v iucode-tool >/dev/null 2>&1; then - echo ERROR "please install the \`iucode-tool\` program" - return 1 - else - iucode_tool="iucode-tool" - fi - else - iucode_tool="iucode_tool" - fi - # 079/001: sig 0x000106c2, pf_mask 0x01, 2009-04-10, rev 0x0217, size 5120 - # 078/004: sig 0x000106ca, pf_mask 0x10, 2009-08-25, rev 0x0107, size 5120 - $iucode_tool -l "$intel_tmp/Intel-Linux-Processor-Microcode-Data-Files-main/intel-ucode" | grep -wF sig | while read -r _line - do - _line=$( echo "$_line" | tr -d ',') - _cpuid=$( echo "$_line" | awk '{print $3}') - _cpuid=$(( _cpuid )) - _cpuid=$(printf "0x%08X" "$_cpuid") - _date=$( echo "$_line" | awk '{print $6}' | tr -d '-') - _version=$(echo "$_line" | awk '{print $8}') - _version=$(( _version )) - _version=$(printf "0x%08X" "$_version") - _sqlstm="$(printf "INSERT INTO \"Intel\" (\"origin\",\"cpuid\",\"version\",\"yyyymmdd\") VALUES ('%s','%s','%s','%s');" "intel" "$(printf "%08X" "$_cpuid")" "$(printf "%08X" "$_version")" "$_date")" - sqlite3 "$mcedb_tmp" "$_sqlstm" - done - _intel_timestamp=$(stat -c %Y "$intel_tmp/Intel-Linux-Processor-Microcode-Data-Files-main/license" 2>/dev/null) - if [ -n "$_intel_timestamp" ]; then - # use this date, it matches the last commit date - _intel_latest_date=$(date +%Y%m%d -d @"$_intel_timestamp") - else - echo "Falling back to the latest microcode date" - _intel_latest_date=$(sqlite3 "$mcedb_tmp" "SELECT \"yyyymmdd\" FROM \"Intel\" WHERE \"origin\"='intel' ORDER BY \"yyyymmdd\" DESC LIMIT 1;") - fi - echo DONE "(version $_intel_latest_date)" - - # now parse the most recent linux-firmware amd-ucode README file - _info_nol "Fetching latest amd-ucode README from linux-firmware project... " - linuxfw_url="https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/tree/amd-ucode/README" - linuxfw_tmp=$(mktemp -t smc-linuxfw-XXXXXX) - if command -v wget >/dev/null 2>&1; then - wget -q "$linuxfw_url" -O "$linuxfw_tmp"; ret=$? - elif command -v curl >/dev/null 2>&1; then - curl -sL "$linuxfw_url" -o "$linuxfw_tmp"; ret=$? - elif command -v fetch >/dev/null 2>&1; then - fetch -q "$linuxfw_url" -o "$linuxfw_tmp"; ret=$? - else - echo ERROR "please install one of \`wget\`, \`curl\` of \`fetch\` programs" - return 1 - fi - if [ "$ret" != 0 ]; then - echo ERROR "error $ret while downloading linux-firmware README" - return $ret - fi - echo DONE - - _info_nol "Parsing the README... " - nbfound=0 - for line in $(grep -E 'Family=0x[0-9a-f]+ Model=0x[0-9a-f]+ Stepping=0x[0-9a-f]+: Patch=0x[0-9a-f]+' "$linuxfw_tmp" | tr " " ","); do - _debug "Parsing line $line" - _family=$( echo "$line" | grep -Eoi 'Family=0x[0-9a-f]+' | cut -d= -f2) - _model=$( echo "$line" | grep -Eoi 'Model=0x[0-9a-f]+' | cut -d= -f2) - _stepping=$(echo "$line" | grep -Eoi 'Stepping=0x[0-9a-f]+' | cut -d= -f2) - _version=$( echo "$line" | grep -Eoi 'Patch=0x[0-9a-f]+' | cut -d= -f2) - _version=$(printf "0x%08X" "$(( _version ))") - _cpuid=$(fms2cpuid "$_family" "$_model" "$_stepping") - _cpuid=$(printf "0x%08X" "$_cpuid") - _date="20000101" - _sqlstm="$(printf "INSERT INTO \"AMD\" (\"origin\",\"cpuid\",\"version\",\"yyyymmdd\") VALUES ('%s','%s','%s','%s');" "linux-firmware" "$(printf "%08X" "$_cpuid")" "$(printf "%08X" "$_version")" "$_date")" - _debug "family $_family model $_model stepping $_stepping cpuid $_cpuid" - _debug "$_sqlstm" - sqlite3 "$mcedb_tmp" "$_sqlstm" - nbfound=$((nbfound + 1)) - unset _family _model _stepping _version _cpuid _date _sqlstm - done - echo "found $nbfound microcodes" - unset nbfound - - dbversion="$mcedb_revision+i$_intel_latest_date" - - if [ "$1" != builtin ] && [ -n "$previous_dbversion" ] && [ "$previous_dbversion" = "v$dbversion" ]; then - echo "We already have this version locally, no update needed" - return 0 - fi - - _info_nol "Building local database... " - { - echo "# Spectre & Meltdown Checker"; - echo "# %%% MCEDB v$dbversion"; - # ensure the official Intel DB always has precedence over mcedb, even if mcedb has seen a more recent fw - sqlite3 "$mcedb_tmp" "DELETE FROM \"Intel\" WHERE \"origin\"!='intel' AND \"cpuid\" IN (SELECT \"cpuid\" FROM \"Intel\" WHERE \"origin\"='intel' GROUP BY \"cpuid\" ORDER BY \"cpuid\" ASC);" - # we'll use the more recent fw for Intel and AMD - sqlite3 "$mcedb_tmp" "SELECT '# I,0x'||\"t1\".\"cpuid\"||',0x'||MAX(\"t1\".\"version\")||','||\"t1\".\"yyyymmdd\" FROM \"Intel\" AS \"t1\" LEFT OUTER JOIN \"Intel\" AS \"t2\" ON \"t2\".\"cpuid\"=\"t1\".\"cpuid\" AND \"t2\".\"yyyymmdd\" > \"t1\".\"yyyymmdd\" WHERE \"t2\".\"yyyymmdd\" IS NULL GROUP BY \"t1\".\"cpuid\" ORDER BY \"t1\".\"cpuid\" ASC;" | grep -v '^# .,0x00000000,'; - sqlite3 "$mcedb_tmp" "SELECT '# A,0x'||\"t1\".\"cpuid\"||',0x'||MAX(\"t1\".\"version\")||','||\"t1\".\"yyyymmdd\" FROM \"AMD\" AS \"t1\" LEFT OUTER JOIN \"AMD\" AS \"t2\" ON \"t2\".\"cpuid\"=\"t1\".\"cpuid\" AND \"t2\".\"yyyymmdd\" > \"t1\".\"yyyymmdd\" WHERE \"t2\".\"yyyymmdd\" IS NULL GROUP BY \"t1\".\"cpuid\" ORDER BY \"t1\".\"cpuid\" ASC;" | grep -v '^# .,0x00000000,'; - } > "$mcedb_cache" - echo DONE "(version $dbversion)" - - if [ "$1" = builtin ]; then - newfile=$(mktemp -t smc-builtin-XXXXXX) - awk '/^# %%% MCEDB / { exit }; { print }' "$0" > "$newfile" - awk '{ if (NR>1) { print } }' "$mcedb_cache" >> "$newfile" - cat "$newfile" > "$0" - rm -f "$newfile" - fi -} - parse_opt_file() { # parse_opt_file option_name option_value @@ -1158,12 +944,15 @@ while [ -n "${1:-}" ]; do # deprecated, kept for compatibility opt_explain=0 shift - elif [ "$1" = "--update-fwdb" ] || [ "$1" = "--update-mcedb" ]; then - update_fwdb - exit $? - elif [ "$1" = "--update-builtin-fwdb" ] || [ "$1" = "--update-builtin-mcedb" ]; then - update_fwdb builtin - exit $? + elif [ "$1" = "--with-fwdb" ] || [ "$1" = "--with-mcedb" ]; then + opt_fwdb=$2 + if [ -f "$opt_fwdb" ]; then + mcedb_cache=$2 + else + echo "$0: error: --with-fwdb should be a file, got '$opt_fwdb'" >&2 + exit 255 + fi + shift 2 elif [ "$1" = "--dump-mock-data" ]; then opt_mock=1 shift @@ -2192,21 +1981,11 @@ is_xen_domU() fi } -builtin_dbversion=$(awk '/^# %%% MCEDB / { print $4 }' "$0") if [ -r "$mcedb_cache" ]; then # we have a local cache file, but it might be older than the builtin version we have local_dbversion=$( awk '/^# %%% MCEDB / { print $4 }' "$mcedb_cache") - # sort -V sorts by version number - older_dbversion=$(printf "%b\n%b" "$local_dbversion" "$builtin_dbversion" | sort -V | head -n1) - if [ "$older_dbversion" = "$builtin_dbversion" ]; then - mcedb_source="$mcedb_cache" - mcedb_info="local firmwares DB $local_dbversion" - fi -fi -# if mcedb_source is not set, either we don't have a local cached db, or it is older than the builtin db -if [ -z "${mcedb_source:-}" ]; then - mcedb_source="$0" - mcedb_info="builtin firmwares DB $builtin_dbversion" + mcedb_source="$mcedb_cache" + mcedb_info="local firmwares DB $local_dbversion" fi read_mcedb() { @@ -2222,7 +2001,9 @@ is_latest_known_ucode() return 2 fi ucode_latest="latest microcode version for your CPU model is unknown" - if is_intel; then + if [ -z "$mcedb_source" ]; then + return 2 + elif is_intel; then cpu_brand_prefix=I elif is_amd; then cpu_brand_prefix=A -- 2.41.0