Compare commits

..
11 Commits
Author SHA1 Message Date
Stéphane Lesimple bc00a81526 fix: xen: consider Xen dom0 as non-guest (#343 continued) 2026-07-30 16:05:49 +02:00
Stéphane Lesimple 5bbffaf053 fix: another attempt to avoid sigpipe on grep (#519)
Take this opportunity to factorize all the greps in /proc/cpuinfo
into a helper that avoids using a pipe to entirely avoid SIGPIPE
on a possibly gigantic /proc/cpuinfo
2026-06-10 23:33:10 +02:00
Stéphane Lesimple 23ea5427b5 fix: mmio: don't report "Intel never assessed this CPU" when the MSR is unreadable
When IA32_ARCH_CAPABILITIES (0x10a) can't be read from userspace (no msr
module, or kernel lockdown under Secure Boot), the FBSDP_NO/PSDP_NO/SBDR_SSDP_NO
bits were left at 0 ("explicitly not immune") instead of -1 ("unknown"). For a
recent CPU not in any kernel model list (e.g. Arrow Lake), this wrongly flipped
the MMIO Stale Data verdict into the "out of servicing period, Intel never
assessed this CPU" bucket.
2026-06-08 22:55:45 +02:00
Stéphane Lesimple cc159fe7fd fix: dmesg_grep: avoid sigpipe on some systems (#519)
Use 'grep -m 1' (works under Linux, busybox, BSD) instead of piping to head -n1
2026-06-08 21:41:08 +02:00
Stéphane Lesimple 737cfe4a5f arm64: add SSBS detection 2026-06-06 17:01:46 +02:00
Stéphane Lesimple 0b022ee253 fix: zenbleed (CVE-2023-20593) handle the VM guest case (#488)
Zenbleed (CVE-2023-20593) is mitigated either by up-to-date CPU microcode
or by the host kernel setting FP_BACKUP_FIX (DE_CFG MSR 0xc0011029 bit 9).
Both are applied at the host level. Inside a Xen dom0/domU (or any VM
guest) the script can't read that MSR and can't trust the microcode
version the hypervisor presents, so it wrongly concluded "kernel too old
+ microcode not fixed" and reported VULN even though the host had applied
the microcode fix (passing on bare metal).

In live mode, when the verdict would be VULN and we're running as a guest,
report UNK instead, explaining the mitigation is host-level and not
observable from inside the guest. Bare metal is unchanged (still VULN),
offline analysis is unchanged, and a guest with positively-confirmed
fixed microcode still reports OK.
2026-06-06 16:13:52 +02:00
Stéphane Lesimple 1e33f40f0a mds/mmio/taa: don't claim "disable SMT" inside a VM guest (#343)
Inside a Xen PVH domU (and any guest where the kernel sets
X86_FEATURE_HYPERVISOR), the kernel appends "; SMT Host state unknown"
to the MDS/MMIO sysfs vuln string: the host controls SMT scheduling and
the guest genuinely can't see it. The "SMT is either mitigated or
disabled" check only matched 'SMT (disabled|mitigated)', so this read as
"not mitigated" and --paranoid flipped the verdict to a misleading
VULN "you must disable SMT (Hyper-Threading)".

Make *_smt_mitigated a tri-state: 1 (disabled/mitigated), 0 (vulnerable),
and 2 (host state unknown). In paranoid mode, when the in-guest
mitigation is active but SMT host state is unknown, report UNK with an
explanation that cross-thread protection depends on the hypervisor host's
SMT/core-scheduling config, instead of VULN. PV DomUs (kernel reports
"SMT vulnerable", no HYPERVISOR bit) are unchanged and still flagged.
2026-06-06 16:13:52 +02:00
Stéphane Lesimple 1211c21261 xen: more reliable Xen/guest detection + container awareness (#173)
Better detect Xen guest type + add container detection

CVE-2017-5754: when we see Xen but we're inside a container,
/proc/xen/capabilities isn't exposed and dmesg is the host's,
so dom0 vs PV DomU can't be told apart. Don't report VULN in
that case, but UNKNOWN instead, and ask to rerun the script on the host.
2026-06-06 16:13:52 +02:00
Stéphane Lesimple d8abfbe20a doc: add CVE-2026-46174 (AMD Zen 2 Op Cache Improper Resource Isolation) to the unsupported list 2026-06-06 15:07:18 +02:00
Stéphane Lesimple 45fe976ca9 doc: add unsupported CVE to list (CVE-2021-26314 / CVE-2021-26313 / CVE-2025-52533)
CVE-2021-26314 / CVE-2021-26313 (Floating-Point Value Injection (FPVI) and Speculative Code Store Bypass (SCSB))
CVE-2025-52533 (AMD On-Chip Debug Interface Improper Access Control)
2026-06-06 12:53:22 +02:00
Stéphane Lesimple 44ba3790d9 fix: arm64: collapse per-core CPU info lists to a single line
Store the per-core implementer/part/arch/variant/revision lists
space-separated (no embedded newlines, which also cleans up JSON and
prometheus output) and dedup them for the human-readable display, so
homogeneous systems show e.g. "0x41" instead of repeating it per core.
2026-06-02 19:15:05 +02:00
5 changed files with 41 additions and 135 deletions
+3 -5
View File
@@ -41,22 +41,20 @@ The project uses 4 branches organized in two pipelines (production and dev/test)
| **`test-build`** | Monolithic test script (built artifact) | CI from `test` |
| **`source`** | Production source (split files + Makefile) | Developers |
| **`source-build`** | Monolithic test script (built artifact) | CI from `source` |
| **`master`** | Monolithic production script (built artifact) | `release` workflow, synced from `source-build` |
| **`master`** | Monolithic production script (built artifact) | PR by developers from `source-build` |
- **`source`** and **`test`** contain the split source files and the Makefile. These are the branches developers commit to.
- **`master`**, **`source-build`** and **`test-build`** contain only the monolithic `spectre-meltdown-checker.sh` built by CI. Nobody commits to these directly.
- **`master`** is the preexisting production branch that users pull from. It cannot be renamed.
- **`test-build`** is a testing branch that users can pull from to test pre-release versions.
- **`source-build`** is a preprod branch to prepare the artifact before releasing it to **`master`**. It is a build *output* branch and is never merged into `master`; instead the assembled files are copied across by the manual `release` workflow (see [RELEASE.md](RELEASE.md)), which keeps `master`'s own CI workflows untouched.
- **`source-build`** is a preprod branch to prepare the artifact before merging to **`master`**.
Typical workflow:
1. Feature/fix branches are created from `test` and merged back into `test`.
2. CI builds the script and pushes it to `test-build` for testing.
3. When ready for release, `test` is merged into `source`.
4. CI builds the script and pushes it to `source-build` for production.
5. Developer runs the manual `release` workflow to sync `source-build`'s
assembled files onto `master` and draft a GitHub release. See
[RELEASE.md](RELEASE.md) for the full procedure.
5. Developer creates a PR from `source-build` to `master`.
## Versioning
-86
View File
@@ -1,86 +0,0 @@
# Releasing
This document describes how a production release reaches the `master` branch
and how a GitHub release is published. It complements the **Branch Model**
section of [DEVELOPMENT.md](DEVELOPMENT.md).
This file lives only on the `source`/`test` development branches. It is **not**
part of `dist/`, so it never reaches `source-build` or `master` — users who
`git clone` the `master` branch onto their servers get only the files needed to
run the tool.
## Why `master` is not a merge target
`master` plays two roles at once:
- It is the **distribution branch** — users clone it directly onto their
servers, so it must stay minimal (just the monolithic script and the few
files needed to run/build the container).
- It is the repository's **default branch**, which is the only place GitHub
runs *scheduled* workflows from. The `autoupdate`, `stale` and `vuln-watch`
workflows are therefore **master-only** and deliberately kept off the
`source`/`test` line (see commit "remove from test branch workflows that must
live on master").
`source-build` is a build **output** branch and does **not** carry those
master-only workflows (the build's `rsync --delete` strips `.github/` down to
what `source` ships). Merging the whole `source-build` tree into `master` would
therefore drag the *absence* of those workflows into `master`, producing
recurring `modify/delete` conflicts — and, worse, a clean merge could silently
delete them when they hadn't been edited since the last release.
So we never merge `source-build` into `master`. Instead we **copy only the
assembled artifact files** across, and cut GitHub releases from `master`
directly.
## The `release` workflow
`.github/workflows/release.yml` (which, like the other master-only workflows,
lives **only on `master`**) is triggered manually via `workflow_dispatch` and
offers two independent actions selected from the `action` dropdown. Run it
against the `master` branch.
### 1. `sync-from-source-build`
Copies every top-level entry on `source-build` **except `.github/`**
(`spectre-meltdown-checker.sh`, `README.md`, `doc/`, `Dockerfile`,
`docker-compose.yml`) onto `master` as a single commit, mirroring exactly
(deletions and renames included). `master`'s own `.github/` — its
master-only CI — is never touched.
The script's contents are copied byte-for-byte, so its `VERSION` (generated by
the `source-build` build) is preserved unchanged. No version bump happens here.
The job is a no-op if `master` is already up to date.
### 2. `draft-github-release`
Reads the `VERSION` from the script currently on `master`, and creates a
**draft** GitHub release tagged `v<VERSION>`, with the monolithic script
attached as an asset. Because it is a draft, **no tag is created and nothing is
published** until you press *Publish* in the Releases UI — so this step is fully
reversible.
The changelog is auto-drafted by locating the `source-build` commit whose built
`VERSION` matches the previous published release, then listing every assembled
commit since. Treat it as a starting point and edit it before publishing.
## Release procedure
1. Confirm `source-build` holds the artifact you want to release (CI is green,
version string looks right).
2. Run the **`release`** workflow on `master` with `action =
sync-from-source-build`. Review the resulting commit/diff on `master`.
3. Run the **`release`** workflow on `master` with `action =
draft-github-release`.
4. Open the draft release, review/edit the auto-generated changelog, then
**Publish** it. Publishing creates the `v<VERSION>` tag.
Steps 2 and 3 are decoupled on purpose: you can refresh `master` from
`source-build` (step 2) without cutting a formal GitHub release yet.
## Rollback
- Before publishing: delete the draft release in the UI (no tag exists yet).
- After a bad `sync-from-source-build`: `master` history is intact — revert the
sync commit with a normal `git revert` (never force-push `master`).
+31 -35
View File
@@ -8,7 +8,7 @@
# with X being either I for Intel, or A for AMD
# When the date is unknown it defaults to 20000101
# %%% MCEDB v351+i20260512+1cce
# %%% MCEDB v349+i20260227+615b
# I,0x00000611,0xFF,0x00000B27,19961218
# I,0x00000612,0xFF,0x000000C6,19961210
# I,0x00000616,0xFF,0x000000C6,19961210
@@ -381,14 +381,14 @@
# I,0x000806F2,0xFF,0x8C0004E0,20211112
# I,0x000806F3,0xFF,0x8D000520,20220812
# I,0x000806F4,0x10,0x2C000421,20250825
# I,0x000806F4,0x87,0x2B000670,20251217
# I,0x000806F4,0x87,0x2B000661,20250825
# I,0x000806F5,0x10,0x2C000421,20250825
# I,0x000806F5,0x87,0x2B000670,20251217
# I,0x000806F5,0x87,0x2B000661,20250825
# I,0x000806F6,0x10,0x2C000421,20250825
# I,0x000806F6,0x87,0x2B000670,20251217
# I,0x000806F7,0x87,0x2B000670,20251217
# I,0x000806F6,0x87,0x2B000661,20250825
# I,0x000806F7,0x87,0x2B000661,20250825
# I,0x000806F8,0x10,0x2C000421,20250825
# I,0x000806F8,0x87,0x2B000670,20251217
# I,0x000806F8,0x87,0x2B000661,20250825
# I,0x00090660,0xFF,0x00000009,20200617
# I,0x00090661,0x01,0x0000001A,20240405
# I,0x00090670,0xFF,0x00000019,20201111
@@ -425,12 +425,11 @@
# I,0x000A06C0,0xFF,0x00000013,20230901
# I,0x000A06C1,0xFF,0x00000005,20231201
# I,0x000A06D0,0xFF,0x10000680,20240818
# I,0x000A06D1,0x20,0x0A000142,20260129
# I,0x000A06D1,0x95,0x01000423,20260129
# I,0x000A06E0,0xFF,0x80000953,20240902
# I,0x000A06E1,0x97,0x01000307,20260226
# I,0x000A06D1,0x20,0x0A000133,20251009
# I,0x000A06D1,0x95,0x01000405,20251031
# I,0x000A06E1,0x97,0x01000303,20251202
# I,0x000A06F0,0xFF,0x80000360,20240130
# I,0x000A06F3,0x01,0x030003A3,20260130
# I,0x000A06F3,0x01,0x03000382,20250730
# I,0x000B0650,0x80,0x0000000D,20250925
# I,0x000B0664,0xFF,0x00000030,20250529
# I,0x000B0670,0xFF,0x0000000E,20220220
@@ -440,28 +439,25 @@
# I,0x000B06A3,0xE0,0x00006134,20251008
# I,0x000B06A8,0xE0,0x00006134,20251008
# I,0x000B06D0,0xFF,0x0000001A,20240610
# I,0x000B06D1,0x80,0x00000126,20251210
# I,0x000B06D1,0x80,0x00000125,20250828
# I,0x000B06E0,0x19,0x00000021,20250912
# I,0x000B06F2,0x07,0x0000003E,20251012
# I,0x000B06F5,0x07,0x0000003E,20251012
# I,0x000B06F6,0x07,0x0000003E,20251012
# I,0x000B06F7,0x07,0x0000003E,20251012
# I,0x000C0652,0x82,0x00000121,20251215
# I,0x000C0652,0x82,0x0000011B,20250803
# I,0x000C0660,0xFF,0x00000018,20240516
# I,0x000C0662,0x82,0x00000121,20251215
# I,0x000C0664,0x82,0x00000121,20251215
# I,0x000C06A2,0x82,0x00000121,20251215
# I,0x000C0662,0x82,0x0000011B,20250803
# I,0x000C0664,0x82,0x0000011B,20250803
# I,0x000C06A2,0x82,0x0000011B,20250803
# I,0x000C06C0,0xFF,0x00000012,20250325
# I,0x000C06C1,0x90,0x0000011B,20260324
# I,0x000C06C2,0x90,0x0000011B,20260324
# I,0x000C06C3,0x90,0x0000011B,20260324
# I,0x000C06F1,0x87,0x210002E0,20251217
# I,0x000C06F2,0x87,0x210002E0,20251217
# I,0x000D0650,0xFF,0x00000009,20260309
# I,0x000D0651,0xFF,0x00000009,20260309
# I,0x000D0670,0xFF,0x00000137,20260218
# I,0x000D06D0,0xFF,0x80000370,20250917
# I,0x000D06D1,0xFF,0x01000120,20260325
# I,0x000C06C1,0xFF,0x00000115,20251203
# I,0x000C06C2,0xFF,0x00000115,20251203
# I,0x000C06C3,0xFF,0x00000115,20251203
# I,0x000C06F1,0x87,0x210002D3,20250825
# I,0x000C06F2,0x87,0x210002D3,20250825
# I,0x000D0670,0xFF,0x00000003,20250825
# I,0x000D06D0,0xFF,0x00000340,20250807
# I,0x00FF0671,0xFF,0x0000010E,20220907
# I,0x00FF0672,0xFF,0x0000000D,20210816
# I,0x00FF0675,0xFF,0x0000000D,20210816
@@ -558,13 +554,13 @@
# A,0x00880F40,0xFF,0x08804005,20210312
# A,0x00890F00,0xFF,0x08900007,20200921
# A,0x00890F01,0xFF,0x08900103,20201105
# A,0x00890F02,0xFF,0x08900208,20241219
# A,0x00890F02,0xFF,0x08900203,20230915
# A,0x00890F10,0xFF,0x08901003,20230919
# A,0x008A0F00,0xFF,0x08A0000B,20241125
# A,0x00A00F00,0xFF,0x0A000033,20200413
# A,0x00A00F10,0xFF,0x0A00107A,20240226
# A,0x00A00F11,0xFF,0x0A0011DF,20260312
# A,0x00A00F12,0xFF,0x0A00124B,20260305
# A,0x00A00F11,0xFF,0x0A0011DE,20250418
# A,0x00A00F12,0xFF,0x0A001247,20250327
# A,0x00A00F80,0xFF,0x0A008005,20230707
# A,0x00A00F82,0xFF,0x0A00820F,20241111
# A,0x00A10F00,0xFF,0x0A10004B,20220309
@@ -603,15 +599,15 @@
# A,0x00B00F00,0xFF,0x0B00004D,20240318
# A,0x00B00F10,0xFF,0x0B001016,20240318
# A,0x00B00F20,0xFF,0x0B002032,20241003
# A,0x00B00F21,0xFF,0x0B002162,20251105
# A,0x00B00F21,0xFF,0x0B002161,20251105
# A,0x00B00F80,0xFF,0x0B008011,20241211
# A,0x00B00F81,0xFF,0x0B008121,20251020
# A,0x00B10F00,0xFF,0x0B10000F,20240320
# A,0x00B10F10,0xFF,0x0B101059,20251105
# A,0x00B10F10,0xFF,0x0B101058,20251105
# A,0x00B20F40,0xFF,0x0B204037,20251019
# A,0x00B40F00,0xFF,0x0B400034,20240318
# A,0x00B40F40,0xFF,0x0B404038,20260408
# A,0x00B40F41,0xFF,0x0B40410B,20260408
# A,0x00B60F00,0xFF,0x0B60003C,20260401
# A,0x00B60F80,0xFF,0x0B60803C,20260401
# A,0x00B40F40,0xFF,0x0B404035,20251020
# A,0x00B40F41,0xFF,0x0B404108,20251020
# A,0x00B60F00,0xFF,0x0B600037,20251019
# A,0x00B60F80,0xFF,0x0B608038,20251019
# A,0x00B70F00,0xFF,0x0B700037,20251019
-1
View File
@@ -72,7 +72,6 @@
readonly INTEL_FAM6_ARROWLAKE_U=$((0xB5))
readonly INTEL_FAM6_LUNARLAKE_M=$((0xBD)) # /* Lion Cove / Skymont */
readonly INTEL_FAM6_PANTHERLAKE_L=$((0xCC)) # /* Cougar Cove / Darkmont */
readonly INTEL_FAM6_PANTHERLAKE_R=$((0xE5)) # /* Cougar Cove / Darkmont */
readonly INTEL_FAM6_WILDCATLAKE_L=$((0xD5))
readonly INTEL_FAM18_NOVALAKE=$((0x01)) # /* Coyote Cove / Arctic Wolf */
readonly INTEL_FAM18_NOVALAKE_L=$((0x03)) # /* Coyote Cove / Arctic Wolf */
+7 -8
View File
@@ -562,7 +562,6 @@ is_cpu_affected() {
06-c5-02/82,0000011b
06-c6-02/82,0000011b
06-bd-01/80,00000125
06-55-07/bf,05003901
06-55-0b/bf,07002b01
06-8f-07/87,2b000661
06-8f-08/87,2b000661
@@ -725,9 +724,9 @@ is_cpu_affected() {
if [ -n "$cpupart" ] && [ -n "$cpuarch" ]; then
# Cortex-R7 and Cortex-R8 are real-time and only used in medical devices or such
# I can't find their CPU part number, but it's probably not that useful anyway
# model R7 R8 A8 A9 A12 A15 A17 A57 A72 A73 A75 A76 A77 Neoverse-N1 Neoverse-V1 Neoverse-N1 Neoverse-V2 Neoverse-V3 Neoverse-V3AE
# part ? ? c08 c09 c0d c0f c0e d07 d08 d09 d0a d0b d0d d0c d40 d49 d4f d84 d83
# arch 7? 7? 7 7 7 7 7 8 8 8 8 8 8 8 8 8 8 8 8
# model R7 R8 A8 A9 A12 A15 A17 A57 A72 A73 A75 A76 A77 Neoverse-N1 Neoverse-V1 Neoverse-N1 Neoverse-V2
# part ? ? c08 c09 c0d c0f c0e d07 d08 d09 d0a d0b d0d d0c d40 d49 d4f
# arch 7? 7? 7 7 7 7 7 8 8 8 8 8 8 8 8 8 8
#
# Whitelist identified non-affected processors, use vulnerability information from
# https://developer.arm.com/support/arm-security-updates/speculative-processor-vulnerability
@@ -778,13 +777,13 @@ is_cpu_affected() {
_infer_immune variant3a
_set_vuln variant4
pr_debug "checking cpu$i: armv8 A76/A77/NeoverseN1 non affected to variant 2, 3 & 3a"
elif [ "$cpuarch" = 8 ] && echo "$cpupart" | grep -q -w -e 0xd40 -e 0xd49 -e 0xd4f -e 0xd84 -e 0xd83; then
elif [ "$cpuarch" = 8 ] && echo "$cpupart" | grep -q -w -e 0xd40 -e 0xd49 -e 0xd4f; then
_set_vuln variant1
_infer_immune variant2
_infer_immune variant3
_infer_immune variant3a
_infer_immune variant4
pr_debug "checking cpu$i: armv8 NeoverseN2/V1/V2/V3/V3AE non affected to variant 2, 3, 3a & 4"
pr_debug "checking cpu$i: armv8 NeoverseN2/V1/V2 non affected to variant 2, 3, 3a & 4"
elif [ "$cpuarch" -le 7 ] || { [ "$cpuarch" = 8 ] && [ $((cpupart)) -lt $((0xd07)) ]; }; then
_infer_immune variant1
_infer_immune variant2
@@ -845,14 +844,14 @@ is_cpu_affected() {
# - arm64 (CVE-2020-13844): Cortex-A32/A34/A35/A53/A57/A72/A73 confirmed affected,
# and broadly all speculative Armv8-A cores. No kernel mitigation merged.
# Part numbers: A32=0xd01 A34=0xd02 A53=0xd03 A35=0xd04 A57=0xd07 A72=0xd08 A73=0xd09
# Plus later speculative cores: A75=0xd0a A76=0xd0b A77=0xd0d N1=0xd0c V1=0xd40 N2=0xd49 V2=0xd4f V3=0xd84 V3AE=0xd83
# Plus later speculative cores: A75=0xd0a A76=0xd0b A77=0xd0d N1=0xd0c V1=0xd40 N2=0xd49 V2=0xd4f
if is_intel || is_amd; then
_infer_vuln sls
elif [ "$cpu_vendor" = ARM ]; then
for cpupart in $cpu_part_list; do
if echo "$cpupart" | grep -q -w -e 0xd01 -e 0xd02 -e 0xd03 -e 0xd04 \
-e 0xd07 -e 0xd08 -e 0xd09 -e 0xd0a -e 0xd0b -e 0xd0c -e 0xd0d \
-e 0xd40 -e 0xd49 -e 0xd4f -e 0xd84 -e 0xd83; then
-e 0xd40 -e 0xd49 -e 0xd4f; then
_set_vuln sls
fi
done