mirror of
https://github.com/speed47/spectre-meltdown-checker.git
synced 2026-04-29 12:03:20 +02:00
Compare commits
1 Commits
source
...
c64d4bb481
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c64d4bb481 |
36
.github/workflows/autoupdate.yml
vendored
Normal file
36
.github/workflows/autoupdate.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
name: autoupdate
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '42 9 * * *'
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
autoupdate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install prerequisites
|
||||
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends iucode-tool sqlite3 unzip
|
||||
- name: Update microcode versions
|
||||
run: ./spectre-meltdown-checker.sh --update-builtin-fwdb
|
||||
- name: Check git diff
|
||||
id: diff
|
||||
run: |
|
||||
echo change="$(git diff spectre-meltdown-checker.sh | awk '/MCEDB/ { if(V) { print V" to "$4; exit } else { V=$4 } }')" >> "$GITHUB_OUTPUT"
|
||||
echo nbdiff="$(git diff spectre-meltdown-checker.sh | grep -cE -- '^\+# [AI],')" >> "$GITHUB_OUTPUT"
|
||||
git diff
|
||||
cat "$GITHUB_OUTPUT"
|
||||
- name: Create Pull Request if needed
|
||||
if: steps.diff.outputs.nbdiff != '0'
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
branch: autoupdate-fwdb
|
||||
commit-message: "update: fwdb from ${{ steps.diff.outputs.change }}, ${{ steps.diff.outputs.nbdiff }} microcode changes"
|
||||
title: "[Auto] Update fwdb from ${{ steps.diff.outputs.change }}"
|
||||
body: |
|
||||
Automated PR to update fwdb from ${{ steps.diff.outputs.change }}
|
||||
Detected ${{ steps.diff.outputs.nbdiff }} microcode changes
|
||||
2
.github/workflows/expected_cve_count
vendored
2
.github/workflows/expected_cve_count
vendored
@@ -1 +1 @@
|
||||
32
|
||||
31
|
||||
|
||||
33
.github/workflows/stale.yml
vendored
Normal file
33
.github/workflows/stale.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: 'Manage stale issues and PRs'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '37 7 * * *'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
action:
|
||||
description: "dry-run"
|
||||
required: true
|
||||
default: "dryrun"
|
||||
type: choice
|
||||
options:
|
||||
- dryrun
|
||||
- apply
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v10
|
||||
with:
|
||||
any-of-labels: 'needs-more-info,answered'
|
||||
labels-to-remove-when-unstale: 'needs-more-info,answered'
|
||||
days-before-stale: 30
|
||||
days-before-close: 7
|
||||
stale-issue-label: stale
|
||||
remove-stale-when-updated: true
|
||||
debug-only: ${{ case(inputs.action == 'dryrun', true, false) }}
|
||||
@@ -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
6
dist/README.md
vendored
@@ -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
|
||||
|
||||
11
dist/doc/UNSUPPORTED_CVE_LIST.md
vendored
11
dist/doc/UNSUPPORTED_CVE_LIST.md
vendored
@@ -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)
|
||||
|
||||
8
dist/doc/batch_json.md
vendored
8
dist/doc/batch_json.md
vendored
@@ -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
|
||||
|
||||
14
dist/doc/batch_json.schema.json
vendored
14
dist/doc/batch_json.schema.json
vendored
@@ -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)" }
|
||||
}
|
||||
|
||||
9
dist/doc/batch_nrpe.md
vendored
9
dist/doc/batch_nrpe.md
vendored
@@ -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
|
||||
|
||||
78
dist/doc/batch_prometheus.md
vendored
78
dist/doc/batch_prometheus.md
vendored
@@ -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/`)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
@@ -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
|
||||
|
||||
34
src/main.sh
34
src/main.sh
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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!"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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=$?
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user