enh: add FPDSS check for AMD Zen1/Zen+ (CVE-2025-54505)

This commit is contained in:
Stéphane Lesimple
2026-04-18 10:56:21 +00:00
parent 48454a5344
commit 048ce5b6a2
9 changed files with 520 additions and 8 deletions

View File

@@ -0,0 +1,199 @@
# Daily transient-execution vulnerability scan
You are a scheduled agent running inside a GitHub Actions job. Your job
is to audit public news/advisory sources for **transient-execution and
CPU side-channel vulnerabilities** that may need to be added to
**spectre-meltdown-checker** (this repository).
## What counts as "relevant"
spectre-meltdown-checker detects, reports, and suggests mitigations for
CPU vulnerabilities such as: Spectre v1/v2/v4, Meltdown, Foreshadow/L1TF,
MDS (ZombieLoad/RIDL/Fallout), TAA, SRBDS, iTLB Multihit, Zenbleed,
Downfall (GDS), Retbleed, Inception, SRSO, BHI, RFDS, Reptar, FP-DSS,
and any similar microarchitectural side-channel or speculative-execution
issue on x86 (Intel/AMD) or ARM CPUs. It also surfaces related hardware
mitigation features (SMAP/SMEP/UMIP/IBPB/eIBRS/STIBP…) when they gate
the remediation for a tracked CVE.
It does **not** track generic software CVEs, GPU driver bugs, networking
stacks, filesystem bugs, userspace crypto issues, or unrelated kernel
subsystems.
## Inputs handed to you by the workflow
- Working directory: the repo root (`/github/workspace` in Actions, or
wherever `actions/checkout` placed it). You may `grep` the repo to
check whether a CVE or codename is already covered.
- `state/seen.json` — memory carried over from the previous run, with
shape:
```json
{
"last_run": "2026-04-17T08:00:12Z",
"seen": {
"<stable-id-1>": { "bucket": "unrelated", "seen_at": "2026-04-17T08:00:12Z", "source": "phoronix" },
"<stable-id-2>": { "bucket": "tocheck", "seen_at": "2026-04-17T08:00:12Z", "source": "oss-sec", "cve": "CVE-2026-1234" }
}
}
```
On the very first run, or when the prior artifact has expired,
the file exists but `seen` is empty and `last_run` is `null`.
- Environment: `SCAN_DATE` (ISO-8601 timestamp of the run start, set by
the workflow). Treat this as "now" for all time-window decisions.
## Time window
This is a belt-and-suspenders design — use **both** mechanisms:
1. **Primary: stable-id dedup.** If an item's stable identifier (see
below) is already present in `state.seen`, skip it entirely — it
was classified on a previous day.
2. **Secondary: 25-hour window.** Among *new* items, prefer those whose
publication/update timestamp is within the last 25 h relative to
`SCAN_DATE`. This bounds work when the prior artifact expired
(90-day retention) or when `last_run` is stale (missed runs).
If `last_run` is older than 25 h, widen the window to
`now - last_run + 1h` so no items are lost across missed runs.
3. Items without a parseable timestamp: include them (fail-safe).
## Sources to poll
Fetch each URL with
`curl -sS -A "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36" -L --max-time 20`.
On non-2xx or timeout, record the failure in the run summary and
continue — do not abort.
### RSS / Atom feeds (primary — parse feed timestamps)
| Short name | URL |
|-----------------|-----|
| phoronix | https://www.phoronix.com/rss.php |
| oss-sec | https://seclists.org/rss/oss-sec.rss |
| lwn | https://lwn.net/headlines/newrss |
| project-zero | https://googleprojectzero.blogspot.com/feeds/posts/default |
| vusec | https://www.vusec.net/feed/ |
| comsec-eth | https://comsec.ethz.ch/category/news/feed/ |
| msrc | https://msrc.microsoft.com/update-guide/rss |
| cisa | https://www.cisa.gov/cybersecurity-advisories/all.xml |
| cert-cc | https://www.kb.cert.org/vuls/atomfeed/ |
### HTML pages (no RSS — fetch, extract dated entries)
| Short name | URL |
|-----------------|-----|
| intel-psirt | https://www.intel.com/content/www/us/en/security-center/default.html |
| amd-psirt | https://www.amd.com/en/resources/product-security.html |
| arm-spec | https://developer.arm.com/Arm%20Security%20Center/Speculative%20Processor%20Vulnerability |
| transient-fail | https://transient.fail/ |
For HTML pages: look for advisory tables or listings with dates. Extract
the advisory title, permalink, and date. If a page has no dates at all,
compare its content against `state.seen` — any new advisory IDs not yet
classified count as "new this run".
## Stable identifier per source
Use the first available of these, in order, as the dedup key:
1. Vendor advisory ID (`INTEL-SA-01234`, `AMD-SB-7001`, `ARM-2024-0042`,
`VU#123456`, `CVE-YYYY-NNNNN`)
2. RSS `<guid>` / Atom `<id>`
3. Permalink URL (`<link>`)
Always also record the permalink URL in the output file so a human can
click through.
## Classification rules
For each **new** item (not in `state.seen`) that passes the time window,
pick exactly one bucket:
- **toimplement** — a clearly-identified new transient-execution / CPU
side-channel vulnerability in scope, **and not already covered by
this repo**. Verify the second half by grepping the repo for the CVE
ID *and* the codename before classifying; if either matches existing
code, demote to `tocheck`.
- **tocheck** — plausibly in-scope but ambiguous: mitigation-only
feature (LASS, IBT, APIC-virt, etc.); item seemingly already
implemented but worth confirming scope; unclear applicability
(e.g. embedded-only ARM SKU); CVE-ID pending; contradictory info
across sources. State clearly what would resolve the ambiguity.
- **unrelated** — everything else.
Tie-breakers: prefer `tocheck` over `unrelated` when uncertain. Prefer
`tocheck` over `toimplement` when the CVE ID is still "reserved" /
"pending" — false positives in `toimplement` waste human time more than
false positives in `tocheck`.
## Outputs
Compute `TODAY=$(date -u -d "$SCAN_DATE" +%F)`. Write these files under
the repo root, overwriting if they already exist (they shouldn't unless
the workflow re-ran the same day):
- `rss_${TODAY}_toimplement.md`
- `rss_${TODAY}_tocheck.md`
- `rss_${TODAY}_unrelated.md`
Each file uses level-2 headers per source short-name, then one bullet
per item: the stable ID (if any), the permalink URL, and 12 sentences.
Keep entries terse — a human skims these daily.
```markdown
## oss-sec
- **CVE-2026-1234** — https://www.openwall.com/lists/oss-security/2026/04/18/3
New Intel transient-execution bug "Foo" disclosed today; affects
Redwood Cove cores, microcode fix pending. Not yet covered by this
repo (grepped for CVE-2026-1234 and "Foo" — no matches).
## phoronix
- https://www.phoronix.com/news/Some-Article
Linux 7.2 drops a compiler-target flag; unrelated to CPU side channels.
```
If a bucket has no items, write the file with a single line
`(no new items in this window)` so it is obvious the job ran.
### Run summary
Append this block to the **tocheck** file (creating it if empty):
```markdown
## Run summary
- SCAN_DATE: <value>
- window cutoff: <computed cutoff>
- prior state size: <N> entries, last_run=<value>
- per-source new item counts: phoronix=<n>, oss-sec=<n>, lwn=<n>, ...
- fetch failures: <list, or "none">
- total classified this run: toimplement=<n>, tocheck=<n>, unrelated=<n>
```
### State update
Rewrite `state/seen.json` with:
- `last_run` = `SCAN_DATE`
- `seen` = union of (pruned prior `seen`) (all items classified this
run, keyed by stable ID, with `{bucket, seen_at=SCAN_DATE, source, cve?}`)
Pruning (keep state bounded): drop any entry whose `seen_at` is older
than 30 days before `SCAN_DATE`. The workflow step also does this as
a safety net, but do it here too so the in-memory view is consistent.
## Guardrails
- Do NOT modify any repo source code. Only write the three markdown
output files and `state/seen.json`.
- Do NOT create commits, branches, or PRs.
- Do NOT call any tool that posts externally (Slack, GitHub comments,
issues, email, etc.).
- Do NOT follow links off-site for deeper investigation unless strictly
needed to resolve a `tocheck` ambiguity — budget of at most 5 such
follow-ups per run.
- If a source returns unexpectedly large content, truncate to the first
~200 items before parsing.
- If total runtime exceeds 15 minutes, finish whatever you can,
write partial outputs, and note it in the run summary.

View File

@@ -1 +1 @@
31
32

129
.github/workflows/vuln-scan.yml vendored Normal file
View File

@@ -0,0 +1,129 @@
name: Online search for vulns
on:
schedule:
- cron: '42 8 * * *'
workflow_dispatch: {} # allow manual trigger
permissions:
contents: read
actions: read # needed to list/download previous run artifacts
id-token: write # needed to mint OIDC token
concurrency:
group: vuln-scan
cancel-in-progress: true
jobs:
scan:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout repository (for grep-based dedup against existing checks)
uses: actions/checkout@v5
with:
fetch-depth: 1
persist-credentials: false
# ---- Load previous state ---------------------------------------------
# Find the most recent successful run of THIS workflow (other than the
# current one) and pull its `vuln-scan-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@v4
continue-on-error: true # tolerate retention expiry
with:
name: vuln-scan-state
path: state/
run-id: ${{ steps.prev.outputs.run_id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Ensure state file exists
run: |
mkdir -p state
if [ ! -f state/seen.json ]; then
echo '{"last_run": null, "seen": {}}' > state/seen.json
echo "Initialized empty state."
fi
echo "State size: $(wc -c < state/seen.json) bytes"
# ---- Run the scan ----------------------------------------------------
# Runs Claude Code against daily_vuln_scan_prompt.md.
# That prompt file fully specifies: sources to poll, how to read
# state/seen.json, the 25-hour window, the output files to write,
# and how to rewrite state/seen.json at the end of the run.
- name: Research for online mentions of new vulns
id: scan
uses: anthropics/claude-code-action@v1
env:
SCAN_DATE: ${{ github.run_started_at }}
with:
claude_args: |
--model claude-opus-4-7 --allowedTools "Read,Write,Edit,Bash,Grep,Glob,WebFetch"
prompt: |
Read the full task instructions from .github/workflows/daily_vuln_scan_prompt.md and execute them end-to-end. That file fully specifies: sources to poll, how to read and update state/seen.json, the 25-hour window, which rss_YYYY-MM-DD_*.md files to write, and the run guardrails. Use $SCAN_DATE (env var) as "now" for time-window decisions.
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
- name: Upload Claude execution log
if: ${{ always() && steps.scan.outputs.execution_file != '' }}
uses: actions/upload-artifact@v4
with:
name: claude-execution-log-${{ github.run_id }}
path: ${{ steps.scan.outputs.execution_file }}
retention-days: 30
if-no-files-found: warn
# ---- Persist outputs -------------------------------------------------
- name: Prune state (keep only entries from the last 30 days)
run: |
python3 - <<'PY'
import json, datetime, pathlib
p = pathlib.Path("state/seen.json")
data = json.loads(p.read_text())
cutoff = (datetime.datetime.utcnow() - datetime.timedelta(days=30)).isoformat()
before = len(data.get("seen", {}))
data["seen"] = {
k: v for k, v in data.get("seen", {}).items()
if v.get("seen_at", "9999") >= cutoff
}
after = len(data["seen"])
p.write_text(json.dumps(data, indent=2, sort_keys=True))
print(f"Pruned state: {before} -> {after} entries")
PY
- name: Upload new state artifact
uses: actions/upload-artifact@v4
with:
name: vuln-scan-state
path: state/seen.json
retention-days: 90
if-no-files-found: error
- name: Upload daily report
uses: actions/upload-artifact@v4
with:
name: vuln-scan-report-${{ github.run_id }}
path: rss_*.md
retention-days: 90
if-no-files-found: warn

View File

@@ -291,7 +291,12 @@ Before writing code, verify the CVE meets the inclusion criteria (see "CVE Inclu
### Step 1: Create the Vulnerability File
Create `src/vulns/CVE-YYYY-NNNNN.sh`. The file header must follow this exact format:
Create `src/vulns/CVE-YYYY-NNNNN.sh`. When no real CVE applies, two placeholder ranges are reserved:
- **`CVE-0000-NNNN`** — permanent placeholder for supplementary `--extra`-only checks that will never receive a real CVE (e.g. SLS / compile-time hardening).
- **`CVE-9999-NNNN`** — temporary placeholder for real vulnerabilities awaiting CVE assignment. Once the real CVE is issued, rename the file, the registry entry, the `--variant` alias, and the function symbols across the codebase.
The file header must follow this exact format:
- **Line 1**: vim modeline (`# vim: set ts=4 sw=4 sts=4 et:`)
- **Line 2**: 31 `#` characters (`###############################`)

6
dist/README.md vendored
View File

@@ -38,6 +38,7 @@ CVE | Name | Aliases
[CVE-2024-36357](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-36357) | Transient Scheduler Attack, L1 | TSA-L1
[CVE-2025-40300](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2025-40300) | VM-Exit Stale Branch Prediction | VMScape
[CVE-2024-45332](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-45332) | Branch Privilege Injection | BPI
[CVE-2025-54505](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2025-54505) | AMD Zen1 Floating-Point Divider Stale Data Leak | FPDSS
## Am I at risk?
@@ -77,6 +78,7 @@ CVE-2024-36350 (TSA-SQ) | 💥 | 💥 (1) | 💥 | 💥 (1) | Microcode + kernel
CVE-2024-36357 (TSA-L1) | 💥 | 💥 (1) | 💥 | 💥 (1) | Microcode + kernel update
CVE-2025-40300 (VMScape) | ✅ | ✅ | 💥 | ✅ | Kernel update (IBPB on VM-exit)
CVE-2024-45332 (BPI) | 💥 | ✅ | 💥 | ✅ | Microcode update
CVE-2025-54505 (FPDSS) | 💥 | 💥 | 💥 | 💥 | Kernel update
> 💥 Data can be leaked across this boundary.
@@ -207,6 +209,10 @@ After a guest VM exits to the host, stale branch predictions from the guest can
A race condition in the branch predictor update mechanism of Intel processors (Coffee Lake through Raptor Lake, plus some server and Atom parts) allows user-space branch predictions to briefly influence kernel-space speculative execution, undermining eIBRS and IBPB protections. This means systems relying solely on eIBRS for Spectre V2 mitigation may not be fully protected without the microcode fix. Mitigation requires a microcode update (intel-microcode 20250512+) that fixes the asynchronous branch predictor update timing so that eIBRS and IBPB work as originally intended. No kernel changes are required. Performance impact is negligible.
**CVE-2025-54505 — AMD Zen1 Floating-Point Divider Stale Data Leak (FPDSS)**
On AMD Zen1 and Zen+ processors (EPYC 7001, EPYC Embedded 3000, Athlon 3000 with Radeon, Ryzen 3000 with Radeon, Ryzen PRO 3000 with Radeon Vega), the hardware floating-point divider can retain partial quotient data from previous operations. Under certain circumstances, those results can be leaked to another thread sharing the same divider, crossing any privilege boundary. This was assigned CVE-2025-54505 and published by AMD as AMD-SB-7053 on 2026-04-17. Mitigation requires a kernel update (mainline commit e55d98e77561, "x86/CPU: Fix FPDSS on Zen1", Linux 7.1) that sets bit 9 (ZEN1_DENORM_FIX_BIT) of MSR 0xc0011028 (MSR_AMD64_FP_CFG) unconditionally on every Zen1 CPU at boot, disabling the hardware optimization responsible for the leak. No microcode update is required: the chicken bit is present in Zen1 silicon from the factory and is independent of microcode revision. Performance impact is limited to a small reduction in floating-point divide throughput, which is why AMD does not enable the bit by default in hardware.
</details>
## Unsupported CVEs

View File

@@ -153,6 +153,12 @@ g_smc_cpu_info_line=''
# CVE Registry: single source of truth for all CVE metadata.
# Fields: cve_id|json_key_name|affected_var_suffix|complete_name_and_aliases
#
# Two ranges of placeholder IDs are reserved when no real CVE applies:
# CVE-0000-NNNN: permanent placeholder for supplementary checks (--extra only)
# that will never receive a real CVE (e.g. SLS, compile-time hardening).
# CVE-9999-NNNN: temporary placeholder for real vulnerabilities awaiting CVE
# assignment. Rename across the codebase once the real CVE is issued.
readonly CVE_REGISTRY='
CVE-2017-5753|SPECTRE VARIANT 1|variant1|Spectre Variant 1, bounds check bypass
CVE-2017-5715|SPECTRE VARIANT 2|variant2|Spectre Variant 2, branch target injection
@@ -186,6 +192,7 @@ CVE-2025-40300|VMSCAPE|vmscape|VMScape, VM-exit stale branch prediction
CVE-2023-28746|RFDS|rfds|Register File Data Sampling (RFDS)
CVE-2024-45332|BPI|bpi|Branch Privilege Injection (BPI)
CVE-0000-0001|SLS|sls|Straight-Line Speculation (SLS)
CVE-2025-54505|FPDSS|fpdss|FPDSS, AMD Zen1 Floating-Point Divider Stale Data Leak
'
# Derive the supported CVE list from the registry

View File

@@ -106,8 +106,9 @@ is_cpu_affected() {
affected_srbds=''
affected_mmio=''
affected_sls=''
# DIV0, Zenbleed and Inception are all AMD specific, look for "is_amd" below:
# DIV0, FPDSS, Zenbleed and Inception are all AMD specific, look for "is_amd" below:
_set_immune div0
_set_immune fpdss
_set_immune zenbleed
_set_immune inception
# TSA is AMD specific (Zen 3/4), look for "is_amd" below:
@@ -605,13 +606,23 @@ is_cpu_affected() {
fi
_set_immune variantl1tf
# DIV0 (Zen1 only)
# DIV0 (Zen1/Zen+)
# 77245f1c3c64 (v6.5, initial model list): family 0x17 models 0x00-0x2f, 0x50-0x5f
# bfff3c6692ce (v6.8): moved to init_amd_zen1(), unconditional for all Zen1
# All Zen1 CPUs are family 0x17, models 0x00-0x2f and 0x50-0x5f
# bfff3c6692ce (v6.8): moved to init_amd_zen1(), unconditional for all ZEN1-flagged CPUs
# The kernel's X86_FEATURE_ZEN1 covers family 0x17 models 0x00-0x2f and 0x50-0x5f,
# which spans both Zen1 (Summit Ridge, Naples, Raven Ridge, Snowy Owl) and Zen+
# (Pinnacle Ridge, Picasso, Dali, Colfax) products -- all using the same divider silicon.
amd_legacy_erratum "$(amd_model_range 0x17 0x00 0x0 0x2f 0xf)" && _set_vuln div0
amd_legacy_erratum "$(amd_model_range 0x17 0x50 0x0 0x5f 0xf)" && _set_vuln div0
# FPDSS: same Zen1/Zen+ cohort as DIV0 (both applied unconditionally in init_amd_zen1()).
# e55d98e77561 (v7.1): unconditional in init_amd_zen1(); CVE-2025-54505 / AMD-SB-7053.
# AMD-SB-7053 only enumerates a subset (EPYC 7001, EPYC Embedded 3000, Athlon/Ryzen 3000
# with Radeon, Ryzen PRO 3000 with Radeon Vega), but the kernel mitigates the full
# ZEN1 cohort, so we flag all of it to match the kernel's behavior.
# shellcheck disable=SC2154
[ "$affected_div0" = 0 ] && _set_vuln fpdss
# Zenbleed
amd_legacy_erratum "$(amd_model_range 0x17 0x30 0x0 0x4f 0xf)" && _set_vuln zenbleed
amd_legacy_erratum "$(amd_model_range 0x17 0x60 0x0 0x7f 0xf)" && _set_vuln zenbleed
@@ -821,7 +832,7 @@ is_cpu_affected() {
pr_debug "is_cpu_affected: final results: variant1=$affected_variant1 variant2=$affected_variant2 variant3=$affected_variant3 variant3a=$affected_variant3a"
pr_debug "is_cpu_affected: final results: variant4=$affected_variant4 variantl1tf=$affected_variantl1tf msbds=$affected_msbds mfbds=$affected_mfbds"
pr_debug "is_cpu_affected: final results: mlpds=$affected_mlpds mdsum=$affected_mdsum taa=$affected_taa itlbmh=$affected_itlbmh srbds=$affected_srbds"
pr_debug "is_cpu_affected: final results: div0=$affected_div0 zenbleed=$affected_zenbleed inception=$affected_inception retbleed=$affected_retbleed tsa=$affected_tsa downfall=$affected_downfall reptar=$affected_reptar rfds=$affected_rfds its=$affected_its"
pr_debug "is_cpu_affected: final results: div0=$affected_div0 fpdss=$affected_fpdss zenbleed=$affected_zenbleed inception=$affected_inception retbleed=$affected_retbleed tsa=$affected_tsa downfall=$affected_downfall reptar=$affected_reptar rfds=$affected_rfds its=$affected_its"
pr_debug "is_cpu_affected: final results: vmscape=$affected_vmscape bpi=$affected_bpi sls=$affected_sls mmio=$affected_mmio"
}
affected_variantl1tf_sgx="$affected_variantl1tf"

View File

@@ -170,7 +170,7 @@ while [ -n "${1:-}" ]; do
case "$2" in
help)
echo "The following parameters are supported for --variant (can be used multiple times):"
echo "1, 2, 3, 3a, 4, msbds, mfbds, mlpds, mdsum, l1tf, taa, mcepsc, srbds, mmio, sbdr, sbds, drpw, div0, zenbleed, downfall, retbleed, inception, reptar, rfds, tsa, tsa-sq, tsa-l1, its, vmscape, bpi, sls"
echo "1, 2, 3, 3a, 4, msbds, mfbds, mlpds, mdsum, l1tf, taa, mcepsc, srbds, mmio, sbdr, sbds, drpw, div0, fpdss, zenbleed, downfall, retbleed, inception, reptar, rfds, tsa, tsa-sq, tsa-l1, its, vmscape, bpi, sls"
exit 0
;;
1)
@@ -245,6 +245,10 @@ while [ -n "${1:-}" ]; do
opt_cve_list="$opt_cve_list CVE-2023-20588"
opt_cve_all=0
;;
fpdss)
opt_cve_list="$opt_cve_list CVE-2025-54505"
opt_cve_all=0
;;
zenbleed)
opt_cve_list="$opt_cve_list CVE-2023-20593"
opt_cve_all=0

151
src/vulns/CVE-2025-54505.sh Normal file
View File

@@ -0,0 +1,151 @@
# vim: set ts=4 sw=4 sts=4 et:
###############################
# CVE-2025-54505, FPDSS, AMD Zen1 Floating-Point Divider Stale Data Leak
check_CVE_2025_54505() {
check_cve 'CVE-2025-54505'
}
# Print remediation advice for FPDSS when reporting VULN
# Callers: check_CVE_2025_54505_linux
_cve_2025_54505_explain_fix() {
explain "Update your kernel to one that carries commit e55d98e77561 (\"x86/CPU: Fix FPDSS on Zen1\", mainline Linux 7.1),\n " \
"or the equivalent backport from your distribution. The kernel sets bit 9 of MSR 0xc0011028 unconditionally on\n " \
"every Zen1 CPU at boot, which disables the hardware optimization responsible for the leak.\n " \
"To manually mitigate the issue right now, you may use the following command:\n " \
"\`wrmsr -a 0xc0011028 \$((\$(rdmsr -c 0xc0011028) | (1<<9)))\`,\n " \
"however note that this manual mitigation will only be active until the next reboot.\n " \
"No microcode update is required: the chicken bit is present on every Zen1 CPU."
}
check_CVE_2025_54505_linux() {
local status sys_interface_available msg kernel_mitigated dmesg_fpdss msr_fpdss ret
status=UNK
sys_interface_available=0
msg=''
# No sysfs interface exists for this vulnerability (no /sys/devices/system/cpu/vulnerabilities/fpdss).
# sys_interface_available stays 0.
#
# Kernel source inventory for FPDSS, traced via git blame:
#
# --- sysfs messages ---
# none: this vulnerability has no sysfs entry
#
# --- Kconfig symbols ---
# none: the mitigation is unconditional, not configurable (no CONFIG_* knob)
#
# --- kernel functions (for $opt_map / System.map) ---
# none: the fix is two inline lines in init_amd_zen1(), no dedicated function
#
# --- dmesg ---
# e55d98e77561 (v7.1, initial fix): "AMD Zen1 FPDSS bug detected, enabling mitigation."
# (printed via pr_notice_once on every Zen1 CPU)
#
# --- /proc/cpuinfo bugs field ---
# none: no X86_BUG_FPDSS flag defined; no cpuinfo exposure
#
# --- MSR ---
# e55d98e77561 (v7.1): MSR_AMD64_FP_CFG = 0xc0011028, bit 9 = ZEN1_DENORM_FIX_BIT
# kernel calls msr_set_bit() unconditionally on any Zen1 CPU in init_amd_zen1().
# The bit is present in Zen1 silicon independently of microcode (no microcode
# revision gate in the kernel, unlike Zenbleed which uses amd_zenbleed_microcode[]).
#
# --- CPU affection logic (for is_cpu_affected) ---
# e55d98e77561 (v7.1): applied unconditionally in init_amd_zen1(), i.e. all Zen1
# AMD: family 0x17 models 0x00-0x2f, 0x50-0x5f (same cohort as DIV0)
# vendor scope: AMD only (Zen1 microarchitecture)
#
# --- stable backports ---
# as of this writing, no stable/LTS backport has landed; only mainline (Linux 7.1).
if [ "$opt_sysfs_only" != 1 ]; then
pr_info_nol "* Kernel supports FPDSS mitigation: "
kernel_mitigated=''
if [ -n "$g_kernel_err" ]; then
pstatus yellow UNKNOWN "$g_kernel_err"
elif is_x86_kernel && grep -q 'AMD Zen1 FPDSS bug detected' "$g_kernel"; then
kernel_mitigated="found FPDSS mitigation message in kernel image"
pstatus green YES "$kernel_mitigated"
else
pstatus yellow NO
fi
pr_info_nol "* FPDSS mitigation enabled and active: "
msr_fpdss=''
dmesg_fpdss=''
if [ "$g_mode" = live ] && is_x86_cpu && is_cpu_affected "$cve"; then
# guard with is_cpu_affected to avoid #GP on non-Zen1 CPUs where 0xc0011028 is undefined
read_msr 0xc0011028
ret=$?
if [ "$ret" = "$READ_MSR_RET_OK" ]; then
if [ $((ret_read_msr_value_lo >> 9 & 1)) -eq 1 ]; then
msr_fpdss=1
pstatus green YES "ZEN1_DENORM_FIX_BIT set in FP_CFG MSR"
else
msr_fpdss=0
pstatus yellow NO "ZEN1_DENORM_FIX_BIT is cleared in FP_CFG MSR"
fi
else
# MSR unreadable (lockdown, no msr module, etc.): fall back to dmesg
dmesg_grep 'AMD Zen1 FPDSS bug detected'
ret=$?
if [ "$ret" -eq 0 ]; then
dmesg_fpdss=1
pstatus green YES "FPDSS mitigation message found in dmesg"
elif [ "$ret" -eq 2 ]; then
pstatus yellow UNKNOWN "couldn't read MSR and dmesg is truncated"
else
pstatus yellow UNKNOWN "couldn't read MSR and no FPDSS message in dmesg"
fi
fi
elif [ "$g_mode" = live ]; then
pstatus blue N/A "CPU is incompatible"
else
pstatus blue N/A "not testable in no-runtime mode"
fi
elif [ "$sys_interface_available" = 0 ]; then
msg="/sys vulnerability interface use forced, but it's not available!"
status=UNK
fi
if ! is_cpu_affected "$cve"; then
pvulnstatus "$cve" OK "your CPU vendor reported your CPU model as not affected"
elif [ -z "$msg" ]; then
if [ "$opt_sysfs_only" != 1 ]; then
if [ "$g_mode" = live ]; then
if [ "$msr_fpdss" = 1 ] || [ "$dmesg_fpdss" = 1 ]; then
pvulnstatus "$cve" OK "ZEN1_DENORM_FIX_BIT is set in FP_CFG MSR, mitigation is active"
elif [ "$msr_fpdss" = 0 ]; then
pvulnstatus "$cve" VULN "ZEN1_DENORM_FIX_BIT is cleared in FP_CFG MSR, FPDSS can leak data between threads"
_cve_2025_54505_explain_fix
elif [ -n "$kernel_mitigated" ]; then
# MSR unreadable at runtime, but kernel image carries the mitigation code
# and init_amd_zen1() sets the bit unconditionally, so mitigation is active
pvulnstatus "$cve" OK "kernel image carries FPDSS mitigation code (init_amd_zen1 sets the MSR bit unconditionally at boot)"
else
pvulnstatus "$cve" VULN "your kernel doesn't support FPDSS mitigation"
_cve_2025_54505_explain_fix
fi
else
if [ -n "$kernel_mitigated" ]; then
pvulnstatus "$cve" OK "Mitigation: FPDSS message found in kernel image"
else
pvulnstatus "$cve" VULN "your kernel doesn't support FPDSS mitigation"
_cve_2025_54505_explain_fix
fi
fi
else
pvulnstatus "$cve" "$status" "no sysfs interface available for this CVE, use --no-sysfs to check"
fi
else
pvulnstatus "$cve" "$status" "$msg"
fi
}
check_CVE_2025_54505_bsd() {
if ! is_cpu_affected "$cve"; then
pvulnstatus "$cve" OK "your CPU vendor reported your CPU model as not affected"
else
pvulnstatus "$cve" UNK "your CPU is affected, but mitigation detection has not yet been implemented for BSD in this script"
fi
}