Check cpu-vulnerabilities¶
Overview¶
Reports which of the CPU vulnerabilities the kernel knows about are left without a mitigation on this host. The kernel publishes its own verdict per vulnerability, so the check reports what the running kernel, the microcode and the boot parameters together actually achieve, and not what the CPU model would allow. This works in a virtual machine as well, where the answer additionally depends on what the hypervisor hands through. A vulnerability the kernel cannot decide on is reported separately and does not alert by default, because a guest regularly cannot see what its host does; raise --unknown-severity to flag it where the answer is expected. Alerts when a vulnerability the CPU is affected by has no mitigation in effect.
What the kernel reports:
The kernel keeps one file per vulnerability, named after its code name (meltdown, spectre_v2, mds, retbleed and so on), and writes a line into each that says where this machine stands. There are four verdicts:
| Verdict | Meaning |
|---|---|
Not affected |
This CPU does not have the flaw. |
Mitigation: <how> |
It has it, and something is holding it off. The text names what. |
Vulnerable[: <why>] |
It has it and nothing is holding it off. The text names what is missing, most often the microcode. |
Unknown[: <why>] |
The kernel cannot decide. Practically always a guest whose answer belongs to its hypervisor. |
Two files word their verdict differently, and the check reads both. itlb_multihit speaks for the KVM side only and puts the component in front, so it reports KVM: Vulnerable or KVM: Mitigation: VMX unsupported; on a kernel built without KVM support it reports Processor vulnerable instead.
Important Notes:
- The verdict is the first word of the line, never a word somewhere in it. Several files append markers that describe a residual risk inside an applied mitigation, for example
; SMT vulnerable,; BHI: Vulnerableor; PBRSB-eIBRS: Vulnerable. A host reportingMitigation: Enhanced / Automatic IBRS; IBPB: conditional; BHI: Vulnerableis mitigated in the kernel's own judgement, and a check that greps for the word "Vulnerable" calls it the opposite. The full line is in the output, so the residual markers stay visible to anyone reading it. - Two states contradict their own prefix, and both read as vulnerable. Booted with
indirect_target_selection=vmexitthe kernel writesMitigation: Vulnerable, KVM: Not affectedintoindirect_target_selection; its own documentation calls that state "System is vulnerable to intra-mode BTI, but not affected by eIBRS guest/host isolation", so only the guests are covered and the host is not. And the kernel documentation listsMitigation: Noneforspectre_v2and explains it as "Vulnerable, no mitigation", although no kernel from 4.15 to 7.1 actually writes it. These are the only two places where the check does not follow the leading word. - In a virtual machine the answer is only half the truth. A guest sees the CPU model and the microcode its hypervisor hands through, and nothing of what the hypervisor itself does. That is why
srbdsandgather_data_samplingreportUnknown: Dependent on hypervisor statuson an affected CPU under a hypervisor. Run the check on the hypervisor as well; that host is the one that can answer. Vulnerable: No microcodeis often not the administrator's fault. The vendor may never have shipped microcode for that CPU generation. Where nothing can be done about it, silence that one vulnerability with--ignoreinstead of switching the whole check off, so the rest keeps alerting.- The check is about posture, not about traffic. Its result changes on a reboot, a microcode update, a kernel update or a change to the kernel command line, and at no other time, which is why the Director template runs it once a day.
- Every vulnerability is always listed, with the kernel's own wording, below the summary line. How many there are is a property of the kernel, not of the host: a 7.1 kernel publishes nineteen entries, the 4.18 kernel of RHEL 8 fifteen. The check reads whatever is there, so a flaw that gets a name tomorrow is covered by a kernel update alone.
- Related checks:
about-mereports what the machine is,rpm-updatesanddeb-updateswhether the kernel and microcode packages are current, andneeds-restartingwhether a reboot is still pending after they were installed.
Data Collection:
- Reads every file below
/sys/devices/system/cpu/vulnerabilities - Reads the verdict from the beginning of each line and never from a substring
- Reads whatever code names the running kernel publishes instead of a built-in list
- Needs no root and no
sudo
Fact Sheet¶
| Fact | Value |
|---|---|
| Check Plugin Download | https://github.com/Linuxfabrik/monitoring-plugins/tree/main/check-plugins/cpu-vulnerabilities |
| Nagios/Icinga Check Name | check_cpu_vulnerabilities |
| Check Interval Recommendation | Every day |
| Can be called without parameters | Yes |
| Runs on | Linux |
| Compiled for Windows | No |
Help¶
usage: cpu-vulnerabilities [-h] [-V] [--always-ok] [--ignore IGNORE]
[--match MATCH]
[--no-match-severity {ok,warn,crit,unknown}]
[--no-perfdata] [--severity {ok,warn,crit,unknown}]
[--unknown-severity {ok,warn,crit,unknown}]
Reports which of the CPU vulnerabilities the kernel knows about are left
without a mitigation on this host. The kernel publishes its own verdict per
vulnerability, so the check reports what the running kernel, the microcode and
the boot parameters together actually achieve, and not what the CPU model
would allow. This works in a virtual machine as well, where the answer
additionally depends on what the hypervisor hands through. A vulnerability the
kernel cannot decide on is reported separately and does not alert by default,
because a guest regularly cannot see what its host does; raise
--unknown-severity to flag it where the answer is expected. Alerts when a
vulnerability the CPU is affected by has no mitigation in effect.
options:
-h, --help show this help message and exit
-V, --version show program's version number and exit
--always-ok Always returns OK.
--ignore IGNORE Any item matching this Python regex will be ignored.
Can be specified multiple times. Example:
`(?i)linuxfabrik` for a case-insensitive match.
--match MATCH Filter by this Python regular expression. Case-
sensitive by default; use `(?i)` for case-insensitive
matching. Can be specified multiple times. If both
`--match` and `--ignore` are given, an item must match
`--match` AND not match `--ignore` to be reported
(include first, exclude second). Examples:
`(?i)example` to match "example" regardless of case.
`^(?!.*example).*$` to match any string except
"example" (negative lookahead).
--no-match-severity {ok,warn,crit,unknown}
State to report when no item matches the filters and
nothing is checked. Default: ok
--no-perfdata Suppress the performance data section from the output.
The status message and the exit code are unaffected,
so alerting keeps working while trending data is
dropped.
--severity {ok,warn,crit,unknown}
Severity for alerting. Applies to a vulnerability the
CPU is affected by that has no mitigation in effect.
Default: warn
--unknown-severity {ok,warn,crit,unknown}
State to report for a vulnerability whose state cannot
be determined, and for a kernel that publishes no
vulnerability information at all. A guest sees only
what its hypervisor hands through and regularly cannot
decide, which is why this defaults to not alerting.
Default: ok
Documentation:
https://linuxfabrik.github.io/monitoring-plugins/check-plugins/cpu-vulnerabilities/
Usage Examples¶
./cpu-vulnerabilities
Output on a host where everything the CPU is affected by is held off:
No CPU vulnerability is left without a mitigation. 4 mitigated, 15 not affected.
Output on a virtual machine on an older Intel host, without a current microcode and with nx_huge_pages switched off on the hypervisor:
5 of 19 CPU vulnerabilities have no mitigation in effect: itlb_multihit, mds, mmio_stale_data, retbleed, spec_store_bypass.
2 CPU vulnerabilities the kernel could not decide on: gather_data_sampling, srbds.
Install the current microcode package, rebuild the initial ramdisk and reboot, run a current kernel, and check whether the kernel command line switches mitigations off (`mitigations=off`, `nopti`, `nospectre_v2` and the like).
On a virtual machine the guest sees only the CPU features and the microcode its hypervisor hands through, so run this check on the hypervisor as well.
The table below the summary lists every vulnerability with the kernel's own words, which is where the residual markers inside an applied mitigation become visible:
No CPU vulnerability is left without a mitigation. 4 mitigated, 15 not affected.
Vulnerability ! Kernel Report ! State
--------------------------+-----------------------------------------------------------------------------------------------------+-------------
gather_data_sampling ! Not affected ! not affected
ghostwrite ! Not affected ! not affected
indirect_target_selection ! Not affected ! not affected
itlb_multihit ! Not affected ! not affected
l1tf ! Not affected ! not affected
mds ! Not affected ! not affected
meltdown ! Not affected ! not affected
mmio_stale_data ! Not affected ! not affected
old_microcode ! Not affected ! not affected
reg_file_data_sampling ! Not affected ! not affected
retbleed ! Not affected ! not affected
spec_rstack_overflow ! Not affected ! not affected
spec_store_bypass ! Mitigation: Speculative Store Bypass disabled via prctl ! mitigated
spectre_v1 ! Mitigation: usercopy/swapgs barriers and __user pointer sanitization ! mitigated
spectre_v2 ! Mitigation: Enhanced / Automatic IBRS; IBPB: conditional; PBRSB-eIBRS: Not affected; BHI: BHI_DIS_S ! mitigated
srbds ! Not affected ! not affected
tsa ! Not affected ! not affected
tsx_async_abort ! Not affected ! not affected
vmscape ! Mitigation: IBPB before exit to userspace ! mitigated
Accept the one vulnerability nobody can do anything about on this hardware and keep alerting on the rest:
./cpu-vulnerabilities --ignore='^srbds$'
On a host where an unmitigated CPU is a reason to act at night:
./cpu-vulnerabilities --severity=crit
On a hypervisor, where a verdict the kernel cannot reach is worth looking into rather than accepting:
./cpu-vulnerabilities --unknown-severity=warn
States¶
- OK if every vulnerability the CPU is affected by has a mitigation in effect, and everything else is reported as not affected.
- WARN if at least one vulnerability the CPU is affected by has no mitigation in effect.
--severitylowers that tookor raises it tocritorunknown. - OK with an explanation if the kernel cannot decide on a vulnerability, or reports it in wording this check does not know.
--unknown-severityraises that towarn,critorunknown. - OK with an explanation if the kernel publishes no vulnerability information at all, which means it was built without
CONFIG_GENERIC_CPU_VULNERABILITIES.--unknown-severityapplies here as well. - OK if
--matchand--ignoreleave nothing to check.--no-match-severityraises that. - UNKNOWN if
--matchor--ignoreis not a valid Python regular expression. - UNKNOWN if the check does not run on Linux.
--always-oksuppresses all alerts and always returns OK.
Perfdata / Metrics¶
All values are counts of vulnerabilities after --match and --ignore have been applied, so silencing one with --ignore takes it out of the graph as well.
| Name | Type | Description |
|---|---|---|
| checked | Number | Vulnerabilities the kernel publishes and the filters kept. The sum of the four below. |
| mitigated | Number | The CPU is affected and something is holding the vulnerability off. |
| not_affected | Number | The CPU does not have the flaw. |
| unknown | Number | The kernel cannot decide, or words its verdict in a way this check does not know. |
| vulnerable | Number | The CPU is affected and nothing is holding the vulnerability off. This is the value the check alerts on. |
Troubleshooting¶
A vulnerability has no mitigation in effect¶
Work through the four things that decide the answer, in this order.
-
Install the current microcode and reboot. Most
Vulnerable: No microcode,Vulnerable: Safe RET, no microcodeandVulnerable: Clear CPU buffers attempted, no microcodelines disappear with it. The package differs per vendor and per distribution:bash dnf install microcode_ctl # Red Hat family, Intel dnf install linux-firmware # RHEL, Rocky and rebuilds, AMD dnf install amd-ucode-firmware # Fedora, AMD apt install intel-microcode # Debian family, Intel apt install amd64-microcode # Debian family, AMD rebootOn an AMD host in the Red Hat family, installing the package is not enough. The processor takes its microcode from the initial ramdisk before anything else runs, and dracut builds that with
early_microcode="yes"by default, copying/lib/firmware/amd-ucodeinto the image.microcode_ctlregenerates the ramdisk itself after an update, butlinux-firmware, which carries the AMD microcode there, does not. Regenerate it by hand, otherwise the reboot comes up with the old microcode and the check keeps reporting the same thing:bash dracut --force rebootAdd
--regenerate-allto cover every installed kernel rather than only the running one.On Debian both packages sit in the
non-free-firmwarecomponent, which is not enabled on a minimal installation. Whereaptclaims the package does not exist, add that component to/etc/apt/sources.list.d/debian.sourcesand runapt updatefirst. Both packages rebuild the initial ramdisk on their own. -
Run a current kernel. A mitigation that does not exist in the running kernel cannot be in effect, however new the microcode is.
bash uname --kernel-release -
Read the kernel command line. A single
mitigations=offswitches every mitigation off at once, and the per-vulnerability switches (nopti,nospectre_v1,nospectre_v2,nospec_store_bypass_disable,mds=off,tsx_async_abort=off,no_stf_barrierand their relatives) do it one at a time. They frequently arrive as a performance tweak somebody applied years ago and nobody removed.bash cat /proc/cmdlineRemove what you find and reboot:
bash grubby --update-kernel=ALL --remove-args="mitigations=off" # Red Hat family reboot -
On a virtual machine, repeat all of the above on the hypervisor. The guest gets the CPU features and the microcode the hypervisor hands it, so a guest cannot mitigate what its host does not expose. A CPU model pinned for live migration (
Nehalem,Westmere,Skylake-Clientand the like) hides the feature bits several mitigations need.
Where the answer is that this hardware will never get a mitigation, keep the rest of the check alive and silence the one entry:
./cpu-vulnerabilities --ignore='^srbds$'
Unknown: Dependent on hypervisor status¶
The CPU is affected and the mitigation lives outside this machine, so the guest kernel refuses to guess. srbds and gather_data_sampling report this on an affected Intel CPU under any hypervisor.
Run the same check on the hypervisor. That host has the microcode, sees the real CPU and gives a real verdict:
./cpu-vulnerabilities
The guest cannot be fixed from inside; a hypervisor that is clean makes the guest safe without changing what the guest reports.
This kernel publishes no CPU vulnerability information¶
The kernel was built without CONFIG_GENERIC_CPU_VULNERABILITIES, so it created no /sys/devices/system/cpu/vulnerabilities at all. Every distribution kernel on x86, arm64 and powerpc has it, so this points at a self-built or an embedded kernel. Read what the running kernel was built with:
grep CONFIG_GENERIC_CPU_VULNERABILITIES /boot/config-$(uname --kernel-release)
Where the file is not shipped, the same setting is readable through /proc/config.gz on a kernel built with CONFIG_IKCONFIG_PROC:
zgrep CONFIG_GENERIC_CPU_VULNERABILITIES /proc/config.gz
reported in wording this check does not know¶
The kernel wrote a verdict that starts with none of the four words the interface has used since it was introduced. No verdict is derived from it, because guessing one would be the wrong kind of answer for a security property, and the raw line is printed instead. Please open an issue with that line so the check learns the wording.
The output says mitigated although the line contains "Vulnerable"¶
Correct and not a defect. A mitigation line may end in ; SMT vulnerable, ; BHI: Vulnerable or ; PBRSB-eIBRS: Vulnerable, and each of those names a residual risk inside a mitigation that is in effect, not a missing mitigation. The kernel's verdict for the file is its first word, and that is what this check reports.
The most common of them, ; SMT vulnerable, says that the mitigation covers everything except what a sibling hyperthread can reach. Where that matters, the answer is to switch simultaneous multithreading off, which costs throughput on every workload that used those siblings:
cat /sys/devices/system/cpu/smt/control
echo off > /sys/devices/system/cpu/smt/control
The file answers on, off, forceoff, notsupported or notimplemented, and rejects a write in the last three states. Make the change survive a reboot with nosmt on the kernel command line.
Credits, License¶
- Authors: Linuxfabrik GmbH, Zurich
- License: The Unlicense, see LICENSE file.