Compare commits

..

1 Commits

Author SHA1 Message Date
Stéphane Lesimple
c64d4bb481 enh: guard x86/arm specific checks in kernel/cpu for the proper arch 2026-04-10 18:39:35 +02:00
35 changed files with 358 additions and 665 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

@@ -1 +1 @@
32
31

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) }}

View File

@@ -105,8 +105,8 @@ The entire tool is a single bash script with no external script dependencies. Ke
- **Output/logging functions** (~line 253): `pr_warn`, `pr_info`, `pr_verbose`, `pr_debug`, `explain`, `pstatus`, `pvulnstatus` - verbosity-aware output with color support
- **CPU detection** (~line 2171): `parse_cpu_details`, `is_intel`/`is_amd`/`is_hygon`, `read_cpuid`, `read_msr`, `is_cpu_smt_enabled` - hardware identification via CPUID/MSR registers
- **Kernel architecture detection** (`src/libs/365_kernel_arch.sh`): `is_arm_kernel`/`is_x86_kernel` - detects the **target kernel's** architecture (not the host CPU) using kernel artifacts (System.map symbols, kconfig, kernel image), with `cpu_vendor` as a fast path for live mode. Results are cached in `g_kernel_arch`. Use these helpers to guard arch-specific kernel/kconfig/System.map checks and to select the appropriate verdict messages. In no-hw mode, the target kernel may differ from the host CPU architecture.
- **CPU architecture detection** (`src/libs/360_cpu_smt.sh`): `is_x86_cpu`/`is_arm_cpu` - detects the **host CPU's** architecture via `cpu_vendor`. Use these to gate hardware operations (CPUID, MSR, microcode) that require the physical CPU to be present. Always use positive logic: `if is_x86_cpu` (not `if ! is_arm_cpu`). These two sets of helpers are independent — a vuln check may need both, each guarding different lines.
- **Kernel architecture detection** (`src/libs/365_kernel_arch.sh`): `is_arm_kernel`/`is_x86_kernel` - detects the target kernel's architecture (not the host CPU) using kernel artifacts (System.map symbols, kconfig, kernel image), with `cpu_vendor` as a fast path for live mode. Results are cached in `g_kernel_arch`. Use these helpers to guard arch-specific kernel/kconfig/System.map checks and to select the appropriate verdict messages
- **CPU architecture detection** (`src/libs/360_cpu_smt.sh`): `is_x86_cpu`/`is_arm_cpu` - detects the host CPU's architecture via `cpu_vendor`. Use these to gate hardware operations (CPUID, MSR, microcode). Always use positive logic: `if is_x86_cpu` (not `if ! is_arm_cpu`)
- **Microcode database** (embedded): Intel/AMD microcode version lookup via `read_mcedb`/`read_inteldb`; updated automatically via `.github/workflows/autoupdate.yml`
- **Kernel analysis** (~line 1568): `extract_kernel`, `try_decompress` - extracts and inspects kernel images (handles gzip, bzip2, xz, lz4, zstd compression)
- **Vulnerability checks**: 19 `check_CVE_<year>_<number>()` functions, each with `_linux()` and `_bsd()` variants. Uses whitelist logic (assumes affected unless proven otherwise)
@@ -118,7 +118,7 @@ The entire tool is a single bash script with no external script dependencies. Ke
Two JSON formats are available via `--batch`:
- **`--batch json`** (comprehensive): A top-level object with five sections:
- `meta` — script version, format version, timestamp, `mode` (`live`, `no-runtime`, `no-hw`, `hw-only`), run mode flags (`run_as_root`, `reduced_accuracy`, `mocked`, `paranoid`, `sysfs_only`, `extra`)
- `meta` — script version, format version, timestamp, run mode flags (`run_as_root`, `reduced_accuracy`, `mocked`, `paranoid`, `sysfs_only`, `extra`)
- `system` — kernel release/version/arch/cmdline, CPU count, SMT status, hypervisor host detection
- `cpu``arch` discriminator (`x86` or `arm`), vendor, friendly name, then an arch-specific sub-object (`cpu.x86` or `cpu.arm`) with identification fields (family/model/stepping/CPUID/codename for x86; part\_list/arch\_list for ARM) and a `capabilities` sub-object containing hardware flags as booleans/nulls
- `cpu_microcode``installed_version`, `latest_version`, `microcode_up_to_date`, `is_blacklisted`, firmware DB source/info
@@ -161,7 +161,7 @@ This works because the kernel always has direct access to CPUID (it doesn't need
**Rules:**
- This is strictly a fallback: `read_cpuid` via `/dev/cpu/N/cpuid` remains the primary method.
- Only use it when `read_cpuid` returned `READ_CPUID_RET_ERR` (device unavailable), **never** when it returned `READ_CPUID_RET_KO` (device available but bit is 0 — meaning the CPU/hypervisor explicitly reports the feature as absent).
- Only in live mode (`$g_mode = live`), since `/proc/cpuinfo` is not available in other modes.
- Only in live mode (`$opt_runtime = 1`), since `/proc/cpuinfo` is not available in no-runtime mode.
- Only for CPUID bits that the kernel exposes as `/proc/cpuinfo` flags. Not all bits have a corresponding flag — only those listed in the kernel's `capflags.c`. If a bit has no `/proc/cpuinfo` flag, no fallback is possible.
- The fallback depends on the running kernel being recent enough to know about the CPUID bit in question. An older kernel won't expose a flag it doesn't know about, so the fallback will silently not trigger — which is fine (we just stay at UNKNOWN, same as the ERR case without fallback).
@@ -184,7 +184,7 @@ read_cpuid 0x7 0x0 $EDX 31 1 1
ret=$?
if [ $ret = $READ_CPUID_RET_OK ]; then
cap_ssbd='Intel SSBD'
elif [ $ret = $READ_CPUID_RET_ERR ] && [ "$g_mode" = live ]; then
elif [ $ret = $READ_CPUID_RET_ERR ] && [ "$opt_runtime" = 1 ]; then
# CPUID device unavailable (e.g. in a VM): fall back to /proc/cpuinfo
if grep ^flags "$g_procfs/cpuinfo" | grep -qw ssbd; then
cap_ssbd='Intel SSBD (cpuinfo)'
@@ -291,12 +291,7 @@ 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`. 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:
Create `src/vulns/CVE-YYYY-NNNNN.sh`. 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 (`###############################`)
@@ -401,10 +396,9 @@ This is where the real detection lives. Check for mitigations at each layer:
Use **positive logic** — always `if is_x86_kernel` (not `if ! is_arm_kernel`) and `if is_x86_cpu` (not `if ! is_arm_cpu`). This ensures unknown architectures (MIPS, RISC-V, PowerPC) are handled safely by defaulting to "skip" rather than "execute."
Two sets of helpers serve different purposes — in no-hw mode the host CPU and the kernel being inspected can be different architectures, so the correct guard depends on what is being checked:
- **`is_x86_kernel`/`is_arm_kernel`**: Gate checks that inspect **kernel artifacts** (kernel image strings, kconfig, System.map). These detect the architecture of the target kernel, not the host, so they work correctly in offline/no-hw mode when analyzing a foreign kernel.
- **`is_x86_cpu`/`is_arm_cpu`**: Gate **hardware operations** that require the host CPU to be a given architecture (CPUID, MSR reads, `/proc/cpuinfo` flags, microcode version checks). These always reflect the running host CPU.
- Within a single vuln check, you may need **both** guards independently — e.g. `is_x86_cpu` for the microcode/MSR check and `is_x86_kernel` for the kernel image grep, not one wrapping the other.
Two sets of helpers serve different purposes:
- **`is_x86_kernel`/`is_arm_kernel`**: Gate kernel artifact checks (kernel image strings, kconfig, System.map).
- **`is_x86_cpu`/`is_arm_cpu`**: Gate hardware operations (CPUID, MSR, `/proc/cpuinfo` flags).
Example:
```sh
@@ -423,16 +417,16 @@ This is where the real detection lives. Check for mitigations at each layer:
```
The same applies to Phase 4 verdict messages: when the explanation or remediation advice differs between architectures (e.g. "CPU microcode update" vs "firmware/kernel update"), branch on `is_arm_kernel`/`is_x86_kernel` rather than on `cpu_vendor`, because `cpu_vendor` reflects the host, not the target kernel.
- **Runtime state** (live mode only): Read MSRs, check cpuinfo flags, parse dmesg, inspect debugfs. All runtime-only checks — including `/proc/cpuinfo` flags — must be guarded by `if [ "$g_mode" = live ]`, both when collecting the evidence in Phase 2 and when using it in Phase 4. In Phase 4, use explicit live/non-live branches so that live-only variables (e.g. cpuinfo flags, MSR values) are never referenced in the non-live path.
- **Runtime state** (live mode only): Read MSRs, check cpuinfo flags, parse dmesg, inspect debugfs. All runtime-only checks — including `/proc/cpuinfo` flags — must be guarded by `if [ "$opt_runtime" = 1 ]`, both when collecting the evidence in Phase 2 and when using it in Phase 4. In Phase 4, use explicit live/no-runtime branches so that live-only variables (e.g. cpuinfo flags, MSR values) are never referenced in the no-runtime path.
```sh
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
read_msr 0xADDRESS
ret=$?
if [ "$ret" = "$READ_MSR_RET_OK" ]; then
# check specific bits in ret_read_msr_value_lo / ret_read_msr_value_hi
fi
else
pstatus blue N/A "not testable in non-live mode"
pstatus blue N/A "not testable in no-runtime mode"
fi
```
@@ -817,7 +811,7 @@ CVEs that need VMM context should call `check_has_vmm` early in their `_linux()`
- **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 non-live modes** use `$g_mode` to branch (`if [ "$g_mode" = live ]`), and print `N/A "not testable in non-live mode"` for runtime-only checks when not in live mode. Inside CVE checks, `live` is the only mode with runtime access (hw-only skips the CVE loop). Outside CVE checks (e.g. `check_cpu`), use the `has_runtime` helper which returns true for both `live` and `hw-only`.
- **Always handle both live and no-runtime modes** - use `$opt_runtime` to branch, and print `N/A "not testable in no-runtime mode"` for runtime-only checks when in no-runtime mode.
- **Use `explain()`** when reporting VULN to give actionable remediation advice (see "Cross-Cutting Features" above).
- **Handle `--paranoid` and `--vmm`** when the CVE has stricter mitigation tiers or VMM-specific aspects (see "Cross-Cutting Features" above).
- **Keep JSON output in sync** - when adding new `cap_*` variables, add them to `_build_json_cpu()` in `src/libs/250_output_emitters.sh` (see Step 2 JSON note above). Per-CVE fields are handled automatically.

6
dist/README.md vendored
View File

@@ -38,7 +38,6 @@ 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?
@@ -78,7 +77,6 @@ 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.
@@ -209,10 +207,6 @@ 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

@@ -124,17 +124,6 @@ A branch predictor initialization issue specific to Intel's Lion Cove microarchi
These CVEs are real vulnerabilities, but no kernel or microcode fix has been issued, the mitigation is delegated to individual software, or the fix is not detectable by this tool.
## CVE-2018-3665 — Lazy FP State Restore (LazyFP)
- **Advisory:** [INTEL-SA-00145](https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/advisory-guidance/lazy-fp-state-restore.html)
- **Research paper:** [LazyFP: Leaking FPU Register State using Microarchitectural Side-Channels (Stecklina & Prescher, 2018)](https://arxiv.org/abs/1806.07480)
- **Affected CPUs:** Intel Core family (Sandy Bridge through Kaby Lake) when lazy FPU switching is in use
- **CVSS:** 4.3 (Medium)
Intel CPUs using lazy FPU state switching may speculatively expose another process's FPU/SSE/AVX register contents (including AES round keys and other cryptographic material) across context switches. The `#NM` (device-not-available) exception normally used to trigger lazy restore is delivered late enough that dependent instructions can transiently execute against the stale FPU state before the fault squashes them.
**Why out of scope:** The Linux mitigation is to use eager FPU save/restore, which was already the default on Intel CPUs with XSAVEOPT well before disclosure, and was then hard-enforced upstream by the removal of all lazy FPU code in Linux 4.14 (Andy Lutomirski's "x86/fpu: Hard-disable lazy FPU mode" cleanup). There is no `/sys/devices/system/cpu/vulnerabilities/` entry, no CPUID flag, no MSR, and no kernel config option that reflects this mitigation — detection on a running kernel would require hardcoding kernel version ranges, which is against this tool's design principles (same rationale as CVE-2019-15902). In practice, any supported kernel today is eager-FPU-only, and CPUs advertising XSAVEOPT/XSAVES cannot enter the vulnerable lazy-switching mode regardless of kernel configuration.
## CVE-2018-9056 — BranchScope
- **Issue:** [#169](https://github.com/speed47/spectre-meltdown-checker/issues/169)

View File

@@ -102,9 +102,7 @@ boundaries by a malicious guest. Prioritise remediation where
### `cpu`
CPU hardware identification. `null` when `--no-hw` is active, or when
`--arch-prefix` is set (host CPU info is then suppressed to avoid mixing
with a different-arch target kernel).
CPU hardware identification. `null` when `--no-hw` is active.
The object uses `arch` as a discriminator: `"x86"` for Intel/AMD/Hygon CPUs,
`"arm"` for ARM/Cavium/Phytium. Arch-specific fields live under a matching
@@ -142,7 +140,7 @@ fields from the other architecture.
#### `cpu.x86.capabilities`
Every capability is a **tri-state**: `true` (present), `false` (absent), or
Each capability is a **tri-state**: `true` (present), `false` (absent), or
`null` (not applicable or could not be read, e.g. when not root or on AMD for
Intel-specific features).
@@ -240,7 +238,7 @@ with an unknown CVE ID).
| `status` | string | `"OK"` / `"VULN"` / `"UNK"` | Check outcome (see below) |
| `vulnerable` | boolean \| null | `false` / `true` / `null` | `false`=OK, `true`=VULN, `null`=UNK |
| `info` | string | | Human-readable description of the specific mitigation state or reason |
| `sysfs_status` | string \| null | `"OK"` / `"VULN"` / `"UNK"` / null | Status as reported by the kernel via `/sys/devices/system/cpu/vulnerabilities/`; null if sysfs was not consulted for this CVE, or if the CVE's check read sysfs in silent/quiet mode (raw message is still captured in `sysfs_message`) |
| `sysfs_status` | string \| null | `"OK"` / `"VULN"` / `"UNK"` / null | Status as reported by the kernel via `/sys/devices/system/cpu/vulnerabilities/`; null if sysfs was not consulted for this CVE |
| `sysfs_message` | string \| null | | Raw text from the sysfs file (e.g. `"Mitigation: PTI"`); null if sysfs was not consulted |
#### Status values

View File

@@ -127,7 +127,7 @@
},
"cpu": {
"description": "CPU hardware identification. Null when --no-hw is active or when --arch-prefix is set (host CPU info is then suppressed to avoid mixing with a different-arch target kernel). Contains an 'arch' discriminator ('x86' or 'arm') and a matching arch-specific sub-object with identification fields and capabilities.",
"description": "CPU hardware identification. Null when --no-hw is active. Contains an 'arch' discriminator ('x86' or 'arm') and a matching arch-specific sub-object with identification fields and capabilities.",
"oneOf": [
{ "type": "null" },
{
@@ -180,16 +180,16 @@
"type": ["string", "null"]
},
"capabilities": {
"description": "CPU feature flags detected via CPUID and MSR reads. Every value is tri-state: true=present, false=absent, null=not applicable or unreadable.",
"description": "CPU feature flags detected via CPUID and MSR reads. Each value is true (present), false (absent), or null (not applicable or could not be read).",
"type": "object",
"additionalProperties": false,
"properties": {
"spec_ctrl": { "type": ["boolean", "null"], "description": "SPEC_CTRL MSR present (Intel; enables IBRS + IBPB via WRMSR)" },
"ibrs": { "type": ["boolean", "null"], "description": "IBRS supported (via SPEC_CTRL, IBRS_SUPPORT, or cpuinfo fallback)" },
"ibpb": { "type": ["boolean", "null"], "description": "IBPB supported (via SPEC_CTRL, IBPB_SUPPORT, or cpuinfo fallback)" },
"ibrs": { "type": ["boolean", "null"], "description": "Indirect Branch Restricted Speculation" },
"ibpb": { "type": ["boolean", "null"], "description": "Indirect Branch Prediction Barrier" },
"ibpb_ret": { "type": ["boolean", "null"], "description": "IBPB on return (enhanced form)" },
"stibp": { "type": ["boolean", "null"], "description": "STIBP supported (Intel/AMD/HYGON or cpuinfo fallback)" },
"ssbd": { "type": ["boolean", "null"], "description": "SSBD supported (SPEC_CTRL, VIRT_SPEC_CTRL, non-architectural MSR, or cpuinfo fallback)" },
"stibp": { "type": ["boolean", "null"], "description": "Single Thread Indirect Branch Predictors" },
"ssbd": { "type": ["boolean", "null"], "description": "Speculative Store Bypass Disable" },
"l1d_flush": { "type": ["boolean", "null"], "description": "L1D cache flush instruction" },
"md_clear": { "type": ["boolean", "null"], "description": "VERW clears CPU buffers (MDS mitigation)" },
"arch_capabilities": { "type": ["boolean", "null"], "description": "IA32_ARCH_CAPABILITIES MSR is present" },
@@ -231,7 +231,7 @@
"tsa_l1_no": { "type": ["boolean", "null"], "description": "Not susceptible to TSA-L1" },
"verw_clear": { "type": ["boolean", "null"], "description": "VERW clears CPU buffers" },
"autoibrs": { "type": ["boolean", "null"], "description": "AMD AutoIBRS (equivalent to enhanced IBRS on Intel)" },
"sbpb": { "type": ["boolean", "null"], "description": "Selective Branch Predictor Barrier (AMD Inception mitigation): true if PRED_CMD MSR SBPB bit write succeeded; false if write failed; null if not verifiable (non-root, CPUID error, or CPU does not report SBPB support)" },
"sbpb": { "type": ["boolean", "null"], "description": "Selective Branch Predictor Barrier (AMD Inception mitigation)" },
"avx2": { "type": ["boolean", "null"], "description": "AVX2 supported (relevant to Downfall / GDS)" },
"avx512": { "type": ["boolean", "null"], "description": "AVX-512 supported (relevant to Downfall / GDS)" }
}

View File

@@ -51,7 +51,6 @@ STATUS: summary | perfdata
| VULN + UNK | `N/T CVE(s) vulnerable: CVE-A CVE-B ..., M inconclusive` |
| UNK only | `N/T CVE checks inconclusive` |
| Non-root + VULN | `N/T CVE(s) appear vulnerable (unconfirmed, not root): CVE-A ...` |
| Non-root + VULN + UNK | `N/T CVE(s) appear vulnerable (unconfirmed, not root): CVE-A ..., M inconclusive` |
### Lines 2+ (long output)
@@ -60,19 +59,15 @@ Never parsed by the monitoring core; safe to add or reorder.
#### Context notes
Printed before per-CVE details when applicable. Notes are emitted in this
order when more than one applies:
Printed before per-CVE details when applicable:
| Note | Condition |
|---|---|
| `NOTE: paranoid mode active, stricter mitigation requirements applied` | `--paranoid` was used |
| `NOTE: hypervisor host detected (reason); L1TF/MDS severity is elevated` | System is detected as a VM host (KVM, Xen, VMware…) |
| `NOTE: hypervisor host detected (reason); L1TF/MDS severity is elevated` | System is a VM host (KVM, Xen, VMware…) |
| `NOTE: not a hypervisor host` | System is confirmed not a VM host |
| `NOTE: not running as root; MSR reads skipped, results may be incomplete` | Script ran without root privileges |
When VMM detection did not run (e.g. `--no-hw`), neither the
`hypervisor host detected` nor the `not a hypervisor host` note is printed.
#### Per-CVE detail lines
One line per non-OK CVE. VULN entries (`[CRITICAL]`) appear before UNK

View File

@@ -59,7 +59,7 @@ Script metadata. Always value `1`; all data is in labels.
| Label | Values | Meaning |
|---|---|---|
| `version` | string | Script version (e.g. `25.30.0250400123`) |
| `mode` | `live` / `no-runtime` / `no-hw` / `hw-only` | Operating mode (see below) |
| `mode` | `live` / `offline` | `live` = running on the active kernel; `offline` = inspecting a kernel image |
| `run_as_root` | `true` / `false` | Whether the script ran as root. Non-root scans skip MSR reads and may miss mitigations |
| `paranoid` | `true` / `false` | `--paranoid` mode: stricter criteria (e.g. requires SMT disabled) |
| `sysfs_only` | `true` / `false` | `--sysfs-only` mode: only the kernel's own sysfs report was used, not independent detection |
@@ -90,16 +90,13 @@ smc_build_info{version="25.30.0250400123",mode="live",run_as_root="true",paranoi
Operating system and kernel metadata. Always value `1`.
Absent entirely when none of `kernel_release`, `kernel_arch`, or
`hypervisor_host` can be determined (e.g. non-live mode with no VMM detection).
Each label is emitted only when its value is known; missing labels are
omitted rather than set to an empty string.
Absent in offline mode when neither `uname -r` nor `uname -m` is available.
| Label | Values | Meaning |
|---|---|---|
| `kernel_release` | string | Output of `uname -r`; emitted only in live mode |
| `kernel_arch` | string | Output of `uname -m`; emitted only in live mode |
| `hypervisor_host` | `true` / `false` | Whether this machine is detected as a hypervisor host (running KVM, Xen, VMware, etc.); absent when VMM detection did not run (e.g. `--no-hw`) |
| `kernel_release` | string | Output of `uname -r` (live mode only) |
| `kernel_arch` | string | Output of `uname -m` (live mode only) |
| `hypervisor_host` | `true` / `false` | Whether this machine is detected as a hypervisor host (running KVM, Xen, VMware, etc.) |
**Example:**
```
@@ -117,47 +114,26 @@ a malicious guest. Always prioritise remediation on hosts where
### `smc_cpu_info`
CPU hardware and microcode metadata. Always value `1`. Absent when `--no-hw`
is used or when `--arch-prefix` is set (host CPU info is suppressed to avoid
mixing with a different-arch target kernel).
Common labels (always emitted when the data is available):
is used.
| Label | Values | Meaning |
|---|---|---|
| `vendor` | string | CPU vendor (e.g. `GenuineIntel`, `AuthenticAMD`, `HygonGenuine`, `ARM`) |
| `vendor` | string | CPU vendor (e.g. `Intel`, `AuthenticAMD`) |
| `model` | string | CPU friendly name from `/proc/cpuinfo` |
| `arch` | `x86` / `arm` | Architecture family; determines which arch-specific labels follow |
| `smt` | `true` / `false` | Whether SMT (HyperThreading) is currently enabled; absent if undeterminable |
| `microcode` | hex string | Installed microcode version (e.g. `0xf4`); absent if unreadable |
| `microcode_latest` | hex string | Latest known-good microcode version from the firmware database; absent if the CPU is not in the database |
| `microcode_up_to_date` | `true` / `false` | Whether `microcode == microcode_latest`; absent if either is unavailable |
| `microcode_blacklisted` | `true` / `false` | Whether the installed microcode is known to cause problems and should be rolled back; emitted whenever `microcode` is emitted |
x86-only labels (emitted when `arch="x86"`):
| Label | Values | Meaning |
|---|---|---|
| `family` | integer string | CPU family number |
| `model_id` | integer string | CPU model number |
| `stepping` | integer string | CPU stepping number |
| `cpuid` | hex string | Full CPUID value (e.g. `0x000906ed`) |
| `codename` | string | Intel CPU codename (e.g. `Coffee Lake`); absent on AMD/Hygon |
| `cpuid` | hex string | Full CPUID value (e.g. `0x000906ed`); absent on some ARM CPUs |
| `codename` | string | Intel CPU codename (e.g. `Coffee Lake`); absent on AMD and ARM |
| `smt` | `true` / `false` | Whether SMT (HyperThreading) is currently enabled |
| `microcode` | hex string | Installed microcode version (e.g. `0xf4`) |
| `microcode_latest` | hex string | Latest known-good microcode version from the firmware database |
| `microcode_up_to_date` | `true` / `false` | Whether `microcode == microcode_latest` |
| `microcode_blacklisted` | `true` / `false` | Whether the installed microcode is known to cause problems and should be rolled back |
ARM-only labels (emitted when `arch="arm"`):
| Label | Values | Meaning |
|---|---|---|
| `part_list` | string | Space-separated list of ARM part numbers across cores (e.g. `0xd0b 0xd05` on big.LITTLE) |
| `arch_list` | string | Space-separated list of ARM architecture levels across cores (e.g. `8 8`) |
**x86 example:**
**Example:**
```
smc_cpu_info{vendor="GenuineIntel",model="Intel(R) Core(TM) i7-9700K CPU @ 3.60GHz",arch="x86",family="6",model_id="158",stepping="13",cpuid="0x000906ed",codename="Coffee Lake",smt="true",microcode="0xf4",microcode_latest="0xf4",microcode_up_to_date="true",microcode_blacklisted="false"} 1
```
**ARM example:**
```
smc_cpu_info{vendor="ARM",model="ARM v8 model 0xd0b",arch="arm",part_list="0xd0b 0xd05",arch_list="8 8",smt="false"} 1
smc_cpu_info{vendor="Intel",model="Intel(R) Core(TM) i7-9700K CPU @ 3.60GHz",family="6",model_id="158",stepping="13",cpuid="0x000906ed",codename="Coffee Lake",smt="true",microcode="0xf4",microcode_latest="0xf4",microcode_up_to_date="true",microcode_blacklisted="false"} 1
```
**Microcode labels:**
@@ -364,28 +340,16 @@ smc_vulnerability_status == 1
## Caveats and edge cases
**No-runtime mode (`--no-runtime`)**
**Offline mode (`--kernel`)**
`smc_system_info` will have no `kernel_release` or `kernel_arch` labels (those
come from `uname`, which reports the running kernel, not the inspected one).
`mode="no-runtime"` in `smc_build_info` signals this. No-runtime mode is
primarily useful for pre-deployment auditing, not fleet runtime monitoring.
`mode="offline"` in `smc_build_info` signals this. Offline mode is primarily
useful for pre-deployment auditing, not fleet runtime monitoring.
**No-hardware mode (`--no-hw`)**
**`--no-hw`**
`smc_cpu_info` is not emitted. CPU and microcode labels are absent from all
queries. CVE checks that rely on hardware capability detection (`cap_*` flags,
MSR reads) will report `unknown` status. `mode="no-hw"` in `smc_build_info`
signals this.
**Cross-arch inspection (`--arch-prefix`)**
When a cross-arch toolchain prefix is passed, the script suppresses the host
CPU metadata so it does not get mixed with data from a different-arch target
kernel: `smc_cpu_info` is not emitted, the same as under `--no-hw`.
**Hardware-only mode (`--hw-only`)**
Only hardware detection is performed; CVE checks are skipped. `smc_cpu_info`
is emitted but no `smc_vulnerability_status` metrics appear (and
`smc_vulnerable_count` / `smc_unknown_count` are `0`). `mode="hw-only"` in
`smc_build_info` signals this.
MSR reads) will report `unknown` status.
**`--sysfs-only`**
The script trusts the kernel's sysfs report (`/sys/devices/system/cpu/vulnerabilities/`)

View File

@@ -125,13 +125,6 @@ opt_vmm=-1
opt_allow_msr_write=0
opt_cpu=0
opt_explain=0
# Canonical run mode, set at the end of option parsing.
# Values: live, no-runtime, no-hw, hw-only
g_mode='live'
# Return 0 (true) if runtime state is accessible (procfs, sysfs, dmesg, debugfs).
# True in live and hw-only modes; false in no-runtime and no-hw modes.
has_runtime() { [ "$g_mode" = live ] || [ "$g_mode" = hw-only ]; }
opt_paranoid=0
opt_extra=0
opt_mock=0
@@ -153,12 +146,6 @@ 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
@@ -192,7 +179,6 @@ 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,9 +106,8 @@ is_cpu_affected() {
affected_srbds=''
affected_mmio=''
affected_sls=''
# DIV0, FPDSS, Zenbleed and Inception are all AMD specific, look for "is_amd" below:
# DIV0, 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:
@@ -606,23 +605,13 @@ is_cpu_affected() {
fi
_set_immune variantl1tf
# DIV0 (Zen1/Zen+)
# DIV0 (Zen1 only)
# 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-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.
# bfff3c6692ce (v6.8): moved to init_amd_zen1(), unconditional for all Zen1
# All Zen1 CPUs are family 0x17, models 0x00-0x2f and 0x50-0x5f
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
@@ -832,7 +821,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 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: 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: 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, fpdss, 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, zenbleed, downfall, retbleed, inception, reptar, rfds, tsa, tsa-sq, tsa-l1, its, vmscape, bpi, sls"
exit 0
;;
1)
@@ -245,10 +245,6 @@ 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
@@ -348,16 +344,3 @@ if [ "$opt_runtime" = 0 ] && [ -z "$opt_kernel" ] && [ -z "$opt_config" ] && [ -
pr_warn "Option --no-runtime requires at least one of --kernel, --config, or --map"
exit 255
fi
# Derive the canonical run mode from the option flags.
# Modes: live (default), no-runtime (--no-runtime), no-hw (--no-hw), hw-only (--hw-only)
# shellcheck disable=SC2034
if [ "$opt_hw_only" = 1 ]; then
g_mode='hw-only'
elif [ "$opt_no_hw" = 1 ]; then
g_mode='no-hw'
elif [ "$opt_runtime" = 0 ]; then
g_mode='no-runtime'
else
g_mode='live'
fi

View File

@@ -15,17 +15,15 @@ _prom_escape() {
printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' | tr '\n' ' '
}
# Convert a shell capability value to a JSON boolean token
# Args: $1=value (1=true, 0=false, -1/empty=null, any other non-empty string=true)
# Prints: JSON token (true/false/null)
# Note: capability variables can be set to arbitrary strings internally to carry
# detection-path context (e.g. cap_ssbd='Intel SSBD'); for the JSON output those
# are normalized to true so consumers see a clean boolean | null type.
# Convert a shell capability value to a JSON token
# Args: $1=value (1=true, 0=false, -1/empty=null, other string=quoted string)
# Prints: JSON token
_json_cap() {
case "${1:-}" in
1) printf 'true' ;;
0) printf 'false' ;;
-1 | '') printf 'null' ;;
*) printf 'true' ;;
*) printf '"%s"' "$(_json_escape "$1")" ;;
esac
}
@@ -68,8 +66,17 @@ _json_bool() {
# Sets: g_json_meta
# shellcheck disable=SC2034
_build_json_meta() {
local timestamp
local timestamp mode
timestamp=$(date -u '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null || echo "unknown")
if [ "$opt_hw_only" = 1 ]; then
mode="hw-only"
elif [ "$opt_no_hw" = 1 ]; then
mode="no-hw"
elif [ "$opt_runtime" = 0 ]; then
mode="no-runtime"
else
mode="live"
fi
local run_as_root
if [ "$(id -u)" -eq 0 ]; then
run_as_root='true'
@@ -80,7 +87,7 @@ _build_json_meta() {
"$(_json_str "$VERSION")" \
"$(_json_str "$timestamp")" \
"$(_json_str "$g_os")" \
"$g_mode" \
"$mode" \
"$run_as_root" \
"$(_json_bool "${g_bad_accuracy:-0}")" \
"$(_json_bool "$opt_paranoid")" \
@@ -93,7 +100,7 @@ _build_json_meta() {
# shellcheck disable=SC2034
_build_json_system() {
local kernel_release kernel_version kernel_arch smt_val
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
kernel_release=$(uname -r)
kernel_version=$(uname -v)
kernel_arch=$(uname -m)
@@ -128,7 +135,7 @@ _build_json_system() {
# Sets: g_json_cpu
# shellcheck disable=SC2034
_build_json_cpu() {
local cpuid_hex codename caps arch_sub arch_type sbpb_norm
local cpuid_hex codename caps arch_sub arch_type
if [ -n "${cpu_cpuid:-}" ]; then
cpuid_hex=$(printf '0x%08x' "$cpu_cpuid")
else
@@ -139,15 +146,6 @@ _build_json_cpu() {
codename=$(get_intel_codename 2>/dev/null || true)
fi
# cap_sbpb uses non-standard encoding (1=YES, 2=NO, 3=UNKNOWN) because the
# CVE-2023-20569 check distinguishes the unknown case. Normalize for JSON.
case "${cap_sbpb:-}" in
1) sbpb_norm=1 ;;
2) sbpb_norm=0 ;;
3) sbpb_norm=-1 ;;
*) sbpb_norm='' ;;
esac
# Determine architecture type and build the arch-specific sub-object
case "${cpu_vendor:-}" in
GenuineIntel | AuthenticAMD | HygonGenuine)
@@ -201,7 +199,7 @@ _build_json_cpu() {
"$(_json_cap "${cap_tsa_l1_no:-}")" \
"$(_json_cap "${cap_verw_clear:-}")" \
"$(_json_cap "${cap_autoibrs:-}")" \
"$(_json_cap "$sbpb_norm")" \
"$(_json_cap "${cap_sbpb:-}")" \
"$(_json_cap "${cap_avx2:-}")" \
"$(_json_cap "${cap_avx512:-}")")
arch_sub=$(printf '{"family":%s,"model":%s,"stepping":%s,"cpuid":%s,"platform_id":%s,"hybrid":%s,"codename":%s,"capabilities":%s}' \
@@ -406,7 +404,7 @@ _emit_prometheus() {
# shellcheck disable=SC2034
_build_prometheus_system_info() {
local kernel_release kernel_arch hypervisor_host sys_labels
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
kernel_release=$(uname -r 2>/dev/null || true)
kernel_arch=$(uname -m 2>/dev/null || true)
else

View File

@@ -1,120 +0,0 @@
# vim: set ts=4 sw=4 sts=4 et:
###############################
# Kernel architecture detection helpers.
# Detects the target kernel's architecture regardless of the host system,
# enabling correct behavior in offline cross-inspection (e.g. x86 host
# analyzing an ARM kernel image or System.map).
# Global cache; populated by _detect_kernel_arch on first call.
# Values: 'arm', 'x86', 'unknown'
g_kernel_arch=''
# Internal: populate g_kernel_arch using all available information sources,
# in order from most to least reliable.
_detect_kernel_arch() {
# Return immediately if already detected
[ -n "$g_kernel_arch" ] && return 0
# arm64_sys_ is the ARM64 syscall table symbol prefix; present in any
# ARM64 System.map (or /proc/kallsyms) and in the kernel image itself.
# sys_call_table + vector_swi is the ARM (32-bit) equivalent.
if [ -n "$opt_map" ]; then
if grep -q 'arm64_sys_' "$opt_map" 2>/dev/null; then
g_kernel_arch='arm'
return 0
fi
if grep -q ' vector_swi$' "$opt_map" 2>/dev/null; then
g_kernel_arch='arm'
return 0
fi
fi
if [ -n "$g_kernel" ]; then
if grep -q 'arm64_sys_' "$g_kernel" 2>/dev/null; then
g_kernel_arch='arm'
return 0
fi
fi
# Kconfig is definitive when available
if [ -n "$opt_config" ]; then
if grep -qE '^CONFIG_(ARM64|ARM)=y' "$opt_config" 2>/dev/null; then
g_kernel_arch='arm'
return 0
fi
if grep -qE '^CONFIG_X86(_64)?=y' "$opt_config" 2>/dev/null; then
g_kernel_arch='x86'
return 0
fi
fi
# Cross-compilation prefix as a last resort (e.g. --arch-prefix aarch64-linux-gnu-)
case "${opt_arch_prefix:-}" in
aarch64-* | arm64-* | arm-* | armv*-)
g_kernel_arch='arm'
return 0
;;
x86_64-* | i686-* | i?86-*)
g_kernel_arch='x86'
return 0
;;
esac
# Last resort: if no artifacts identified the arch, assume the target
# kernel matches the host CPU. This covers live mode when no kernel
# image, config, or System.map is available.
if is_x86_cpu; then
g_kernel_arch='x86'
return 0
fi
if is_arm_cpu; then
g_kernel_arch='arm'
return 0
fi
g_kernel_arch='unknown'
return 0
}
# Return 0 (true) if the target kernel is ARM (32 or 64-bit), 1 otherwise.
is_arm_kernel() {
_detect_kernel_arch
[ "$g_kernel_arch" = 'arm' ]
}
# Return 0 (true) if the target kernel is x86/x86_64, 1 otherwise.
is_x86_kernel() {
_detect_kernel_arch
[ "$g_kernel_arch" = 'x86' ]
}
# Compare the target kernel's architecture against the host CPU.
# If they differ, hardware reads (CPUID, MSR, sysfs) would reflect the host,
# not the target kernel — force no-hw mode to avoid misleading results.
# Sets: g_mode (when mismatch detected)
# Callers: src/main.sh (after check_kernel_info, before check_cpu)
check_kernel_cpu_arch_mismatch() {
local host_arch
_detect_kernel_arch
host_arch='unknown'
if is_x86_cpu; then
host_arch='x86'
elif is_arm_cpu; then
host_arch='arm'
fi
# Unsupported CPU architecture (MIPS, RISC-V, PowerPC, ...): force no-hw
# since we have no hardware-level checks for these platforms
if [ "$host_arch" = 'unknown' ]; then
pr_warn "Unsupported CPU architecture (vendor: $cpu_vendor), forcing no-hw mode"
g_mode='no-hw'
return 0
fi
# If kernel arch is unknown, we can't tell if there's a mismatch
[ "$g_kernel_arch" = 'unknown' ] && return 0
[ "$host_arch" = "$g_kernel_arch" ] && return 0
pr_warn "Target kernel architecture ($g_kernel_arch) differs from host CPU ($host_arch), forcing no-hw mode"
g_mode='no-hw'
}

View File

@@ -18,7 +18,7 @@ if [ "$g_os" = Darwin ] || [ "$g_os" = VMkernel ]; then
fi
# check for mode selection inconsistency
if [ "$g_mode" = hw-only ]; then
if [ "$opt_hw_only" = 1 ]; then
if [ "$opt_cve_all" = 0 ]; then
show_usage
echo "$0: error: incompatible modes specified, --hw-only vs --variant" >&2
@@ -89,7 +89,7 @@ if [ "$opt_cpu" != all ] && [ "$opt_cpu" -gt "$g_max_core_id" ]; then
exit 255
fi
if has_runtime; then
if [ "$opt_runtime" = 1 ]; then
pr_info "Checking for vulnerabilities on current system"
# try to find the image of the current running kernel
@@ -226,7 +226,7 @@ if [ -e "$opt_kernel" ]; then
if ! command -v "${opt_arch_prefix}readelf" >/dev/null 2>&1; then
pr_debug "readelf not found"
g_kernel_err="missing '${opt_arch_prefix}readelf' tool, please install it, usually it's in the 'binutils' package"
elif [ "$opt_sysfs_only" = 1 ] || [ "$g_mode" = hw-only ]; then
elif [ "$opt_sysfs_only" = 1 ] || [ "$opt_hw_only" = 1 ]; then
g_kernel_err='kernel image decompression skipped'
else
extract_kernel "$opt_kernel"
@@ -251,7 +251,7 @@ else
fi
if [ -n "$g_kernel_version" ]; then
# in live mode, check if the img we found is the correct one
if has_runtime; then
if [ "$opt_runtime" = 1 ]; then
pr_verbose "Kernel image is \033[35m$g_kernel_version"
if ! echo "$g_kernel_version" | grep -qF "$(uname -r)"; then
pr_warn "Possible discrepancy between your running kernel '$(uname -r)' and the image '$g_kernel_version' we found ($opt_kernel), results might be incorrect"
@@ -283,7 +283,7 @@ sys_interface_check() {
msg=''
ret_sys_interface_check_fullmsg=''
if has_runtime && [ "$opt_no_sysfs" = 0 ] && [ -r "$file" ]; then
if [ "$opt_runtime" = 1 ] && [ "$opt_no_sysfs" = 0 ] && [ -r "$file" ]; then
:
else
g_mockme=$(printf "%b\n%b" "$g_mockme" "SMC_MOCK_SYSFS_$(basename "$file")_RET=1")
@@ -352,7 +352,7 @@ sys_interface_check() {
check_kernel_info() {
local config_display
pr_info "\033[1;34mKernel information\033[0m"
if has_runtime; then
if [ "$opt_runtime" = 1 ]; then
pr_info "* Kernel is \033[35m$g_os $(uname -r) $(uname -v) $(uname -m)\033[0m"
elif [ -n "$g_kernel_version" ]; then
pr_info "* Kernel is \033[35m$g_kernel_version\033[0m"
@@ -456,7 +456,7 @@ check_cpu() {
ret=invalid
pstatus yellow NO "unknown CPU"
fi
if [ -z "$cap_ibrs" ] && [ $ret = $READ_CPUID_RET_ERR ] && has_runtime; then
if [ -z "$cap_ibrs" ] && [ $ret = $READ_CPUID_RET_ERR ] && [ "$opt_runtime" = 1 ]; then
# CPUID device unavailable (e.g. in a VM): fall back to /proc/cpuinfo
if grep ^flags "$g_procfs/cpuinfo" | grep -qw ibrs; then
cap_ibrs='IBRS (cpuinfo)'
@@ -533,7 +533,7 @@ check_cpu() {
if [ $ret = $READ_CPUID_RET_OK ]; then
cap_ibpb='IBPB_SUPPORT'
pstatus green YES "IBPB_SUPPORT feature bit"
elif [ $ret = $READ_CPUID_RET_ERR ] && has_runtime && grep ^flags "$g_procfs/cpuinfo" | grep -qw ibpb; then
elif [ $ret = $READ_CPUID_RET_ERR ] && [ "$opt_runtime" = 1 ] && grep ^flags "$g_procfs/cpuinfo" | grep -qw ibpb; then
# CPUID device unavailable (e.g. in a VM): fall back to /proc/cpuinfo
cap_ibpb='IBPB (cpuinfo)'
pstatus green YES "ibpb flag in $g_procfs/cpuinfo"
@@ -604,7 +604,7 @@ check_cpu() {
ret=invalid
pstatus yellow UNKNOWN "unknown CPU"
fi
if [ -z "$cap_stibp" ] && [ $ret = $READ_CPUID_RET_ERR ] && has_runtime; then
if [ -z "$cap_stibp" ] && [ $ret = $READ_CPUID_RET_ERR ] && [ "$opt_runtime" = 1 ]; then
# CPUID device unavailable (e.g. in a VM): fall back to /proc/cpuinfo
if grep ^flags "$g_procfs/cpuinfo" | grep -qw stibp; then
cap_stibp='STIBP (cpuinfo)'
@@ -676,7 +676,7 @@ check_cpu() {
fi
fi
if [ -z "$cap_ssbd" ] && [ "$ret24" = $READ_CPUID_RET_ERR ] && [ "$ret25" = $READ_CPUID_RET_ERR ] && has_runtime; then
if [ -z "$cap_ssbd" ] && [ "$ret24" = $READ_CPUID_RET_ERR ] && [ "$ret25" = $READ_CPUID_RET_ERR ] && [ "$opt_runtime" = 1 ]; then
# CPUID device unavailable (e.g. in a VM): fall back to /proc/cpuinfo
if grep ^flags "$g_procfs/cpuinfo" | grep -qw ssbd; then
cap_ssbd='SSBD (cpuinfo)'
@@ -740,7 +740,7 @@ check_cpu() {
if [ $ret = $READ_CPUID_RET_OK ]; then
pstatus green YES "L1D flush feature bit"
cap_l1df=1
elif [ $ret = $READ_CPUID_RET_ERR ] && has_runtime && grep ^flags "$g_procfs/cpuinfo" | grep -qw flush_l1d; then
elif [ $ret = $READ_CPUID_RET_ERR ] && [ "$opt_runtime" = 1 ] && grep ^flags "$g_procfs/cpuinfo" | grep -qw flush_l1d; then
# CPUID device unavailable (e.g. in a VM): fall back to /proc/cpuinfo
pstatus green YES "flush_l1d flag in $g_procfs/cpuinfo"
cap_l1df=1
@@ -760,7 +760,7 @@ check_cpu() {
if [ $ret = $READ_CPUID_RET_OK ]; then
cap_md_clear=1
pstatus green YES "MD_CLEAR feature bit"
elif [ $ret = $READ_CPUID_RET_ERR ] && has_runtime && grep ^flags "$g_procfs/cpuinfo" | grep -qw md_clear; then
elif [ $ret = $READ_CPUID_RET_ERR ] && [ "$opt_runtime" = 1 ] && grep ^flags "$g_procfs/cpuinfo" | grep -qw md_clear; then
# CPUID device unavailable (e.g. in a VM): fall back to /proc/cpuinfo
cap_md_clear=1
pstatus green YES "md_clear flag in $g_procfs/cpuinfo"
@@ -830,7 +830,7 @@ check_cpu() {
if [ $ret = $READ_CPUID_RET_OK ]; then
pstatus green YES
cap_arch_capabilities=1
elif [ $ret = $READ_CPUID_RET_ERR ] && has_runtime && grep ^flags "$g_procfs/cpuinfo" | grep -qw arch_capabilities; then
elif [ $ret = $READ_CPUID_RET_ERR ] && [ "$opt_runtime" = 1 ] && grep ^flags "$g_procfs/cpuinfo" | grep -qw arch_capabilities; then
# CPUID device unavailable (e.g. in a VM): fall back to /proc/cpuinfo
pstatus green YES "arch_capabilities flag in $g_procfs/cpuinfo"
cap_arch_capabilities=1

View File

@@ -14,7 +14,7 @@ fi
pr_info
if [ "$g_mode" != no-hw ] && [ -z "$opt_arch_prefix" ]; then
if [ "$opt_no_hw" = 0 ] && [ -z "$opt_arch_prefix" ]; then
pr_info "\033[1;34mHardware check\033[0m"
check_cpu
check_cpu_vulnerabilities
@@ -24,7 +24,7 @@ fi
# Build JSON system/cpu/microcode sections (after check_cpu has populated cap_* vars and VMM detection)
if [ "$opt_batch" = 1 ] && [ "$opt_batch_format" = "json" ]; then
_build_json_system
if [ "$g_mode" != no-hw ] && [ -z "$opt_arch_prefix" ]; then
if [ "$opt_no_hw" = 0 ] && [ -z "$opt_arch_prefix" ]; then
_build_json_cpu
_build_json_cpu_microcode
fi
@@ -33,22 +33,18 @@ fi
# Build Prometheus info metric lines (same timing requirement as JSON builders above)
if [ "$opt_batch" = 1 ] && [ "$opt_batch_format" = "prometheus" ]; then
_build_prometheus_system_info
if [ "$g_mode" != no-hw ] && [ -z "$opt_arch_prefix" ]; then
if [ "$opt_no_hw" = 0 ] && [ -z "$opt_arch_prefix" ]; then
_build_prometheus_cpu_info
fi
fi
# now run the checks the user asked for (hw-only mode skips CVE checks)
if [ "$g_mode" = hw-only ]; then
pr_info "Hardware-only mode, skipping vulnerability checks"
else
for cve in $g_supported_cve_list; do
if [ "$opt_cve_all" = 1 ] || echo "$opt_cve_list" | grep -qw "$cve"; then
check_"$(echo "$cve" | tr - _)"
pr_info
fi
done
fi # g_mode != hw-only
# now run the checks the user asked for
for cve in $g_supported_cve_list; do
if [ "$opt_cve_all" = 1 ] || echo "$opt_cve_list" | grep -qw "$cve"; then
check_"$(echo "$cve" | tr - _)"
pr_info
fi
done
if [ -n "$g_final_summary" ]; then
pr_info "> \033[46m\033[30mSUMMARY:\033[0m$g_final_summary"
@@ -175,7 +171,15 @@ fi
if [ "$opt_batch" = 1 ] && [ "$opt_batch_format" = "prometheus" ]; then
prom_run_as_root='false'
[ "$(id -u)" -eq 0 ] && prom_run_as_root='true'
prom_mode="$g_mode"
if [ "$opt_hw_only" = 1 ]; then
prom_mode='hw-only'
elif [ "$opt_no_hw" = 1 ]; then
prom_mode='no-hw'
elif [ "$opt_runtime" = 0 ]; then
prom_mode='no-runtime'
else
prom_mode='live'
fi
prom_paranoid='false'
[ "$opt_paranoid" = 1 ] && prom_paranoid='true'
prom_sysfs_only='false'

View File

@@ -3,7 +3,7 @@
check_mds_bsd() {
local kernel_md_clear kernel_smt_allowed kernel_mds_enabled kernel_mds_state
pr_info_nol "* Kernel supports using MD_CLEAR mitigation: "
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
if sysctl hw.mds_disable >/dev/null 2>&1; then
pstatus green YES
kernel_md_clear=1
@@ -76,7 +76,7 @@ check_mds_bsd() {
else
if [ "$cap_md_clear" = 1 ]; then
if [ "$kernel_md_clear" = 1 ]; then
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
# mitigation must also be enabled
if [ "$kernel_mds_enabled" -ge 1 ]; then
if [ "$opt_paranoid" != 1 ] || [ "$kernel_smt_allowed" = 0 ]; then
@@ -95,7 +95,7 @@ check_mds_bsd() {
pvulnstatus "$cve" VULN "Your microcode supports mitigation, but your kernel doesn't, upgrade it to mitigate the vulnerability"
fi
else
if [ "$kernel_md_clear" = 1 ] && [ "$g_mode" = live ]; then
if [ "$kernel_md_clear" = 1 ] && [ "$opt_runtime" = 1 ]; then
# no MD_CLEAR in microcode, but FreeBSD may still have software-only mitigation active
case "$kernel_mds_state" in
software*)
@@ -138,7 +138,7 @@ check_mds_linux() {
if is_x86_kernel; then
pr_info_nol "* Kernel supports using MD_CLEAR mitigation: "
kernel_md_clear_can_tell=1
if [ "$g_mode" = live ] && grep ^flags "$g_procfs/cpuinfo" | grep -qw md_clear; then
if [ "$opt_runtime" = 1 ] && grep ^flags "$g_procfs/cpuinfo" | grep -qw md_clear; then
kernel_md_clear="md_clear found in $g_procfs/cpuinfo"
pstatus green YES "$kernel_md_clear"
fi
@@ -161,7 +161,7 @@ check_mds_linux() {
fi
fi
if [ "$g_mode" = live ] && [ "$sys_interface_available" = 1 ]; then
if [ "$opt_runtime" = 1 ] && [ "$sys_interface_available" = 1 ]; then
pr_info_nol "* Kernel mitigation is enabled and active: "
if echo "$ret_sys_interface_check_fullmsg" | grep -qi ^mitigation; then
mds_mitigated=1
@@ -194,7 +194,7 @@ check_mds_linux() {
# compute mystatus and mymsg from our own logic
if [ "$cap_md_clear" = 1 ]; then
if [ -n "$kernel_md_clear" ]; then
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
# mitigation must also be enabled
if [ "$mds_mitigated" = 1 ]; then
if [ "$opt_paranoid" != 1 ] || [ "$mds_smt_mitigated" = 1 ]; then

View File

@@ -165,7 +165,7 @@ check_mmio_linux() {
pstatus yellow NO
fi
if [ "$g_mode" = live ] && [ "$sys_interface_available" = 1 ]; then
if [ "$opt_runtime" = 1 ] && [ "$sys_interface_available" = 1 ]; then
pr_info_nol "* Kernel mitigation is enabled and active: "
if echo "$ret_sys_interface_check_fullmsg" | grep -qi ^mitigation; then
mmio_mitigated=1
@@ -198,7 +198,7 @@ check_mmio_linux() {
# compute mystatus and mymsg from our own logic
if [ "$cap_fb_clear" = 1 ]; then
if [ -n "$kernel_mmio" ]; then
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
# mitigation must also be enabled
if [ "$mmio_mitigated" = 1 ]; then
if [ "$opt_paranoid" != 1 ] || [ "$mmio_smt_mitigated" = 1 ]; then

View File

@@ -269,7 +269,7 @@ check_CVE_2017_5715_linux() {
g_ibpb_supported=''
g_ibpb_enabled=''
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
# in live mode, we can check for the ibrs_enabled file in debugfs
# all versions of the patches have it (NOT the case of IBPB or KPTI)
g_ibrs_can_tell=1
@@ -420,7 +420,7 @@ check_CVE_2017_5715_linux() {
fi
pr_info_nol " * IBRS enabled and active: "
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
if [ "$g_ibpb_enabled" = 2 ]; then
# if ibpb=2, ibrs is forcefully=0
pstatus blue NO "IBPB used instead of IBRS in all kernel entrypoints"
@@ -471,7 +471,7 @@ check_CVE_2017_5715_linux() {
fi
pr_info_nol " * IBPB enabled and active: "
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
case "$g_ibpb_enabled" in
"")
if [ "$g_ibrs_supported" = 1 ]; then
@@ -554,7 +554,7 @@ check_CVE_2017_5715_linux() {
#
# since 5.15.28, this is now "Retpolines" as the implementation was switched to a generic one,
# so we look for both "retpoline" and "retpolines"
if [ "$g_mode" = live ] && [ -n "$ret_sys_interface_check_fullmsg" ]; then
if [ "$opt_runtime" = 1 ] && [ -n "$ret_sys_interface_check_fullmsg" ]; then
if echo "$ret_sys_interface_check_fullmsg" | grep -qwi -e retpoline -e retpolines; then
if echo "$ret_sys_interface_check_fullmsg" | grep -qwi minimal; then
retpoline_compiler=0
@@ -605,7 +605,7 @@ check_CVE_2017_5715_linux() {
# only Red Hat has a tunable to disable it on runtime
retp_enabled=-1
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
if [ -e "$g_specex_knob_dir/retp_enabled" ]; then
retp_enabled=$(cat "$g_specex_knob_dir/retp_enabled" 2>/dev/null)
pr_debug "retpoline: found $g_specex_knob_dir/retp_enabled=$retp_enabled"
@@ -635,7 +635,7 @@ check_CVE_2017_5715_linux() {
if is_vulnerable_to_empty_rsb || [ "$opt_verbose" -ge 2 ]; then
pr_info_nol " * Kernel supports RSB filling: "
rsb_filling=0
if [ "$g_mode" = live ] && [ "$opt_no_sysfs" != 1 ]; then
if [ "$opt_runtime" = 1 ] && [ "$opt_no_sysfs" != 1 ]; then
# if we're live and we aren't denied looking into /sys, let's do it
if echo "$ret_sys_interface_check_fullmsg" | grep -qw RSB; then
rsb_filling=1
@@ -728,7 +728,7 @@ check_CVE_2017_5715_linux() {
*", IBPB"* | *"; IBPB"*) v2_ibpb_mode=conditional ;;
*) v2_ibpb_mode=disabled ;;
esac
elif [ "$g_mode" = live ]; then
elif [ "$opt_runtime" = 1 ]; then
case "$g_ibpb_enabled" in
2) v2_ibpb_mode=always-on ;;
1) v2_ibpb_mode=conditional ;;
@@ -826,7 +826,7 @@ check_CVE_2017_5715_linux() {
*"PBRSB-eIBRS: Vulnerable"*) v2_pbrsb_status=vulnerable ;;
*) v2_pbrsb_status=unknown ;;
esac
elif [ "$g_mode" != live ] && [ -n "$g_kernel" ]; then
elif [ "$opt_runtime" != 1 ] && [ -n "$g_kernel" ]; then
if grep -q 'PBRSB-eIBRS' "$g_kernel" 2>/dev/null; then
v2_pbrsb_status=sw-sequence
else
@@ -857,7 +857,7 @@ check_CVE_2017_5715_linux() {
*"BHI: Vulnerable"*) v2_bhi_status=vulnerable ;;
*) v2_bhi_status=unknown ;;
esac
elif [ "$g_mode" != live ] && [ -n "$opt_config" ] && [ -r "$opt_config" ]; then
elif [ "$opt_runtime" != 1 ] && [ -n "$opt_config" ] && [ -r "$opt_config" ]; then
if grep -q '^CONFIG_\(MITIGATION_\)\?SPECTRE_BHI' "$opt_config"; then
if [ "$cap_bhi" = 1 ]; then
v2_bhi_status=bhi_dis_s
@@ -881,7 +881,7 @@ check_CVE_2017_5715_linux() {
esac
# --- v2_vuln_module ---
if [ "$g_mode" = live ] && [ -n "$ret_sys_interface_check_fullmsg" ]; then
if [ "$opt_runtime" = 1 ] && [ -n "$ret_sys_interface_check_fullmsg" ]; then
pr_info_nol " * Non-retpoline module loaded: "
if echo "$ret_sys_interface_check_fullmsg" | grep -q 'vulnerable module loaded'; then
v2_vuln_module=1
@@ -982,7 +982,7 @@ check_CVE_2017_5715_linux() {
if [ -n "${SMC_MOCK_UNPRIVILEGED_BPF_DISABLED:-}" ]; then
_ebpf_disabled="$SMC_MOCK_UNPRIVILEGED_BPF_DISABLED"
g_mocked=1
elif [ "$g_mode" = live ] && [ -r "$g_procfs/sys/kernel/unprivileged_bpf_disabled" ]; then
elif [ "$opt_runtime" = 1 ] && [ -r "$g_procfs/sys/kernel/unprivileged_bpf_disabled" ]; then
_ebpf_disabled=$(cat "$g_procfs/sys/kernel/unprivileged_bpf_disabled" 2>/dev/null)
g_mockme=$(printf "%b\n%b" "$g_mockme" "SMC_MOCK_UNPRIVILEGED_BPF_DISABLED='$_ebpf_disabled'")
fi
@@ -1170,7 +1170,7 @@ check_CVE_2017_5715_linux() {
pvulnstatus "$cve" OK "Full IBPB is mitigating the vulnerability"
# No-runtime mode fallback
elif [ "$g_mode" != live ]; then
elif [ "$opt_runtime" != 1 ]; then
if [ "$retpoline" = 1 ] && [ -n "$g_ibpb_supported" ]; then
pvulnstatus "$cve" OK "no-runtime mode: kernel supports retpoline + IBPB to mitigate the vulnerability"
elif [ -n "$g_ibrs_supported" ] && [ -n "$g_ibpb_supported" ]; then

View File

@@ -101,48 +101,60 @@ check_CVE_2017_5753_linux() {
# For no-runtime analysis of these old kernels, match the specific instruction patterns.
if [ -z "$v1_kernel_mitigated" ]; then
pr_info_nol "* Kernel has array_index_mask_nospec (v4.15 binary pattern): "
# vanilla: look for the Linus' mask aka array_index_mask_nospec()
# that is inlined at least in raw_copy_from_user (__get_user_X symbols)
#mov PER_CPU_VAR(current_task), %_ASM_DX
#cmp TASK_addr_limit(%_ASM_DX),%_ASM_AX
#jae bad_get_user
# /* array_index_mask_nospec() are the 2 opcodes that follow */
#+sbb %_ASM_DX, %_ASM_DX
#+and %_ASM_DX, %_ASM_AX
#ASM_STAC
# x86 64bits: jae(0x0f 0x83 0x?? 0x?? 0x?? 0x??) sbb(0x48 0x19 0xd2) and(0x48 0x21 0xd0)
# x86 32bits: cmp(0x3b 0x82 0x?? 0x?? 0x00 0x00) jae(0x73 0x??) sbb(0x19 0xd2) and(0x21 0xd0)
#
# arm32
##ifdef CONFIG_THUMB2_KERNEL
##define CSDB ".inst.w 0xf3af8014"
##else
##define CSDB ".inst 0xe320f014" e320f014
##endif
#asm volatile(
# "cmp %1, %2\n" e1500003
#" sbc %0, %1, %1\n" e0c03000
#CSDB
#: "=r" (mask)
#: "r" (idx), "Ir" (sz)
#: "cc");
#
# http://git.arm.linux.org.uk/cgit/linux-arm.git/commit/?h=spectre&id=a78d156587931a2c3b354534aa772febf6c9e855
v1_mask_nospec=''
if [ -n "$g_kernel_err" ]; then
pstatus yellow UNKNOWN "couldn't check ($g_kernel_err)"
elif is_x86_kernel; then
# x86: binary pattern matching for array_index_mask_nospec()
# x86 64bits: jae(0x0f 0x83 ....) sbb(0x48 0x19 0xd2) and(0x48 0x21 0xd0)
# x86 32bits: cmp(0x3b 0x82 .. .. 0x00 0x00) jae(0x73 ..) sbb(0x19 0xd2) and(0x21 0xd0)
if ! command -v perl >/dev/null 2>&1; then
pstatus yellow UNKNOWN "missing 'perl' binary, please install it"
elif ! command -v perl >/dev/null 2>&1; then
pstatus yellow UNKNOWN "missing 'perl' binary, please install it"
else
perl -ne '/\x0f\x83....\x48\x19\xd2\x48\x21\xd0/ and $found++; END { exit($found ? 0 : 1) }' "$g_kernel"
ret=$?
if [ "$ret" -eq 0 ]; then
pstatus green YES "x86 64 bits array_index_mask_nospec()"
v1_mask_nospec="x86 64 bits array_index_mask_nospec"
else
perl -ne '/\x0f\x83....\x48\x19\xd2\x48\x21\xd0/ and $found++; END { exit($found ? 0 : 1) }' "$g_kernel"
perl -ne '/\x3b\x82..\x00\x00\x73.\x19\xd2\x21\xd0/ and $found++; END { exit($found ? 0 : 1) }' "$g_kernel"
ret=$?
if [ "$ret" -eq 0 ]; then
pstatus green YES "x86 64 bits array_index_mask_nospec()"
v1_mask_nospec="x86 64 bits array_index_mask_nospec"
pstatus green YES "x86 32 bits array_index_mask_nospec()"
v1_mask_nospec="x86 32 bits array_index_mask_nospec"
else
perl -ne '/\x3b\x82..\x00\x00\x73.\x19\xd2\x21\xd0/ and $found++; END { exit($found ? 0 : 1) }' "$g_kernel"
ret=$?
if [ "$ret" -eq 0 ]; then
pstatus green YES "x86 32 bits array_index_mask_nospec()"
v1_mask_nospec="x86 32 bits array_index_mask_nospec"
ret=$("${opt_arch_prefix}objdump" "$g_objdump_options" "$g_kernel" | grep -w -e f3af8014 -e e320f014 -B2 | grep -B1 -w sbc | grep -w -c cmp)
if [ "$ret" -gt 0 ]; then
pstatus green YES "$ret occurrence(s) found of arm 32 bits array_index_mask_nospec()"
v1_mask_nospec="arm 32 bits array_index_mask_nospec"
else
pstatus yellow NO
fi
fi
fi
elif is_arm_kernel; then
# arm32: match CSDB instruction (0xf3af8014 Thumb2 or 0xe320f014 ARM) preceded by sbc+cmp
# http://git.arm.linux.org.uk/cgit/linux-arm.git/commit/?h=spectre&id=a78d156587931a2c3b354534aa772febf6c9e855
if ! command -v "${opt_arch_prefix}objdump" >/dev/null 2>&1; then
pstatus yellow UNKNOWN "missing '${opt_arch_prefix}objdump' tool, please install it, usually it's in the binutils package"
else
ret=$("${opt_arch_prefix}objdump" "$g_objdump_options" "$g_kernel" | grep -w -e f3af8014 -e e320f014 -B2 | grep -B1 -w sbc | grep -w -c cmp)
if [ "$ret" -gt 0 ]; then
pstatus green YES "$ret occurrence(s) found of arm 32 bits array_index_mask_nospec()"
v1_mask_nospec="arm 32 bits array_index_mask_nospec"
else
pstatus yellow NO
fi
fi
else
pstatus yellow NO
fi
fi
@@ -160,69 +172,67 @@ check_CVE_2017_5753_linux() {
pstatus yellow NO
fi
if is_arm_kernel; then
pr_info_nol "* Kernel has mask_nospec64 (arm64): "
#.macro mask_nospec64, idx, limit, tmp
#sub \tmp, \idx, \limit
#bic \tmp, \tmp, \idx
#and \idx, \idx, \tmp, asr #63
#csdb
#.endm
#$ aarch64-linux-gnu-objdump -d vmlinux | grep -w bic -A1 -B1 | grep -w sub -A2 | grep -w and -B2
#ffffff8008082e44: cb190353 sub x19, x26, x25
#ffffff8008082e48: 8a3a0273 bic x19, x19, x26
#ffffff8008082e4c: 8a93ff5a and x26, x26, x19, asr #63
#ffffff8008082e50: d503229f hint #0x14
# /!\ can also just be "csdb" instead of "hint #0x14" for native objdump
#
# if we already have a detection, don't bother disassembling the kernel, the answer is no.
if [ -n "$v1_kernel_mitigated" ] || [ -n "$v1_mask_nospec" ] || [ "$g_redhat_canonical_spectre" -gt 0 ]; then
pstatus yellow NO
elif [ -n "$g_kernel_err" ]; then
pstatus yellow UNKNOWN "couldn't check ($g_kernel_err)"
elif ! command -v perl >/dev/null 2>&1; then
pstatus yellow UNKNOWN "missing 'perl' binary, please install it"
elif ! command -v "${opt_arch_prefix}objdump" >/dev/null 2>&1; then
pstatus yellow UNKNOWN "missing '${opt_arch_prefix}objdump' tool, please install it, usually it's in the binutils package"
pr_info_nol "* Kernel has mask_nospec64 (arm64): "
#.macro mask_nospec64, idx, limit, tmp
#sub \tmp, \idx, \limit
#bic \tmp, \tmp, \idx
#and \idx, \idx, \tmp, asr #63
#csdb
#.endm
#$ aarch64-linux-gnu-objdump -d vmlinux | grep -w bic -A1 -B1 | grep -w sub -A2 | grep -w and -B2
#ffffff8008082e44: cb190353 sub x19, x26, x25
#ffffff8008082e48: 8a3a0273 bic x19, x19, x26
#ffffff8008082e4c: 8a93ff5a and x26, x26, x19, asr #63
#ffffff8008082e50: d503229f hint #0x14
# /!\ can also just be "csdb" instead of "hint #0x14" for native objdump
#
# if we already have a detection, don't bother disassembling the kernel, the answer is no.
if [ -n "$v1_kernel_mitigated" ] || [ -n "$v1_mask_nospec" ] || [ "$g_redhat_canonical_spectre" -gt 0 ]; then
pstatus yellow NO
elif [ -n "$g_kernel_err" ]; then
pstatus yellow UNKNOWN "couldn't check ($g_kernel_err)"
elif ! command -v perl >/dev/null 2>&1; then
pstatus yellow UNKNOWN "missing 'perl' binary, please install it"
elif ! command -v "${opt_arch_prefix}objdump" >/dev/null 2>&1; then
pstatus yellow UNKNOWN "missing '${opt_arch_prefix}objdump' tool, please install it, usually it's in the binutils package"
else
"${opt_arch_prefix}objdump" "$g_objdump_options" "$g_kernel" | perl -ne 'push @r, $_; /\s(hint|csdb)\s/ && $r[0]=~/\ssub\s+(x\d+)/ && $r[1]=~/\sbic\s+$1,\s+$1,/ && $r[2]=~/\sand\s/ && exit(9); shift @r if @r>3'
ret=$?
if [ "$ret" -eq 9 ]; then
pstatus green YES "mask_nospec64 macro is present and used"
v1_mask_nospec="arm64 mask_nospec64"
else
"${opt_arch_prefix}objdump" "$g_objdump_options" "$g_kernel" | perl -ne 'push @r, $_; /\s(hint|csdb)\s/ && $r[0]=~/\ssub\s+(x\d+)/ && $r[1]=~/\sbic\s+$1,\s+$1,/ && $r[2]=~/\sand\s/ && exit(9); shift @r if @r>3'
ret=$?
if [ "$ret" -eq 9 ]; then
pstatus green YES "mask_nospec64 macro is present and used"
v1_mask_nospec="arm64 mask_nospec64"
else
pstatus yellow NO
fi
pstatus yellow NO
fi
fi
pr_info_nol "* Kernel has array_index_nospec (arm64): "
# in 4.19+ kernels, the mask_nospec64 asm64 macro is replaced by array_index_nospec, defined in nospec.h, and used in invoke_syscall()
# ffffff8008090a4c: 2a0203e2 mov w2, w2
# ffffff8008090a50: eb0200bf cmp x5, x2
# ffffff8008090a54: da1f03e2 ngc x2, xzr
# ffffff8008090a58: d503229f hint #0x14
# /!\ can also just be "csdb" instead of "hint #0x14" for native objdump
#
# if we already have a detection, don't bother disassembling the kernel, the answer is no.
if [ -n "$v1_kernel_mitigated" ] || [ -n "$v1_mask_nospec" ] || [ "$g_redhat_canonical_spectre" -gt 0 ]; then
pstatus yellow NO
elif [ -n "$g_kernel_err" ]; then
pstatus yellow UNKNOWN "couldn't check ($g_kernel_err)"
elif ! command -v perl >/dev/null 2>&1; then
pstatus yellow UNKNOWN "missing 'perl' binary, please install it"
elif ! command -v "${opt_arch_prefix}objdump" >/dev/null 2>&1; then
pstatus yellow UNKNOWN "missing '${opt_arch_prefix}objdump' tool, please install it, usually it's in the binutils package"
pr_info_nol "* Kernel has array_index_nospec (arm64): "
# in 4.19+ kernels, the mask_nospec64 asm64 macro is replaced by array_index_nospec, defined in nospec.h, and used in invoke_syscall()
# ffffff8008090a4c: 2a0203e2 mov w2, w2
# ffffff8008090a50: eb0200bf cmp x5, x2
# ffffff8008090a54: da1f03e2 ngc x2, xzr
# ffffff8008090a58: d503229f hint #0x14
# /!\ can also just be "csdb" instead of "hint #0x14" for native objdump
#
# if we already have a detection, don't bother disassembling the kernel, the answer is no.
if [ -n "$v1_kernel_mitigated" ] || [ -n "$v1_mask_nospec" ] || [ "$g_redhat_canonical_spectre" -gt 0 ]; then
pstatus yellow NO
elif [ -n "$g_kernel_err" ]; then
pstatus yellow UNKNOWN "couldn't check ($g_kernel_err)"
elif ! command -v perl >/dev/null 2>&1; then
pstatus yellow UNKNOWN "missing 'perl' binary, please install it"
elif ! command -v "${opt_arch_prefix}objdump" >/dev/null 2>&1; then
pstatus yellow UNKNOWN "missing '${opt_arch_prefix}objdump' tool, please install it, usually it's in the binutils package"
else
"${opt_arch_prefix}objdump" "$g_objdump_options" "$g_kernel" | perl -ne 'push @r, $_; /\s(hint|csdb)\s/ && $r[0]=~/\smov\s+(w\d+),\s+(w\d+)/ && $r[1]=~/\scmp\s+(x\d+),\s+(x\d+)/ && $r[2]=~/\sngc\s+$2,/ && exit(9); shift @r if @r>3'
ret=$?
if [ "$ret" -eq 9 ]; then
pstatus green YES "array_index_nospec macro is present and used"
v1_mask_nospec="arm64 array_index_nospec"
else
"${opt_arch_prefix}objdump" "$g_objdump_options" "$g_kernel" | perl -ne 'push @r, $_; /\s(hint|csdb)\s/ && $r[0]=~/\smov\s+(w\d+),\s+(w\d+)/ && $r[1]=~/\scmp\s+(x\d+),\s+(x\d+)/ && $r[2]=~/\sngc\s+$2,/ && exit(9); shift @r if @r>3'
ret=$?
if [ "$ret" -eq 9 ]; then
pstatus green YES "array_index_nospec macro is present and used"
v1_mask_nospec="arm64 array_index_nospec"
else
pstatus yellow NO
fi
pstatus yellow NO
fi
fi # is_arm_kernel
fi
elif [ "$sys_interface_available" = 0 ]; then
msg="/sys vulnerability interface use forced, but it's not available!"

View File

@@ -104,7 +104,7 @@ check_CVE_2017_5754_linux() {
mount_debugfs
pr_info_nol " * PTI enabled and active: "
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
dmesg_grep="Kernel/User page tables isolation: enabled"
dmesg_grep="$dmesg_grep|Kernel page table isolation enabled"
dmesg_grep="$dmesg_grep|x86/pti: Unmapping kernel while in userspace"
@@ -170,7 +170,7 @@ check_CVE_2017_5754_linux() {
is_xen_dom0 && xen_pv_domo=1
is_xen_domU && xen_pv_domu=1
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
# checking whether we're running under Xen PV 64 bits. If yes, we are affected by affected_variant3
# (unless we are a Dom0)
pr_info_nol "* Running as a Xen PV DomU: "
@@ -186,7 +186,7 @@ check_CVE_2017_5754_linux() {
pvulnstatus "$cve" OK "your CPU vendor reported your CPU model as not affected"
elif [ -z "$msg" ]; then
# if msg is empty, sysfs check didn't fill it, rely on our own test
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
if [ "$kpti_enabled" = 1 ]; then
pvulnstatus "$cve" OK "PTI mitigates the vulnerability"
elif [ "$xen_pv_domo" = 1 ]; then

View File

@@ -36,7 +36,7 @@ check_CVE_2018_12207_linux() {
fi
pr_info_nol "* iTLB Multihit mitigation enabled and active: "
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
if [ -n "$ret_sys_interface_check_fullmsg" ]; then
if echo "$ret_sys_interface_check_fullmsg" | grep -qF 'Mitigation'; then
pstatus green YES "$ret_sys_interface_check_fullmsg"
@@ -63,7 +63,7 @@ check_CVE_2018_12207_linux() {
elif [ -z "$msg" ]; then
# if msg is empty, sysfs check didn't fill it, rely on our own test
if [ "$opt_sysfs_only" != 1 ]; then
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
# if we're in live mode and $msg is empty, sysfs file is not there so kernel is too old
pvulnstatus "$cve" VULN "Your kernel doesn't support iTLB Multihit mitigation, update it"
else

View File

@@ -37,7 +37,7 @@ check_CVE_2018_3620_linux() {
fi
pr_info_nol "* PTE inversion enabled and active: "
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
if [ -n "$ret_sys_interface_check_fullmsg" ]; then
if echo "$ret_sys_interface_check_fullmsg" | grep -q 'Mitigation: PTE Inversion'; then
pstatus green YES
@@ -66,7 +66,7 @@ check_CVE_2018_3620_linux() {
# if msg is empty, sysfs check didn't fill it, rely on our own test
if [ "$opt_sysfs_only" != 1 ]; then
if [ "$pteinv_supported" = 1 ]; then
if [ "$pteinv_active" = 1 ] || [ "$g_mode" != live ]; then
if [ "$pteinv_active" = 1 ] || [ "$opt_runtime" != 1 ]; then
pvulnstatus "$cve" OK "PTE inversion mitigates the vulnerability"
else
pvulnstatus "$cve" VULN "Your kernel supports PTE inversion but it doesn't seem to be enabled"

View File

@@ -18,7 +18,7 @@ check_CVE_2018_3639_linux() {
fi
if [ "$opt_sysfs_only" != 1 ]; then
pr_info_nol "* Kernel supports disabling speculative store bypass (SSB): "
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
if grep -Eq 'Speculation.?Store.?Bypass:' "$g_procfs/self/status" 2>/dev/null; then
kernel_ssb="found in $g_procfs/self/status"
pr_debug "found Speculation.Store.Bypass: in $g_procfs/self/status"
@@ -57,7 +57,7 @@ check_CVE_2018_3639_linux() {
fi
kernel_ssbd_enabled=-1
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
# https://elixir.bootlin.com/linux/v5.0/source/fs/proc/array.c#L340
pr_info_nol "* SSB mitigation is enabled and active: "
if grep -Eq 'Speculation.?Store.?Bypass:[[:space:]]+thread' "$g_procfs/self/status" 2>/dev/null; then
@@ -106,7 +106,7 @@ check_CVE_2018_3639_linux() {
# if msg is empty, sysfs check didn't fill it, rely on our own test
if [ -n "$cap_ssbd" ]; then
if [ -n "$kernel_ssb" ]; then
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
if [ "$kernel_ssbd_enabled" -gt 0 ]; then
pvulnstatus "$cve" OK "your CPU and kernel both support SSBD and mitigation is enabled"
else

View File

@@ -69,19 +69,14 @@ check_CVE_2018_3646_linux() {
pr_info "* Mitigation 1 (KVM)"
pr_info_nol " * EPT is disabled: "
ept_disabled=-1
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
if ! [ -r "$SYS_MODULE_BASE/kvm_intel/parameters/ept" ]; then
pstatus blue N/A "the kvm_intel module is not loaded"
elif [ "$(cat "$SYS_MODULE_BASE/kvm_intel/parameters/ept")" = N ]; then
pstatus green YES
ept_disabled=1
else
ept_value="$(cat "$SYS_MODULE_BASE/kvm_intel/parameters/ept" 2>/dev/null || echo ERROR)"
if [ "$ept_value" = N ]; then
pstatus green YES
ept_disabled=1
elif [ "$ept_value" = ERROR ]; then
pstatus yellow UNK "Couldn't read $SYS_MODULE_BASE/kvm_intel/parameters/ept"
else
pstatus yellow NO
fi
pstatus yellow NO
fi
else
pstatus blue N/A "not testable in no-runtime mode"
@@ -89,7 +84,7 @@ check_CVE_2018_3646_linux() {
pr_info "* Mitigation 2"
pr_info_nol " * L1D flush is supported by kernel: "
if [ "$g_mode" = live ] && grep -qw flush_l1d "$g_procfs/cpuinfo"; then
if [ "$opt_runtime" = 1 ] && grep -qw flush_l1d "$g_procfs/cpuinfo"; then
l1d_kernel="found flush_l1d in $g_procfs/cpuinfo"
fi
if [ -z "$l1d_kernel" ]; then
@@ -111,7 +106,7 @@ check_CVE_2018_3646_linux() {
fi
pr_info_nol " * L1D flush enabled: "
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
if [ -n "$ret_sys_interface_check_fullmsg" ]; then
# vanilla: VMX: $l1dstatus, SMT $smtstatus
# Red Hat: VMX: SMT $smtstatus, L1D $l1dstatus
@@ -161,7 +156,7 @@ check_CVE_2018_3646_linux() {
fi
pr_info_nol " * Hardware-backed L1D flush supported: "
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
if grep -qw flush_l1d "$g_procfs/cpuinfo" || [ -n "$l1d_xen_hardware" ]; then
pstatus green YES "performance impact of the mitigation will be greatly reduced"
else

View File

@@ -33,7 +33,7 @@ check_CVE_2019_11135_linux() {
fi
pr_info_nol "* TAA mitigation enabled and active: "
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
if [ -n "$ret_sys_interface_check_fullmsg" ]; then
if echo "$ret_sys_interface_check_fullmsg" | grep -qE '^Mitigation'; then
pstatus green YES "$ret_sys_interface_check_fullmsg"
@@ -57,7 +57,7 @@ check_CVE_2019_11135_linux() {
pvulnstatus "$cve" OK "your CPU vendor reported your CPU model as not affected"
elif [ -z "$msg" ]; then
# if msg is empty, sysfs check didn't fill it, rely on our own test
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
# if we're in live mode and $msg is empty, sysfs file is not there so kernel is too old
pvulnstatus "$cve" VULN "Your kernel doesn't support TAA mitigation, update it"
else

View File

@@ -32,7 +32,7 @@ check_CVE_2020_0543_linux() {
pstatus yellow NO
fi
pr_info_nol "* SRBDS mitigation control is enabled and active: "
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
if [ -n "$ret_sys_interface_check_fullmsg" ]; then
if echo "$ret_sys_interface_check_fullmsg" | grep -qE '^Mitigation'; then
pstatus green YES "$ret_sys_interface_check_fullmsg"
@@ -61,7 +61,7 @@ check_CVE_2020_0543_linux() {
# SRBDS mitigation control is enabled
if [ -z "$msg" ]; then
# if msg is empty, sysfs check didn't fill it, rely on our own test
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
# if we're in live mode and $msg is empty, sysfs file is not there so kernel is too old
pvulnstatus "$cve" OK "Your microcode is up to date for SRBDS mitigation control. The kernel needs to be updated"
fi
@@ -75,7 +75,7 @@ check_CVE_2020_0543_linux() {
elif [ "$cap_srbds_on" = 0 ]; then
# SRBDS mitigation control is disabled
if [ -z "$msg" ]; then
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
# if we're in live mode and $msg is empty, sysfs file is not there so kernel is too old
pvulnstatus "$cve" VULN "Your microcode is up to date for SRBDS mitigation control. The kernel needs to be updated. Mitigation is disabled"
fi

View File

@@ -174,7 +174,7 @@ check_CVE_2022_29900_linux() {
# Zen/Zen+/Zen2: check IBPB microcode support and SMT
if [ "$cpu_family" = $((0x17)) ]; then
pr_info_nol "* CPU supports IBPB: "
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
if [ -n "$cap_ibpb" ]; then
pstatus green YES "$cap_ibpb"
else
@@ -217,7 +217,7 @@ check_CVE_2022_29900_linux() {
"doesn't fully protect cross-thread speculation."
elif [ -z "$kernel_unret" ] && [ -z "$kernel_ibpb_entry" ]; then
pvulnstatus "$cve" VULN "Your kernel doesn't have either UNRET_ENTRY or IBPB_ENTRY compiled-in"
elif [ "$smt_enabled" = 0 ] && [ -z "$cap_ibpb" ] && [ "$g_mode" = live ]; then
elif [ "$smt_enabled" = 0 ] && [ -z "$cap_ibpb" ] && [ "$opt_runtime" = 1 ]; then
pvulnstatus "$cve" VULN "SMT is enabled and your microcode doesn't support IBPB"
explain "Update your CPU microcode to get IBPB support, or disable SMT by adding\n" \
"\`nosmt\` to your kernel command line."

View File

@@ -84,7 +84,7 @@ check_CVE_2022_29901_linux() {
fi
pr_info_nol "* CPU supports Enhanced IBRS (IBRS_ALL): "
if [ "$g_mode" = live ] || [ "$cap_ibrs_all" != -1 ]; then
if [ "$opt_runtime" = 1 ] || [ "$cap_ibrs_all" != -1 ]; then
if [ "$cap_ibrs_all" = 1 ]; then
pstatus green YES
elif [ "$cap_ibrs_all" = 0 ]; then
@@ -97,7 +97,7 @@ check_CVE_2022_29901_linux() {
fi
pr_info_nol "* CPU has RSB Alternate Behavior (RSBA): "
if [ "$g_mode" = live ] || [ "$cap_rsba" != -1 ]; then
if [ "$opt_runtime" = 1 ] || [ "$cap_rsba" != -1 ]; then
if [ "$cap_rsba" = 1 ]; then
pstatus yellow YES "this CPU is affected by RSB underflow"
elif [ "$cap_rsba" = 0 ]; then

View File

@@ -145,7 +145,7 @@ check_CVE_2022_40982_linux() {
if [ -n "$kernel_gds" ]; then
pr_info_nol "* Kernel has disabled AVX as a mitigation: "
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
# Check dmesg message to see whether AVX has been disabled
dmesg_grep 'Microcode update needed! Disabling AVX as mitigation'
dmesgret=$?

View File

@@ -101,7 +101,7 @@ check_CVE_2023_20588_linux() {
pr_info_nol "* DIV0 mitigation enabled and active: "
cpuinfo_div0=''
dmesg_div0=''
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
if [ -e "$g_procfs/cpuinfo" ] && grep -qw 'div0' "$g_procfs/cpuinfo" 2>/dev/null; then
cpuinfo_div0=1
pstatus green YES "div0 found in $g_procfs/cpuinfo bug flags"
@@ -141,7 +141,7 @@ check_CVE_2023_20588_linux() {
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 [ "$opt_runtime" = 1 ]; then
# live mode: cpuinfo div0 flag is the strongest proof the mitigation is active
if [ "$cpuinfo_div0" = 1 ] || [ "$dmesg_div0" = 1 ]; then
_cve_2023_20588_pvulnstatus_smt

View File

@@ -28,7 +28,7 @@ check_CVE_2023_20593_linux() {
pstatus yellow NO
fi
pr_info_nol "* Zenbleed kernel mitigation enabled and active: "
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
# read the DE_CFG MSR, we want to check the 9th bit
# don't do it on non-Zen2 AMD CPUs or later, aka Family 17h,
# as the behavior could be unknown on others
@@ -82,7 +82,7 @@ check_CVE_2023_20593_linux() {
elif [ -z "$msg" ]; then
# if msg is empty, sysfs check didn't fill it, rely on our own test
zenbleed_print_vuln=0
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
if [ "$fp_backup_fix" = 1 ] && [ "$ucode_zenbleed" = 1 ]; then
# this should never happen, but if it does, it's interesting to know
pvulnstatus "$cve" OK "Both your CPU microcode and kernel are mitigating Zenbleed"

View File

@@ -69,48 +69,44 @@ check_CVE_2023_28746_linux() {
fi
if [ "$opt_sysfs_only" != 1 ]; then
if is_x86_cpu; then
pr_info_nol "* CPU microcode mitigates the vulnerability: "
if [ "$cap_rfds_clear" = 1 ]; then
pstatus green YES "RFDS_CLEAR capability indicated by microcode"
elif [ "$cap_rfds_clear" = 0 ]; then
pstatus yellow NO
else
pstatus yellow UNKNOWN "couldn't read MSR"
fi
pr_info_nol "* CPU microcode mitigates the vulnerability: "
if [ "$cap_rfds_clear" = 1 ]; then
pstatus green YES "RFDS_CLEAR capability indicated by microcode"
elif [ "$cap_rfds_clear" = 0 ]; then
pstatus yellow NO
else
pstatus yellow UNKNOWN "couldn't read MSR"
fi
if is_x86_kernel; then
pr_info_nol "* Kernel supports RFDS mitigation (VERW on transitions): "
kernel_rfds=''
kernel_rfds_err=''
if [ -n "$g_kernel_err" ]; then
kernel_rfds_err="$g_kernel_err"
elif grep -q 'Clear Register File' "$g_kernel"; then
kernel_rfds="found 'Clear Register File' string in kernel image"
elif grep -q 'reg_file_data_sampling' "$g_kernel"; then
kernel_rfds="found reg_file_data_sampling in kernel image"
fi
if [ -z "$kernel_rfds" ] && [ -r "$opt_config" ]; then
if grep -q '^CONFIG_MITIGATION_RFDS=y' "$opt_config"; then
kernel_rfds="RFDS mitigation config option found enabled in kernel config"
fi
fi
if [ -z "$kernel_rfds" ] && [ -n "$opt_map" ]; then
if grep -q 'rfds_select_mitigation' "$opt_map"; then
kernel_rfds="found rfds_select_mitigation in System.map"
fi
fi
if [ -n "$kernel_rfds" ]; then
pstatus green YES "$kernel_rfds"
elif [ -n "$kernel_rfds_err" ]; then
pstatus yellow UNKNOWN "$kernel_rfds_err"
else
pstatus yellow NO
pr_info_nol "* Kernel supports RFDS mitigation (VERW on transitions): "
kernel_rfds=''
kernel_rfds_err=''
if [ -n "$g_kernel_err" ]; then
kernel_rfds_err="$g_kernel_err"
elif is_x86_kernel && grep -q 'Clear Register File' "$g_kernel"; then
kernel_rfds="found 'Clear Register File' string in kernel image"
elif is_x86_kernel && grep -q 'reg_file_data_sampling' "$g_kernel"; then
kernel_rfds="found reg_file_data_sampling in kernel image"
fi
if [ -z "$kernel_rfds" ] && is_x86_kernel && [ -r "$opt_config" ]; then
if grep -q '^CONFIG_MITIGATION_RFDS=y' "$opt_config"; then
kernel_rfds="RFDS mitigation config option found enabled in kernel config"
fi
fi
if [ -z "$kernel_rfds" ] && is_x86_kernel && [ -n "$opt_map" ]; then
if grep -q 'rfds_select_mitigation' "$opt_map"; then
kernel_rfds="found rfds_select_mitigation in System.map"
fi
fi
if [ -n "$kernel_rfds" ]; then
pstatus green YES "$kernel_rfds"
elif [ -n "$kernel_rfds_err" ]; then
pstatus yellow UNKNOWN "$kernel_rfds_err"
else
pstatus yellow NO
fi
if is_x86_cpu && [ "$g_mode" = live ] && [ "$sys_interface_available" = 1 ]; then
if [ "$opt_runtime" = 1 ] && [ "$sys_interface_available" = 1 ]; then
pr_info_nol "* RFDS mitigation is enabled and active: "
if echo "$ret_sys_interface_check_fullmsg" | grep -qi '^Mitigation'; then
rfds_mitigated=1
@@ -133,7 +129,7 @@ check_CVE_2023_28746_linux() {
if [ "$opt_sysfs_only" != 1 ]; then
if [ "$cap_rfds_clear" = 1 ]; then
if [ -n "$kernel_rfds" ]; then
if [ "$g_mode" = live ]; then
if [ "$opt_runtime" = 1 ]; then
if [ "$rfds_mitigated" = 1 ]; then
pvulnstatus "$cve" OK "Your microcode and kernel are both up to date for this mitigation, and mitigation is enabled"
else

View File

@@ -1,151 +0,0 @@
# 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
}