enh: add known fixed ucode versions for CVE-2023-23583 (Reptar) and CVE-2024-45332 (BPI)

This commit is contained in:
Stéphane Lesimple
2026-04-04 17:50:04 +02:00
parent 090f109c52
commit cccb3c0081
4 changed files with 135 additions and 26 deletions

View File

@@ -123,10 +123,12 @@ It must always be okay to run this script in a production environment.
Never look at the kernel version string to determine whether it supports a mitigation. This would defeat the script's purpose: it must detect mitigations in unknown, vendor-patched, or backported kernels. Similarly, do not blindly trust what `sysfs` reports when it is possible to verify directly.
### 3. Never hardcode microcode versions
### 3. Never hardcode microcode versions (with one exception)
Never look at the microcode version to determine whether it has the proper mitigation mechanisms. Instead, probe for the mechanisms themselves (CPUID bits, MSR values), as the kernel would.
**Exception**: When a vulnerability is fixed purely by a microcode update and the fix exposes **no** detectable CPUID bit, MSR bit, or ARCH\_CAP flag, then we must hardcode the known-fixing microcode versions for each affected CPU stepping. In this case, build a `<vuln>_ucode_list` table of `FF-MM-SS/platformid_mask,fixed_ucode_version` tuples (sourced from the Intel affected processor list and the Intel-Linux-Processor-Microcode-Data-Files release notes), match against `cpu_cpuid` + `cpu_platformid` in `is_cpu_affected()`, and store the required version in a `g_<vuln>_fixed_ucode_version` global. The CVE check then compares `cpu_ucode` against this threshold. Because Intel never lists EOL CPUs, the microcode list may be incomplete: keep a model blacklist as a fallback so that affected CPUs without a known fix are still flagged as affected (the CVE check should handle the empty `g_<vuln>_fixed_ucode_version` case by reporting VULN with "no microcode update available"). See Reptar (`g_reptar_fixed_ucode_version`) and BPI (`g_bpi_fixed_ucode_version`) for reference implementations.
### 4. Assume affected unless proven otherwise (whitelist approach)
When a CPU is not explicitly known to be unaffected by a vulnerability, assume that it is affected. This conservative default has been the right call since the early Spectre/Meltdown days and remains sound.
@@ -705,7 +707,7 @@ CVEs that need VMM context should call `check_has_vmm` early in their `_linux()`
### Key Rules to Remember
- **Never hardcode kernel or microcode versions** - detect capabilities directly (design principles 2 and 3).
- **Never hardcode kernel or microcode versions** - detect capabilities directly (design principles 2 and 3). Exception: when a microcode fix has no detectable indicator, hardcode fixing versions per CPU (see principle 3).
- **Assume affected by default** - only mark a CPU as unaffected when there is positive evidence (design principle 4).
- **Always handle both live and offline modes** - use `$opt_live` to branch, and print `N/A "not testable in offline mode"` for runtime-only checks when offline.
- **Use `explain()`** when reporting VULN to give actionable remediation advice (see "Cross-Cutting Features" above).

View File

@@ -42,7 +42,7 @@ _is_cpu_affected_cached() {
# Args: $1=cve_id (one of the $g_supported_cve_list items)
# Returns: 0 if affected, 1 if not affected
is_cpu_affected() {
local result cpuid_hex reptar_ucode_list tuple fixed_ucode_ver affected_fmspi affected_fms ucode_platformid_mask affected_cpuid i cpupart cpuarch
local result cpuid_hex reptar_ucode_list bpi_ucode_list tuple fixed_ucode_ver affected_fmspi affected_fms ucode_platformid_mask affected_cpuid i cpupart cpuarch
# if CPU is Intel and is in our dump of the Intel official affected CPUs page, use it:
if is_intel; then
@@ -296,16 +296,17 @@ is_cpu_affected() {
# as the mitigation is only ucode-based and there's no flag exposed by the kernel or by an updated ucode.
# we have to hardcode the truthtable of affected CPUs vs updated ucodes...
# https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/advisory-guidance/redundant-prefix-issue.html
# list taken from:
# list initially taken from:
# https://github.com/intel/Intel-Linux-Processor-Microcode-Data-Files/commit/ece0d294a29a1375397941a4e6f2f7217910bc89#diff-e6fad0f2abbac6c9603b2e8f88fe1d151a83de708aeca1c1d93d881c958ecba4R26
# both pages have a lot of inconsistencies, I've tried to fix the errors the best I could, the logic being: if it's not in the
# blog page, then the microcode update in the commit is not related to reptar, if microcode versions differ, then the one in github is correct,
# if a stepping exists in the blog page but not in the commit, then the blog page is right
# updated 2026-04 with Intel affected processor list + releasenote.md:
# added 06-9a-04/40 (AZB), 06-bd-01/80 (Lunar Lake, post-dates Reptar: first ucode already includes fix)
g_reptar_fixed_ucode_version=''
reptar_ucode_list='
06-97-02/07,00000032
06-97-05/07,00000032
06-9a-03/80,00000430
06-9a-04/80,00000430
06-9a-04/40,00000005
06-6c-01/10,01000268
06-6a-06/87,0d0003b9
06-7e-05/80,000000c2
@@ -326,6 +327,7 @@ is_cpu_affected() {
06-8d-01/c2,0000004e
06-8d-00/c2,0000004e
06-8c-02/c2,00000034
06-bd-01/80,0000011f
'
for tuple in $reptar_ucode_list; do
fixed_ucode_ver=$((0x$(echo "$tuple" | cut -d, -f2)))
@@ -339,12 +341,35 @@ is_cpu_affected() {
0x"$(echo "$affected_fms" | cut -d- -f3)"
)
if [ "$cpu_cpuid" = "$affected_cpuid" ] && [ $((cpu_platformid & ucode_platformid_mask)) -gt 0 ]; then
# this is not perfect as Intel never tells about their EOL CPUs, so more CPUs might be affected but there's no way to tell
_set_vuln reptar
g_reptar_fixed_ucode_version=$fixed_ucode_ver
break
fi
done
# if we didn't match the ucode list above, also check the model blacklist:
# Intel never tells about their EOL CPUs, so more CPUs might be affected
# than the ones that received a microcode update (e.g. steppings with
# different platform IDs that were dropped before the Reptar fix).
if [ -z "$g_reptar_fixed_ucode_version" ] && [ "$cpu_family" = 6 ]; then
set -u
if [ "$cpu_model" = "$INTEL_FAM6_ALDERLAKE" ] ||
[ "$cpu_model" = "$INTEL_FAM6_ALDERLAKE_L" ] ||
[ "$cpu_model" = "$INTEL_FAM6_ICELAKE_X" ] ||
[ "$cpu_model" = "$INTEL_FAM6_ICELAKE_D" ] ||
[ "$cpu_model" = "$INTEL_FAM6_ICELAKE_L" ] ||
[ "$cpu_model" = "$INTEL_FAM6_ROCKETLAKE" ] ||
[ "$cpu_model" = "$INTEL_FAM6_TIGERLAKE_L" ] ||
[ "$cpu_model" = "$INTEL_FAM6_TIGERLAKE" ] ||
[ "$cpu_model" = "$INTEL_FAM6_SAPPHIRERAPIDS_X" ] ||
[ "$cpu_model" = "$INTEL_FAM6_RAPTORLAKE" ] ||
[ "$cpu_model" = "$INTEL_FAM6_RAPTORLAKE_P" ] ||
[ "$cpu_model" = "$INTEL_FAM6_RAPTORLAKE_S" ] ||
[ "$cpu_model" = "$INTEL_FAM6_LUNARLAKE_M" ]; then
pr_debug "is_cpu_affected: reptar: affected (model match, no known fixing ucode)"
_set_vuln reptar
fi
set +u
fi
# Retbleed (Intel, CVE-2022-29901): Skylake through Rocket Lake, or any CPU with RSBA
# kernel cpu_vuln_blacklist for RETBLEED (6b80b59b3555, 6ad0ad2bf8a6, f54d45372c6a)
@@ -416,12 +441,76 @@ is_cpu_affected() {
fi
# BPI (Branch Privilege Injection, CVE-2024-45332)
# microcode-only fix (intel-microcode 20250512+), no kernel X86_BUG flag
# Intel affected processor list: Coffee Lake through Arrow Lake/Lunar Lake,
# plus some server parts (Cooper Lake, Sapphire/Emerald Rapids, Grand Ridge)
# immunity: no ARCH_CAP bits
# vendor scope: Intel only (family 6)
if [ "$cpu_family" = 6 ]; then
# microcode-only fix, no kernel X86_BUG flag, no CPUID/MSR indicator for the fix.
# We have to hardcode the truthtable of affected CPUs vs fixing ucodes,
# same approach as Reptar (see above).
# https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/advisory-guidance/indirect-branch-predictor-delayed-updates.html
# list taken from Intel affected processor list + Intel-Linux-Processor-Microcode-Data-Files releasenote.md
# format: FF-MM-SS/platformid_mask,fixed_ucode_version
g_bpi_fixed_ucode_version=''
bpi_ucode_list='
06-9e-0d/22,00000104
06-8e-0a/c0,000000f6
06-8e-0b/d0,000000f6
06-8e-0c/94,00000100
06-a5-02/20,00000100
06-a5-03/22,00000100
06-a5-05/22,00000100
06-a6-00/80,00000102
06-a6-01/80,00000100
06-a7-01/02,00000065
06-7e-05/80,000000cc
06-6a-06/87,0d000421
06-6c-01/10,010002f1
06-8c-01/80,000000be
06-8c-02/c2,0000003e
06-8d-01/c2,00000058
06-97-02/07,0000003e
06-97-05/07,0000003e
06-9a-03/80,0000043b
06-9a-04/80,0000043b
06-9a-04/40,0000000c
06-be-00/19,00000021
06-b7-01/32,00000133
06-ba-02/e0,00006134
06-ba-03/e0,00006134
06-bf-02/07,0000003e
06-bf-05/07,0000003e
06-aa-04/e6,00000028
06-b5-00/80,0000000d
06-c5-02/82,0000011b
06-c6-02/82,0000011b
06-bd-01/80,00000125
06-55-0b/bf,07002b01
06-8f-07/87,2b000661
06-8f-08/87,2b000661
06-8f-08/10,2c000421
06-cf-02/87,210002d3
06-7a-08/01,00000026
'
for tuple in $bpi_ucode_list; do
fixed_ucode_ver=$((0x$(echo "$tuple" | cut -d, -f2)))
affected_fmspi=$(echo "$tuple" | cut -d, -f1)
affected_fms=$(echo "$affected_fmspi" | cut -d/ -f1)
ucode_platformid_mask=0x$(echo "$affected_fmspi" | cut -d/ -f2)
affected_cpuid=$(
fms2cpuid \
0x"$(echo "$affected_fms" | cut -d- -f1)" \
0x"$(echo "$affected_fms" | cut -d- -f2)" \
0x"$(echo "$affected_fms" | cut -d- -f3)"
)
if [ "$cpu_cpuid" = "$affected_cpuid" ] && [ $((cpu_platformid & ucode_platformid_mask)) -gt 0 ]; then
_set_vuln bpi
g_bpi_fixed_ucode_version=$fixed_ucode_ver
break
fi
done
# if we didn't match the ucode list above, also check the model blacklist:
# Intel never tells about their EOL CPUs, so more CPUs might be affected
# than the ones that received a microcode update. In that case, we flag
# the CPU as affected but g_bpi_fixed_ucode_version stays empty (the CVE
# check will handle this by reporting VULN with no known fix).
if [ -z "$g_bpi_fixed_ucode_version" ] && [ "$cpu_family" = 6 ]; then
set -u
if [ "$cpu_model" = "$INTEL_FAM6_KABYLAKE_L" ] ||
[ "$cpu_model" = "$INTEL_FAM6_KABYLAKE" ] ||
@@ -449,7 +538,7 @@ is_cpu_affected() {
[ "$cpu_model" = "$INTEL_FAM6_EMERALDRAPIDS_X" ] ||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_GOLDMONT_PLUS" ] ||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_CRESTMONT" ]; then
pr_debug "is_cpu_affected: bpi: affected"
pr_debug "is_cpu_affected: bpi: affected (model match, no known fixing ucode)"
_set_vuln bpi
fi
set +u

View File

@@ -15,8 +15,13 @@ check_CVE_2023_23583_linux() {
# there is no sysfs file for this vuln, and no kernel patch,
# the mitigation is only ucode-based and there's no flag exposed,
# so most of the work has already been done by is_cpu_affected()
# shellcheck disable=SC2154
if ! is_cpu_affected "$cve"; then
pvulnstatus "$cve" OK "your CPU vendor reported your CPU model as not affected"
elif [ -z "$g_reptar_fixed_ucode_version" ]; then
# CPU matched the model blacklist but has no known fixing microcode
# (likely an EOL stepping that Intel won't release a fix for)
pvulnstatus "$cve" VULN "your CPU is affected and no microcode update is available for your CPU stepping"
else
pr_info_nol "* Reptar is mitigated by microcode: "
if [ "$cpu_ucode" -lt "$g_reptar_fixed_ucode_version" ]; then

View File

@@ -13,21 +13,34 @@ check_CVE_2024_45332_linux() {
msg=''
# There is no dedicated sysfs file for this vulnerability, and no kernel
# mitigation code. The fix is purely a microcode update (intel-microcode
# 20250512+) that corrects the asynchronous branch predictor update timing
# so that eIBRS and IBPB work as originally intended. There is no new
# CPUID bit, MSR bit, or ARCH_CAP flag to detect the fix. The only
# reliable indicator is the microcode version, which we cannot check
# without violating design principle 3 (never hardcode microcode versions).
# mitigation code. The fix is purely a microcode update that corrects the
# asynchronous branch predictor update timing so that eIBRS and IBPB work
# as originally intended. There is no new CPUID bit, MSR bit, or ARCH_CAP
# flag to detect the fix, so we hardcode known-fixing microcode versions
# per CPU (see bpi_ucode_list in is_cpu_affected).
# shellcheck disable=SC2154
if ! is_cpu_affected "$cve"; then
pvulnstatus "$cve" OK "your CPU vendor reported your CPU model as not affected"
else
pvulnstatus "$cve" UNK "the microcode fix for this vulnerability cannot be detected (no CPUID/MSR indicator); ensure you have intel-microcode 20250512 or later installed"
elif [ -z "$g_bpi_fixed_ucode_version" ]; then
# CPU matched the model blacklist but has no known fixing microcode
# (likely an EOL stepping that Intel won't release a fix for)
pvulnstatus "$cve" VULN "your CPU is affected and no microcode update is available for your CPU stepping"
explain "CVE-2024-45332 (Branch Privilege Injection) is a race condition in the branch predictor\n" \
"that undermines eIBRS and IBPB protections. The fix is a microcode update only (intel-microcode\n" \
"20250512+). No kernel changes are required. Verify your microcode version with: grep microcode\n" \
"/proc/cpuinfo. Contact your OS vendor to ensure the latest Intel microcode package is installed."
"that undermines eIBRS and IBPB protections. The fix is a microcode update, but no\n" \
"update is available for your specific CPU stepping."
else
pr_info_nol "* BPI is mitigated by microcode: "
if [ "$cpu_ucode" -lt "$g_bpi_fixed_ucode_version" ]; then
pstatus yellow NO "You have ucode $(printf "0x%x" "$cpu_ucode") and version $(printf "0x%x" "$g_bpi_fixed_ucode_version") minimum is required"
pvulnstatus "$cve" VULN "Your microcode is too old to mitigate the vulnerability"
explain "CVE-2024-45332 (Branch Privilege Injection) is a race condition in the branch predictor\n" \
"that undermines eIBRS and IBPB protections. The fix is a microcode update only.\n" \
"No kernel changes are required."
else
pstatus green YES "You have ucode $(printf "0x%x" "$cpu_ucode") which is recent enough (>= $(printf "0x%x" "$g_bpi_fixed_ucode_version"))"
pvulnstatus "$cve" OK "Your microcode mitigates the vulnerability"
fi
fi
}