Compare commits

..

18 Commits

Author SHA1 Message Date
林博仁 Buo-ren Lin
02fa416bab doc: readme: correct markdown indentation for unordered list items (#569)
Signed-off-by: 林博仁(Buo-ren Lin) <buo.ren.lin@gmail.com>
2026-04-20 16:02:47 +00:00
Stéphane Lesimple
1c067add59 release v26.33.0420460 (#567) 2026-04-20 15:18:11 +00:00
Stéphane Lesimple
00bb4a951c workflow: expose reconsider_age_days input + env var 2026-04-19 14:46:56 +02:00
Stéphane Lesimple
43d5b77885 chore: workflow: add manual model + window_hours inputs, add reconsider 2026-04-19 12:55:03 +02:00
Stéphane Lesimple
78a6e4a418 chore: move cron vuln-watch workflow script files to their own branch 2026-04-19 11:14:21 +02:00
Stéphane Lesimple
5af1a9fec9 chore: workflow: add scan id 2026-04-18 16:23:47 +02:00
Stéphane Lesimple
b93027640f chore: vuln workflow: use opus, no persist creds, conditional upload 2026-04-18 16:19:10 +02:00
Stéphane Lesimple
5c27284119 chore: workflow: save logs 2026-04-18 16:05:15 +02:00
Stéphane Lesimple
f2e5999fc0 chore: explicit prompt for workflow 2026-04-18 15:41:03 +02:00
Stéphane Lesimple
25f20b8860 chore: fix workflow perms (#558) 2026-04-18 15:29:54 +02:00
Stéphane Lesimple
77e3dbd6b2 add scheduled vuln research (#557) 2026-04-18 15:14:13 +02:00
Stéphane Lesimple
8a6f9d5d63 Implement ITS/VMScape/BTI and misc enhancements (#539)
7a7408d fix: add rebleet to --variant
cccb3c0 enh: add known fixed ucode versions for CVE-2023-23583 (Reptar) and CVE-2024-45332 (BPI)
090f109 doc: add CVE-2023-31315 (SinkClose) to the unsupported list, add categories
5dc9c3c chore: reorder CVE list in README.md
a00fab1 feat: implement CVE-2025-40300 (VMScape) and CVE-2024-45332 (BTI)
e0b818f chore: stalebot: disable dryrun by default
4af1155 feat: implement CVE-2024-28956 (ITS, Indirect Target Selection) vulnerability and mitigation detection
dfed6f3 doc: add note about more unsupported CVEs
1652977 add a generated version of src/libs/003_intel_models.sh
a089ae8 fix: sys_interface_check() must set the caller's $msg var (closes #533)
cc6bbaa chore: don't include src/ generated files in build
2717b0a doc: CVE-2020-12965 unsupported (#478)
2026-04-04 18:38:49 +02:00
Stéphane Lesimple
f2d871acff fix: spurious local keyword broke sysfs based detection (#533) (#534)
The $msg var assigned in sys_interface_check() must not be
local to the func, but must capture and modify the callers'
own local $msg var
2026-04-03 01:31:58 +02:00
Stéphane Lesimple
83ebe2f75f chore: update workflows (#531)
* chore: add stalebot in dryrun

 built from commit afadf53f7f
 dated 2026-04-02 13:13:19 +0200
 by Stéphane Lesimple (speed47_github@speed47.net)

* Merge branch 'test' into source

 built from commit 952fe6a87f
 dated 2026-04-02 18:40:05 +0200
 by Stéphane Lesimple (speed47_github@speed47.net)

* Merge pull request #530 from speed47/test

 built from commit d3c0f1a24d
 dated 2026-04-02 16:49:41 +0000
 by Stéphane Lesimple (speed47_github@speed47.net)

 chore: workflows revamp

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-02 18:53:44 +02:00
github-actions[bot]
a05f8aab34 chore: add stalebot in dryrun
built from commit afadf53f7f
 dated 2026-04-02 13:13:19 +0200
 by Stéphane Lesimple (speed47_github@speed47.net)
2026-04-02 11:34:30 +00:00
github-actions[bot]
f9c3d19f72 enh: CVE-2017-5715; check for unprivileged eBPF for paranoid mode
built from commit e5c6d2d905
 dated 2026-04-01 20:37:54 +0000
 by Stéphane Lesimple (speed47_github@speed47.net)
2026-04-01 21:30:51 +00:00
github-actions[bot]
8389d9593c chore: prepare for dev-build renaming to test-build
built from commit 9497abbee2
 dated 2026-03-31 19:34:52 +0200
 by Stéphane Lesimple (speed47_github@speed47.net)
2026-04-01 21:30:51 +00:00
Stéphane Lesimple
3a822fdcf2 chore: master: remove obsolete workflow 2026-03-31 19:53:57 +02:00
7 changed files with 312 additions and 201 deletions

36
.github/workflows/autoupdate.yml vendored Normal file
View File

@@ -0,0 +1,36 @@
name: autoupdate
on:
workflow_dispatch:
schedule:
- cron: '42 9 * * *'
permissions:
pull-requests: write
jobs:
autoupdate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install prerequisites
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends iucode-tool sqlite3 unzip
- name: Update microcode versions
run: ./spectre-meltdown-checker.sh --update-builtin-fwdb
- name: Check git diff
id: diff
run: |
echo change="$(git diff spectre-meltdown-checker.sh | awk '/MCEDB/ { if(V) { print V" to "$4; exit } else { V=$4 } }')" >> "$GITHUB_OUTPUT"
echo nbdiff="$(git diff spectre-meltdown-checker.sh | grep -cE -- '^\+# [AI],')" >> "$GITHUB_OUTPUT"
git diff
cat "$GITHUB_OUTPUT"
- name: Create Pull Request if needed
if: steps.diff.outputs.nbdiff != '0'
uses: peter-evans/create-pull-request@v7
with:
branch: autoupdate-fwdb
commit-message: "update: fwdb from ${{ steps.diff.outputs.change }}, ${{ steps.diff.outputs.nbdiff }} microcode changes"
title: "[Auto] Update fwdb from ${{ steps.diff.outputs.change }}"
body: |
Automated PR to update fwdb from ${{ steps.diff.outputs.change }}
Detected ${{ steps.diff.outputs.nbdiff }} microcode changes

View File

@@ -25,81 +25,21 @@ jobs:
mv spectre-meltdown-checker.sh dist/
- name: check direct execution
run: |
set -x
expected=$(cat .github/workflows/expected_cve_count)
cd dist
json=$(sudo ./spectre-meltdown-checker.sh --batch json || true)
# Validate JSON is well-formed (and show it if not)
echo "$json" | jq . >/dev/null || {
echo "Invalid JSON produced by spectre-meltdown-checker.sh"
echo "$json"
exit 1
}
# Validate required keys exist
for key in meta system cpu cpu_microcode vulnerabilities; do
echo "$json" | jq -e ".$key" >/dev/null || {
echo "Missing top-level key: $key"
echo "$json" | jq .
exit 1
}
done
# Use -r to get raw scalars (no quotes)
fmtver=$(echo "$json" | jq -r '.meta.format_version // empty')
if [ "$fmtver" != "1" ]; then
echo "Unexpected format_version: $fmtver"
echo "$json" | jq .
exit 1
fi
run_as_root=$(echo "$json" | jq -r '.meta.run_as_root // empty')
if [ "$run_as_root" != "true" ]; then
echo "Expected run_as_root=true, got: $run_as_root"
echo "$json" | jq .
exit 1
fi
mocked=$(echo "$json" | jq -r '.meta.mocked // "false"')
if [ "$mocked" = "true" ]; then
echo "mocked=true must never appear in production"
echo "$json" | jq .
exit 1
fi
# Count CVEs robustly (as a number)
nb=$(echo "$json" | jq -r '[.vulnerabilities[].cve] | length')
nb=$(sudo ./spectre-meltdown-checker.sh --batch json | jq '.[]|.CVE' | wc -l)
if [ "$nb" -ne "$expected" ]; then
echo "Invalid number of CVEs reported: $nb instead of $expected"
echo "$json" | jq '.vulnerabilities[].cve'
exit 1
else
echo "OK $nb CVEs reported"
fi
# Validate json-terse backward compatibility
nb_terse=$(sudo ./spectre-meltdown-checker.sh --batch json-terse | jq -r 'map(.CVE) | length')
if [ "$nb_terse" -ne "$expected" ]; then
echo "json-terse backward compat broken: $nb_terse CVEs instead of $expected"
exit 1
else
echo "OK json-terse backward compat: $nb_terse CVEs"
fi
- name: check docker compose run execution
run: |
expected=$(cat .github/workflows/expected_cve_count)
cd dist
docker compose build
json=$(docker compose run --rm spectre-meltdown-checker --batch json || true)
echo "$json" | jq . > /dev/null
fmtver=$(echo "$json" | jq '.meta.format_version')
if [ "$fmtver" != "1" ]; then
echo "Unexpected format_version: $fmtver"
exit 1
fi
nb=$(echo "$json" | jq '.vulnerabilities[].cve' | wc -l)
nb=$(docker compose run --rm spectre-meltdown-checker --batch json | jq '.[]|.CVE' | wc -l)
if [ "$nb" -ne "$expected" ]; then
echo "Invalid number of CVEs reported: $nb instead of $expected"
exit 1
@@ -111,14 +51,7 @@ jobs:
expected=$(cat .github/workflows/expected_cve_count)
cd dist
docker build -t spectre-meltdown-checker .
json=$(docker run --rm --privileged -v /boot:/boot:ro -v /dev/cpu:/dev/cpu:ro -v /lib/modules:/lib/modules:ro spectre-meltdown-checker --batch json || true)
echo "$json" | jq . > /dev/null
fmtver=$(echo "$json" | jq '.meta.format_version')
if [ "$fmtver" != "1" ]; then
echo "Unexpected format_version: $fmtver"
exit 1
fi
nb=$(echo "$json" | jq '.vulnerabilities[].cve' | wc -l)
nb=$(docker run --rm --privileged -v /boot:/boot:ro -v /dev/cpu:/dev/cpu:ro -v /lib/modules:/lib/modules:ro spectre-meltdown-checker --batch json | jq '.[]|.CVE' | wc -l)
if [ "$nb" -ne "$expected" ]; then
echo "Invalid number of CVEs reported: $nb instead of $expected"
exit 1
@@ -159,19 +92,15 @@ jobs:
fi
- name: create a pull request to ${{ github.ref_name }}-build
run: |
# all the files in dist/* and .github/* must be moved as is to the -build branch root, move them out for now:
tmpdir=$(mktemp -d)
mv ./dist/* .github $tmpdir/
rm -rf ./dist
git fetch origin ${{ github.ref_name }}-build
git checkout -f ${{ github.ref_name }}-build
rm -rf doc/
mv $tmpdir/* .
rm -rf src/ scripts/ img/
rm -rf src/
mkdir -p .github
rsync -vaP --delete $tmpdir/.github/ .github/
git add --all
echo =#=#= DIFF CACHED
git diff --cached

View File

@@ -1 +1 @@
32
26

33
.github/workflows/stale.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: 'Manage stale issues and PRs'
on:
schedule:
- cron: '37 7 * * *'
workflow_dispatch:
inputs:
action:
description: "dry-run"
required: true
default: "dryrun"
type: choice
options:
- dryrun
- apply
permissions:
issues: write
pull-requests: write
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v10
with:
any-of-labels: 'needs-more-info,answered'
labels-to-remove-when-unstale: 'needs-more-info,answered'
days-before-stale: 30
days-before-close: 7
stale-issue-label: stale
remove-stale-when-updated: true
debug-only: ${{ case(inputs.action == 'dryrun', true, false) }}

190
.github/workflows/vuln-watch.yml vendored Normal file
View File

@@ -0,0 +1,190 @@
name: Online search for vulns
on:
schedule:
- cron: '42 8 * * *'
workflow_dispatch:
inputs:
model:
description: 'Claude model to use (cron runs default to Sonnet)'
required: false
type: choice
default: claude-sonnet-4-6
options:
- claude-sonnet-4-6
- claude-opus-4-7
- claude-haiku-4-5-20251001
window_hours:
description: 'Lookback window in hours (cron runs use 25)'
required: false
type: string
default: '25'
reconsider_age_days:
description: 'Only reconsider backlog entries last reviewed ≥ N days ago (0 = all, default 7)'
required: false
type: string
default: '7'
permissions:
contents: read
actions: read # needed to list/download previous run artifacts
id-token: write # needed by claude-code-action for OIDC auth
concurrency:
group: vuln-watch
cancel-in-progress: true
jobs:
watch:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
# The scripts driving this workflow live on the `vuln-watch` branch so
# they don't clutter master (which is what ships to production). The
# workflow file itself MUST stay on the default branch, as GitHub only
# honors `schedule:` triggers on the default branch.
- name: Checkout vuln-watch branch (scripts + prompt)
uses: actions/checkout@v5
with:
ref: vuln-watch
fetch-depth: 1
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install Python dependencies
run: python -m pip install --quiet feedparser
# ---- Load previous state ---------------------------------------------
# Find the most recent successful run of THIS workflow (other than the
# current one) and pull its `vuln-watch-state` artifact. On the very
# first run there will be none — that's fine, we start empty.
- name: Find previous successful run id
id: prev
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -e
run_id=$(gh run list \
--workflow="${{ github.workflow }}" \
--status=success \
--limit 1 \
--json databaseId \
--jq '.[0].databaseId // empty')
echo "run_id=${run_id}" >> "$GITHUB_OUTPUT"
if [ -n "$run_id" ]; then
echo "Found previous successful run: $run_id"
else
echo "No previous successful run — starting from empty state."
fi
- name: Download previous state artifact
if: steps.prev.outputs.run_id != ''
uses: actions/download-artifact@v5
continue-on-error: true # tolerate retention expiry
with:
name: vuln-watch-state
path: state/
run-id: ${{ steps.prev.outputs.run_id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
# ---- Fetch + diff (token-free; runs every time) ---------------------
# Performs conditional GETs (ETag / If-Modified-Since) against every
# source, parses RSS/Atom/HTML, dedups against state.seen + state.aliases,
# applies the time-window filter, and emits new_items.json.
# Updates state.sources (HTTP cache metadata + per-source high-water
# marks) in place so the cache survives even when Claude doesn't run.
- name: Fetch + diff all sources
id: diff
env:
SCAN_DATE: ${{ github.run_started_at }}
# Cron runs have no `inputs` context, so the fallback kicks in.
WINDOW_HOURS: ${{ inputs.window_hours || '25' }}
RECONSIDER_AGE_DAYS: ${{ inputs.reconsider_age_days || '7' }}
run: python -m scripts.vuln_watch.fetch_and_diff
# ---- Fetch checker code so Claude can grep it for coverage ---------
# The orphan vuln-watch branch has none of the actual checker code,
# so we pull the `test` branch (the dev branch where coded-but-
# unreleased CVE checks live) into ./checker/. The prompt tells
# Claude this is the canonical source of truth for "is CVE-X already
# implemented?". Only fetched on days with something to classify.
- name: Checkout checker code (test branch) for coverage grep
if: steps.diff.outputs.new_count != '0' || steps.diff.outputs.reconsider_count != '0'
uses: actions/checkout@v5
with:
ref: test
path: checker
fetch-depth: 1
persist-credentials: false
# ---- Classify new items with Claude (skipped when nothing is new) ---
# Model selection: a manual workflow_dispatch run picks from a dropdown
# (defaulting to Sonnet). Scheduled cron runs have no `inputs` context,
# so the `|| 'claude-sonnet-4-6'` fallback kicks in — cron always uses
# Sonnet to keep the daily cost floor low.
- name: Run classifier with Claude
id: classify
if: steps.diff.outputs.new_count != '0' || steps.diff.outputs.reconsider_count != '0'
uses: anthropics/claude-code-action@v1
env:
SCAN_DATE: ${{ github.run_started_at }}
with:
prompt: |
Read the full task instructions from scripts/daily_vuln_watch_prompt.md
and execute them end-to-end. Your input is new_items.json (already
deduped, windowed, and pre-filtered — do NOT re-fetch sources).
Write the three watch_${TODAY}_*.md files and classifications.json.
Use $SCAN_DATE as the canonical timestamp.
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
# model + tool allowlist pass through claude_args (v1 dropped the
# dedicated `model:` and `allowed_tools:` inputs). Job-level
# `timeout-minutes: 20` above bounds total runtime.
claude_args: |
--model ${{ inputs.model || 'claude-sonnet-4-6' }}
--allowedTools "Read,Write,Edit,Bash,Grep,Glob,WebFetch"
- name: Upload Claude execution log
if: ${{ always() && steps.classify.outputs.execution_file != '' }}
uses: actions/upload-artifact@v5
with:
name: claude-execution-log-${{ github.run_id }}
path: ${{ steps.classify.outputs.execution_file }}
retention-days: 30
if-no-files-found: warn
# ---- Merge classifications back into state --------------------------
# Also writes stub watch_*.md files if the classify step was skipped, so
# the report artifact is consistent across runs.
- name: Merge classifications into state
if: always()
env:
SCAN_DATE: ${{ github.run_started_at }}
run: python -m scripts.vuln_watch.merge_state
- name: Upload new state artifact
if: always()
uses: actions/upload-artifact@v5
with:
name: vuln-watch-state
path: state/seen.json
retention-days: 90
if-no-files-found: error
- name: Upload daily report
if: always()
uses: actions/upload-artifact@v5
with:
name: vuln-watch-report-${{ github.run_id }}
path: |
watch_*.md
current_toimplement.md
current_tocheck.md
new_items.json
classifications.json
retention-days: 90
if-no-files-found: warn

View File

@@ -307,13 +307,3 @@ A weakness in AMD's microcode signature verification (AES-CMAC hash) allows load
Exploits a synchronization failure in the AMD stack engine via an undocumented MSR bit, targeting AMD SEV-SNP confidential VMs. Requires hypervisor-level (ring 0) access.
**Why out of scope:** Not a transient/speculative execution side channel. This is an architectural attack on AMD SEV-SNP confidential computing that requires hypervisor access, which is outside the threat model of this tool.
## No CVE — Jump Conditional Code (JCC) Erratum
- **Issue:** [#329](https://github.com/speed47/spectre-meltdown-checker/issues/329)
- **Intel whitepaper:** [Mitigations for Jump Conditional Code Erratum](https://www.intel.com/content/dam/support/us/en/documents/processors/mitigations-jump-conditional-code-erratum.pdf)
- **Affected CPUs:** Intel 6th through 10th generation Core and Xeon processors (Skylake through Cascade Lake)
A microarchitectural correctness erratum where a conditional jump instruction that straddles or ends at a 64-byte instruction fetch boundary can corrupt the branch predictor state, potentially causing incorrect execution. Intel addressed this in a November 2019 microcode update. Compilers and assemblers (GCC, LLVM, binutils) also introduced alignment options (`-mbranch-alignment`, `-x86-branches-within-32B-boundaries`) to pad jump instructions away from boundary conditions, preserving performance on CPUs with updated microcode.
**Why out of scope:** The JCC erratum is a microarchitectural correctness bug, not a transient or speculative execution side-channel vulnerability. No CVE was ever assigned. Red Hat noted that privilege escalation "has not been ruled out" but made no definitive security finding, and no exploit has been demonstrated. There is no Linux sysfs entry, no CPUID bit, and no MSR flag exposing the mitigation status. The microcode fix introduces no detectable hardware indicator, so checking for it would require maintaining a per-CPU-stepping minimum microcode version table (the design principle 3 exception) — costly to maintain without a CVE anchor or confirmed exploitability to justify the ongoing work. The kernel compiler mitigation is a build-time-only change (instruction alignment) with no observable runtime state.

View File

@@ -13,7 +13,7 @@
#
# Stephane Lesimple
#
VERSION='26.33.0420863'
VERSION='26.33.0420460'
# --- Common paths and basedirs ---
readonly VULN_SYSFS_BASE="/sys/devices/system/cpu/vulnerabilities"
@@ -1674,76 +1674,58 @@ is_cpu_srbds_free() {
}
# Check whether the CPU is architecturally immune to MMIO Stale Data
# Mirrors the kernel's arch_cap_mmio_immune() helper: ALL THREE ARCH_CAP bits must be set:
# ARCH_CAP_SBDR_SSDP_NO (bit 13), ARCH_CAP_FBSDP_NO (bit 14), ARCH_CAP_PSDP_NO (bit 15)
# Returns: 0 if immune, 1 otherwise
is_arch_cap_mmio_immune() {
[ "$cap_sbdr_ssdp_no" = 1 ] && [ "$cap_fbsdp_no" = 1 ] && [ "$cap_psdp_no" = 1 ]
}
# Check whether the CPU is known to be unaffected by MMIO Stale Data (CVE-2022-21123/21125/21166)
# Matches the kernel's NO_MMIO whitelist plus arch_cap_mmio_immune().
# Model inventory and kernel-commit history are documented in check_mmio_linux().
# Returns: 0 if MMIO-free, 1 if affected or unknown
is_cpu_mmio_free() {
# source: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/kernel/cpu/common.c
#
# CPU affection logic from kernel (51802186158c, v5.19):
# Bug is set when: cpu_matches(blacklist, MMIO) AND NOT arch_cap_mmio_immune()
# arch_cap_mmio_immune() requires ALL THREE bits set:
# ARCH_CAP_FBSDP_NO (bit 14) AND ARCH_CAP_PSDP_NO (bit 15) AND ARCH_CAP_SBDR_SSDP_NO (bit 13)
#
# Intel Family 6 model blacklist (unchanged since v5.19):
# HASWELL_X (0x3F)
# BROADWELL_D (0x56), BROADWELL_X (0x4F)
# SKYLAKE_X (0x55), SKYLAKE_L (0x4E), SKYLAKE (0x5E)
# KABYLAKE_L (0x8E), KABYLAKE (0x9E)
# ICELAKE_L (0x7E), ICELAKE_D (0x6C), ICELAKE_X (0x6A)
# COMETLAKE (0xA5), COMETLAKE_L (0xA6)
# LAKEFIELD (0x8A)
# ROCKETLAKE (0xA7)
# ATOM_TREMONT (0x96), ATOM_TREMONT_D (0x86), ATOM_TREMONT_L (0x9C)
#
# Vendor scope: Intel only. Non-Intel CPUs are not affected.
parse_cpu_details
is_arch_cap_mmio_immune && return 0
# Non-Intel x86 vendors the kernel unconditionally whitelists (AMD/Hygon all
# families; Centaur/Zhaoxin fam 7 only).
if is_amd || is_hygon; then
# ARCH_CAP immunity: all three bits must be set
if [ "$cap_sbdr_ssdp_no" = 1 ] && [ "$cap_fbsdp_no" = 1 ] && [ "$cap_psdp_no" = 1 ]; then
return 0
fi
if { [ "$cpu_vendor" = "CentaurHauls" ] || [ "$cpu_vendor" = "Shanghai" ]; } && [ "$cpu_family" = 7 ]; then
return 0
fi
# Intel NO_MMIO whitelist
if is_intel && [ "$cpu_family" = 6 ]; then
if [ "$cpu_model" = "$INTEL_FAM6_TIGERLAKE" ] ||
[ "$cpu_model" = "$INTEL_FAM6_TIGERLAKE_L" ] ||
[ "$cpu_model" = "$INTEL_FAM6_ALDERLAKE" ] ||
[ "$cpu_model" = "$INTEL_FAM6_ALDERLAKE_L" ] ||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_GOLDMONT" ] ||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_GOLDMONT_D" ] ||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_GOLDMONT_PLUS" ]; then
return 0
if is_intel; then
if [ "$cpu_family" = 6 ]; then
if [ "$cpu_model" = "$INTEL_FAM6_HASWELL_X" ] ||
[ "$cpu_model" = "$INTEL_FAM6_BROADWELL_D" ] ||
[ "$cpu_model" = "$INTEL_FAM6_BROADWELL_X" ] ||
[ "$cpu_model" = "$INTEL_FAM6_SKYLAKE_X" ] ||
[ "$cpu_model" = "$INTEL_FAM6_SKYLAKE_L" ] ||
[ "$cpu_model" = "$INTEL_FAM6_SKYLAKE" ] ||
[ "$cpu_model" = "$INTEL_FAM6_KABYLAKE_L" ] ||
[ "$cpu_model" = "$INTEL_FAM6_KABYLAKE" ] ||
[ "$cpu_model" = "$INTEL_FAM6_ICELAKE_L" ] ||
[ "$cpu_model" = "$INTEL_FAM6_ICELAKE_D" ] ||
[ "$cpu_model" = "$INTEL_FAM6_ICELAKE_X" ] ||
[ "$cpu_model" = "$INTEL_FAM6_COMETLAKE" ] ||
[ "$cpu_model" = "$INTEL_FAM6_COMETLAKE_L" ] ||
[ "$cpu_model" = "$INTEL_FAM6_LAKEFIELD" ] ||
[ "$cpu_model" = "$INTEL_FAM6_ROCKETLAKE" ] ||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_TREMONT" ] ||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_TREMONT_D" ] ||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_TREMONT_L" ]; then
return 1
fi
fi
fi
return 1
}
# Check whether the CPU's MMIO Stale Data status is unknown ("out of servicing period")
# Matches the kernel's X86_BUG_MMIO_UNKNOWN: Intel CPU not MMIO-free and not in the
# MMIO blacklist. The kernel reports "Unknown: No mitigations" for such CPUs.
# Callers: check_mmio_linux, check_mmio_bsd
# Returns: 0 if unknown, 1 if known (either affected or not affected)
is_cpu_mmio_unknown() {
parse_cpu_details
# Only Intel can reach the unknown bucket — other x86 vendors are whitelisted by vendor-id.
is_intel || return 1
is_cpu_mmio_free && return 1
if [ "$cpu_family" = 6 ]; then
if [ "$cpu_model" = "$INTEL_FAM6_HASWELL_X" ] ||
[ "$cpu_model" = "$INTEL_FAM6_BROADWELL_D" ] ||
[ "$cpu_model" = "$INTEL_FAM6_BROADWELL_X" ] ||
[ "$cpu_model" = "$INTEL_FAM6_SKYLAKE_X" ] ||
[ "$cpu_model" = "$INTEL_FAM6_SKYLAKE_L" ] ||
[ "$cpu_model" = "$INTEL_FAM6_SKYLAKE" ] ||
[ "$cpu_model" = "$INTEL_FAM6_KABYLAKE_L" ] ||
[ "$cpu_model" = "$INTEL_FAM6_KABYLAKE" ] ||
[ "$cpu_model" = "$INTEL_FAM6_ICELAKE_L" ] ||
[ "$cpu_model" = "$INTEL_FAM6_ICELAKE_D" ] ||
[ "$cpu_model" = "$INTEL_FAM6_ICELAKE_X" ] ||
[ "$cpu_model" = "$INTEL_FAM6_COMETLAKE" ] ||
[ "$cpu_model" = "$INTEL_FAM6_COMETLAKE_L" ] ||
[ "$cpu_model" = "$INTEL_FAM6_LAKEFIELD" ] ||
[ "$cpu_model" = "$INTEL_FAM6_ROCKETLAKE" ] ||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_TREMONT" ] ||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_TREMONT_D" ] ||
[ "$cpu_model" = "$INTEL_FAM6_ATOM_TREMONT_L" ]; then
return 1
fi
fi
return 0
}
@@ -5558,7 +5540,7 @@ check_cpu() {
pr_info_nol " * CPU explicitly indicates not being affected by MMIO Stale Data (FBSDP_NO & PSDP_NO & SBDR_SSDP_NO): "
if [ "$cap_sbdr_ssdp_no" = -1 ]; then
pstatus yellow UNKNOWN "couldn't read MSR"
elif is_arch_cap_mmio_immune; then
elif [ "$cap_sbdr_ssdp_no" = 1 ] && [ "$cap_fbsdp_no" = 1 ] && [ "$cap_psdp_no" = 1 ]; then
pstatus green YES
else
pstatus yellow NO
@@ -6248,30 +6230,16 @@ check_mds_linux() {
# vim: set ts=4 sw=4 sts=4 et:
# MMIO Stale Data (Processor MMIO Stale Data Vulnerabilities) - BSD mitigation check
check_mmio_bsd() {
# No BSD (FreeBSD, OpenBSD, NetBSD, DragonFlyBSD) has implemented an OS-level
# MMIO Stale Data mitigation. All four stopped at MDS/TAA. Microcode update is
# the only partial defense available, and without OS-level VERW invocation it
# cannot close the vulnerability.
local unk
unk="your CPU's MMIO Stale Data status is unknown (Intel never officially assessed this CPU, its servicing period has ended)"
if ! is_cpu_affected "$cve"; then
pvulnstatus "$cve" OK "your CPU vendor reported your CPU model as not affected"
elif is_cpu_mmio_unknown; then
if [ "$opt_paranoid" = 1 ]; then
pvulnstatus "$cve" VULN "$unk, and no BSD mitigation exists"
explain "There is no known mitigation for this CPU model. Even with up-to-date microcode, BSD kernels do not invoke VERW for MMIO Stale Data clearing. Only a hardware replacement can fully address this."
else
pvulnstatus "$cve" UNK "$unk; no BSD mitigation exists in any case"
fi
else
pvulnstatus "$cve" VULN "your CPU is affected and no BSD has implemented an MMIO Stale Data mitigation"
explain "No BSD kernel currently implements an MMIO Stale Data mitigation (which would require invoking VERW at context switches and VM-entries). Updating CPU microcode alone does not mitigate this vulnerability without OS cooperation."
pvulnstatus "$cve" UNK "your CPU is affected, but mitigation detection has not yet been implemented for BSD in this script"
fi
}
# MMIO Stale Data (Processor MMIO Stale Data Vulnerabilities) - Linux mitigation check
check_mmio_linux() {
local status sys_interface_available msg kernel_mmio kernel_mmio_can_tell mmio_mitigated mmio_smt_mitigated mystatus mymsg unk
local status sys_interface_available msg kernel_mmio kernel_mmio_can_tell mmio_mitigated mmio_smt_mitigated mystatus mymsg
status=UNK
sys_interface_available=0
msg=''
@@ -6373,33 +6341,9 @@ check_mmio_linux() {
#
# No models have been added to or removed from the MMIO blacklist since v5.19.
#
# 7df548840c49 (v6.0, NO_MMIO whitelist added, Pawan Gupta 2022-08-03):
# Intel Family 6:
# TIGERLAKE (0x8D), TIGERLAKE_L (0x8C)
# ALDERLAKE (0x97), ALDERLAKE_L (0x9A)
# ATOM_GOLDMONT (0x5C), ATOM_GOLDMONT_D (0x5F), ATOM_GOLDMONT_PLUS (0x7A)
# AMD: fam 0x0f-0x12 + X86_FAMILY_ANY (all families)
# Hygon: all families
# Centaur fam 7, Zhaoxin fam 7
#
# Kernel logic (v6.0+):
# if (!arch_cap_mmio_immune(ia32_cap)) {
# if (cpu_matches(cpu_vuln_blacklist, MMIO))
# setup_force_cpu_bug(X86_BUG_MMIO_STALE_DATA);
# else if (!cpu_matches(cpu_vuln_whitelist, NO_MMIO))
# setup_force_cpu_bug(X86_BUG_MMIO_UNKNOWN);
# }
# => Intel CPUs that are neither blacklisted nor whitelisted (e.g. Ivy Bridge,
# Haswell client, Broadwell client, Sandy Bridge, pre-Goldmont Atom, etc.) get
# X86_BUG_MMIO_UNKNOWN and report "Unknown: No mitigations" in sysfs. Intel
# never published an affected-processor evaluation for these models because
# their servicing period had already ended.
# => is_cpu_mmio_unknown() matches this set so the script can report UNK (or
# VULN under --paranoid) rather than the misleading "not affected" that
# a plain blacklist check would produce.
#
# immunity: ARCH_CAP_SBDR_SSDP_NO (bit 13) AND ARCH_CAP_FBSDP_NO (bit 14) AND ARCH_CAP_PSDP_NO (bit 15)
# All three must be set. Checked via arch_cap_mmio_immune() in common.c.
# Bug is set only when: cpu_matches(blacklist, MMIO) AND NOT arch_cap_mmio_immune().
#
# microcode mitigation: ARCH_CAP_FB_CLEAR (bit 17) -- VERW clears fill buffers.
# Alternative: MD_CLEAR CPUID + FLUSH_L1D CPUID when MDS_NO is not set (legacy path).
@@ -6478,17 +6422,6 @@ check_mmio_linux() {
if ! is_cpu_affected "$cve"; then
# override status & msg in case CPU is not vulnerable after all
pvulnstatus "$cve" OK "your CPU vendor reported your CPU model as not affected"
elif [ "$opt_sysfs_only" != 1 ] && is_cpu_mmio_unknown; then
# Bypass the normal sysfs reconciliation: sysfs reports "Unknown: No mitigations"
# only on v6.0-v6.15. On earlier and on v6.16+ kernels it wrongly says "Not affected"
# for these CPUs (which predate FB_CLEAR microcode and Intel's affected-processor list).
unk="your CPU's MMIO Stale Data status is unknown (Intel never officially assessed this CPU, its servicing period has ended)"
if [ "$opt_paranoid" = 1 ]; then
pvulnstatus "$cve" VULN "$unk, and no mitigation is available"
explain "There is no known mitigation for this CPU model. Intel ended its servicing period without evaluating whether it is affected by MMIO Stale Data vulnerabilities, so no FB_CLEAR-capable microcode was released. Consider replacing affected hardware."
else
pvulnstatus "$cve" UNK "$unk; no mitigation is available in any case"
fi
else
if [ "$opt_sysfs_only" != 1 ]; then
# compute mystatus and mymsg from our own logic