mirror of
https://github.com/speed47/spectre-meltdown-checker.git
synced 2026-05-03 05:53:20 +02:00
Compare commits
18 Commits
5262efbf55
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02fa416bab | ||
|
|
1c067add59 | ||
|
|
00bb4a951c | ||
|
|
43d5b77885 | ||
|
|
78a6e4a418 | ||
|
|
5af1a9fec9 | ||
|
|
b93027640f | ||
|
|
5c27284119 | ||
|
|
f2e5999fc0 | ||
|
|
25f20b8860 | ||
|
|
77e3dbd6b2 | ||
|
|
8a6f9d5d63 | ||
|
|
f2d871acff | ||
|
|
83ebe2f75f | ||
|
|
a05f8aab34 | ||
|
|
f9c3d19f72 | ||
|
|
8389d9593c | ||
|
|
3a822fdcf2 |
36
.github/workflows/autoupdate.yml
vendored
Normal file
36
.github/workflows/autoupdate.yml
vendored
Normal 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
|
||||
79
.github/workflows/build.yml
vendored
79
.github/workflows/build.yml
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/expected_cve_count
vendored
2
.github/workflows/expected_cve_count
vendored
@@ -1 +1 @@
|
||||
32
|
||||
26
|
||||
|
||||
33
.github/workflows/stale.yml
vendored
Normal file
33
.github/workflows/stale.yml
vendored
Normal 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
190
.github/workflows/vuln-watch.yml
vendored
Normal 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
|
||||
@@ -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.
|
||||
|
||||
@@ -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,54 +1674,34 @@ 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
|
||||
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 is_intel; then
|
||||
if [ "$cpu_family" = 6 ]; then
|
||||
if [ "$cpu_model" = "$INTEL_FAM6_HASWELL_X" ] ||
|
||||
[ "$cpu_model" = "$INTEL_FAM6_BROADWELL_D" ] ||
|
||||
@@ -1744,6 +1724,8 @@ is_cpu_mmio_unknown() {
|
||||
return 1
|
||||
fi
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user