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

