enh: massive update for Spectre v2 (CVE-2017-5715)

The state of the mitigations for spectre v2 has been
updated several times in the kernel, this commit brings
up the script to par with the view of the most recent kernels.

When run on old kernels, that might deem the vulnerability as
mitigated, we'll reclassify it as vulnerable if applicable, unless
the `--sysfs-only` parameter is passed, which instructs the script
to blindly trust what the kernel has to say.

A full inventory of all mitigation strings found in mainline,
stable and redhat kernels since the first spectre v2 mitigation
has been added as a gigantic comment to help understanding the context.

Gory details below:

  1. New structured sub-mitigation parsing ("Mitigation 3")

  A new section parses the sysfs message into discrete variables for each sub-mitigation component:
  - v2_base_mode: The primary mitigation (eibrs, eibrs_lfence, eibrs_retpoline, ibrs, retpoline, lfence, none) - parsed from sysfs with hardware fallback
  - v2_stibp_status: STIBP state (always-on, forced, conditional, disabled, eibrs-implicit, etc.)
  - v2_ibpb_mode: IBPB mode (always-on, conditional, disabled)
  - v2_pbrsb_status: PBRSB-eIBRS mitigation (not-affected, sw-sequence, vulnerable)
  - v2_bhi_status: BHI mitigation (not-affected, bhi_dis_s, sw-loop, retpoline, vulnerable)
  - v2_vuln_module: Whether a non-retpoline kernel module is loaded
  - v2_is_autoibrs: Distinguishes AMD AutoIBRS from Intel eIBRS (they have different cross-thread properties)

  2. Rewritten verdict logic (Phase 4)

  The old flat if/elif chain is replaced with per-base-mode branches, each checking all relevant sub-mitigations:
  - LFENCE: Always VULN (reclassified in kernel v5.17)
  - eIBRS path: Checks BHI, PBRSB (VMM-only), AutoIBRS STIBP, vulnerable modules, paranoid mode (IBPB always-on + SMT off)
  - IBRS path: Checks IBPB, STIBP+SMT, RSB filling on Skylake+, BHI, paranoid mode
  - Retpoline path: Checks compiler, runtime enable, RSB filling, BHI+RRSBA, IBPB, vulnerable modules, paranoid mode

  3. Caveat accumulation pattern

  A _v2_add_caveat helper collects all gaps into a single string, producing verdicts like "eIBRS active but insufficient: BHI vulnerable; STIBP not active with SMT on AMD AutoIBRS" instead of the old single-issue messages.

  4. Other changes

  - check_has_vmm called early in Phase 2 (for PBRSB VMM-awareness)
  - explain_hypervisor variable removed - its advice is now folded into the per-path explain calls
  - Offline mode gains eIBRS/AutoIBRS detection via cap_ibrs_all/cap_autoibrs
  - smt_enabled variable added (via is_cpu_smt_enabled) for cross-thread checks
This commit is contained in:
Stéphane Lesimple
2026-03-31 22:14:35 +02:00
parent e09d0cf221
commit 536dfb8701

View File

@@ -12,7 +12,8 @@ check_CVE_2017_5715() {
# Sets: g_ibrs_can_tell, g_ibrs_supported, g_ibrs_enabled, g_ibrs_fw_enabled, # Sets: g_ibrs_can_tell, g_ibrs_supported, g_ibrs_enabled, g_ibrs_fw_enabled,
# g_ibpb_can_tell, g_ibpb_supported, g_ibpb_enabled, g_specex_knob_dir # g_ibpb_can_tell, g_ibpb_supported, g_ibpb_enabled, g_specex_knob_dir
check_CVE_2017_5715_linux() { check_CVE_2017_5715_linux() {
local status sys_interface_available msg dir bp_harden_can_tell bp_harden retpoline retpoline_compiler retpoline_compiler_reason retp_enabled rsb_filling explain_hypervisor local status sys_interface_available msg dir bp_harden_can_tell bp_harden retpoline retpoline_compiler retpoline_compiler_reason retp_enabled rsb_filling
local v2_base_mode v2_stibp_status v2_pbrsb_status v2_bhi_status v2_ibpb_mode v2_vuln_module v2_is_autoibrs smt_enabled
status=UNK status=UNK
sys_interface_available=0 sys_interface_available=0
msg='' msg=''
@@ -210,6 +211,16 @@ check_CVE_2017_5715_linux() {
# all messages start with either "Not affected", "Mitigation", or "Vulnerable" # all messages start with either "Not affected", "Mitigation", or "Vulnerable"
fi fi
if [ "$opt_sysfs_only" != 1 ]; then if [ "$opt_sysfs_only" != 1 ]; then
check_has_vmm
v2_base_mode=''
v2_stibp_status=''
v2_pbrsb_status=''
v2_bhi_status=''
v2_ibpb_mode=''
v2_vuln_module=''
v2_is_autoibrs=0
pr_info "* Mitigation 1" pr_info "* Mitigation 1"
g_ibrs_can_tell=0 g_ibrs_can_tell=0
@@ -588,6 +599,229 @@ check_CVE_2017_5715_linux() {
fi fi
fi fi
# Mitigation 3: derive structured mitigation variables for the verdict.
# These are set from sysfs fields (when available) with hardware fallbacks.
pr_info "* Mitigation 3 (sub-mitigations)"
# --- v2_base_mode: which base Spectre v2 mitigation is active ---
pr_info_nol " * Base Spectre v2 mitigation mode: "
if [ -n "$ret_sys_interface_check_fullmsg" ]; then
# Parse from sysfs (handle all mainline, stable, and RHEL variants)
case "$ret_sys_interface_check_fullmsg" in
*"Enhanced / Automatic IBRS + LFENCE"* | *"Enhanced IBRS + LFENCE"*) v2_base_mode=eibrs_lfence ;;
*"Enhanced / Automatic IBRS + Retpolines"* | *"Enhanced IBRS + Retpolines"*) v2_base_mode=eibrs_retpoline ;;
*"Enhanced / Automatic IBRS"* | *"Enhanced IBRS"*) v2_base_mode=eibrs ;;
*"Mitigation: IBRS (kernel and user space)"*) v2_base_mode=ibrs ;;
*"Mitigation: IBRS (kernel)"*) v2_base_mode=ibrs ;;
*"Mitigation: IBRS"*) v2_base_mode=ibrs ;;
*"Mitigation: Retpolines"* | *"Full generic retpoline"* | *"Full retpoline"* | *"Full AMD retpoline"*) v2_base_mode=retpoline ;;
*"Vulnerable: LFENCE"* | *"Mitigation: LFENCE"*) v2_base_mode=lfence ;;
*"Vulnerable"*) v2_base_mode=none ;;
*) v2_base_mode=unknown ;;
esac
fi
# Fallback to existing variables if sysfs didn't provide a base mode
if [ -z "$v2_base_mode" ] || [ "$v2_base_mode" = "unknown" ]; then
if [ "$g_ibrs_enabled" = 4 ]; then
v2_base_mode=eibrs
elif [ -n "$g_ibrs_enabled" ] && [ "$g_ibrs_enabled" -ge 1 ] 2>/dev/null; then
v2_base_mode=ibrs
elif [ "$retpoline" = 1 ] && [ "$retpoline_compiler" = 1 ]; then
v2_base_mode=retpoline
elif [ "$retpoline" = 1 ]; then
v2_base_mode=retpoline
fi
fi
case "$v2_base_mode" in
eibrs) pstatus green "Enhanced / Automatic IBRS" ;;
eibrs_lfence) pstatus green "Enhanced / Automatic IBRS + LFENCE" ;;
eibrs_retpoline) pstatus green "Enhanced / Automatic IBRS + Retpolines" ;;
ibrs) pstatus green "IBRS" ;;
retpoline) pstatus green "Retpolines" ;;
lfence) pstatus red "LFENCE (insufficient)" ;;
none) pstatus yellow "None" ;;
*) pstatus yellow UNKNOWN ;;
esac
# --- v2_is_autoibrs: AMD AutoIBRS vs Intel eIBRS ---
case "$v2_base_mode" in
eibrs | eibrs_lfence | eibrs_retpoline)
if [ "$cap_autoibrs" = 1 ] || { (is_amd || is_hygon) && [ "$cap_ibrs_all" != 1 ]; }; then
v2_is_autoibrs=1
fi
;;
esac
# --- v2_ibpb_mode ---
pr_info_nol " * IBPB mode: "
if [ -n "$ret_sys_interface_check_fullmsg" ]; then
case "$ret_sys_interface_check_fullmsg" in
*"IBPB: always-on"*) v2_ibpb_mode=always-on ;;
*"IBPB: conditional"*) v2_ibpb_mode=conditional ;;
*"IBPB: disabled"*) v2_ibpb_mode=disabled ;;
*", IBPB"* | *"; IBPB"*) v2_ibpb_mode=conditional ;;
*) v2_ibpb_mode=disabled ;;
esac
elif [ "$opt_live" = 1 ]; then
case "$g_ibpb_enabled" in
2) v2_ibpb_mode=always-on ;;
1) v2_ibpb_mode=conditional ;;
0) v2_ibpb_mode=disabled ;;
*) v2_ibpb_mode=unknown ;;
esac
else
v2_ibpb_mode=unknown
fi
case "$v2_ibpb_mode" in
always-on) pstatus green YES "always-on" ;;
conditional) pstatus green YES "conditional" ;;
disabled) pstatus yellow NO "disabled" ;;
*) pstatus yellow UNKNOWN ;;
esac
# --- v2_stibp_status ---
pr_info_nol " * STIBP status: "
if [ -n "$ret_sys_interface_check_fullmsg" ]; then
case "$ret_sys_interface_check_fullmsg" in
*"STIBP: always-on"*) v2_stibp_status=always-on ;;
*"STIBP: forced"*) v2_stibp_status=forced ;;
*"STIBP: conditional"*) v2_stibp_status=conditional ;;
*"STIBP: disabled"*) v2_stibp_status=disabled ;;
*", STIBP"* | *"; STIBP"*) v2_stibp_status=forced ;;
*)
# No STIBP field: Intel eIBRS suppresses it (implicit cross-thread protection)
case "$v2_base_mode" in
eibrs | eibrs_lfence | eibrs_retpoline)
if [ "$v2_is_autoibrs" != 1 ]; then
v2_stibp_status=eibrs-implicit
else
v2_stibp_status=unknown
fi
;;
*) v2_stibp_status=unknown ;;
esac
;;
esac
else
# No sysfs: use hardware capability + context to infer STIBP status
if [ "$smt_enabled" != 0 ]; then
# SMT disabled or unknown: STIBP is not needed
v2_stibp_status=not-needed
else
case "$v2_base_mode" in
eibrs | eibrs_lfence | eibrs_retpoline)
if [ "$v2_is_autoibrs" != 1 ]; then
# Intel eIBRS provides implicit cross-thread protection
v2_stibp_status=eibrs-implicit
elif [ -n "$cap_stibp" ]; then
# AMD AutoIBRS: CPU supports STIBP but can't confirm runtime state
v2_stibp_status=unknown
else
# No STIBP support on this CPU
v2_stibp_status=unavailable
fi
;;
*)
if [ -n "$cap_stibp" ]; then
# CPU supports STIBP but can't confirm runtime state without sysfs
v2_stibp_status=unknown
else
# CPU does not support STIBP at all
v2_stibp_status=unavailable
fi
;;
esac
fi
fi
case "$v2_stibp_status" in
always-on) pstatus green YES "always-on" ;;
forced) pstatus green YES "forced" ;;
conditional) pstatus green YES "conditional" ;;
eibrs-implicit) pstatus green YES "implicit via eIBRS" ;;
not-needed) pstatus green YES "not needed (SMT disabled)" ;;
unavailable) pstatus red NO "CPU does not support STIBP" ;;
disabled) pstatus yellow NO "disabled" ;;
*) pstatus yellow UNKNOWN ;;
esac
# --- v2_pbrsb_status (only relevant for eIBRS) ---
case "$v2_base_mode" in
eibrs | eibrs_lfence | eibrs_retpoline)
pr_info_nol " * PBRSB-eIBRS mitigation: "
if [ -n "$ret_sys_interface_check_fullmsg" ]; then
case "$ret_sys_interface_check_fullmsg" in
*"PBRSB-eIBRS: Not affected"*) v2_pbrsb_status=not-affected ;;
*"PBRSB-eIBRS: SW sequence"*) v2_pbrsb_status=sw-sequence ;;
*"PBRSB-eIBRS: Vulnerable"*) v2_pbrsb_status=vulnerable ;;
*) v2_pbrsb_status=unknown ;;
esac
elif [ "$opt_live" != 1 ] && [ -n "$g_kernel" ]; then
if grep -q 'PBRSB-eIBRS' "$g_kernel" 2>/dev/null; then
v2_pbrsb_status=sw-sequence
else
v2_pbrsb_status=unknown
fi
else
v2_pbrsb_status=unknown
fi
case "$v2_pbrsb_status" in
not-affected) pstatus green "Not affected" ;;
sw-sequence) pstatus green "SW sequence" ;;
vulnerable) pstatus red "Vulnerable" ;;
*) pstatus yellow UNKNOWN ;;
esac
;;
*) v2_pbrsb_status=n/a ;;
esac
# --- v2_bhi_status ---
pr_info_nol " * BHI mitigation: "
if [ -n "$ret_sys_interface_check_fullmsg" ]; then
case "$ret_sys_interface_check_fullmsg" in
*"BHI: Not affected"*) v2_bhi_status=not-affected ;;
*"BHI: BHI_DIS_S"*) v2_bhi_status=bhi_dis_s ;;
*"BHI: SW loop"*) v2_bhi_status=sw-loop ;;
*"BHI: Retpoline"*) v2_bhi_status=retpoline ;;
*"BHI: Vulnerable, KVM: SW loop"*) v2_bhi_status=vuln-kvm-loop ;;
*"BHI: Vulnerable"*) v2_bhi_status=vulnerable ;;
*) v2_bhi_status=unknown ;;
esac
elif [ "$opt_live" != 1 ] && [ -n "$opt_config" ] && [ -r "$opt_config" ]; then
if grep -q '^CONFIG_\(MITIGATION_\)\?SPECTRE_BHI' "$opt_config"; then
v2_bhi_status=sw-loop
else
v2_bhi_status=unknown
fi
else
v2_bhi_status=unknown
fi
case "$v2_bhi_status" in
not-affected) pstatus green "Not affected" ;;
bhi_dis_s) pstatus green "BHI_DIS_S (hardware)" ;;
sw-loop) pstatus green "SW loop" ;;
retpoline) pstatus green "Retpoline" ;;
vuln-kvm-loop) pstatus yellow "Vulnerable (KVM: SW loop)" ;;
vulnerable) pstatus red "Vulnerable" ;;
*) pstatus yellow UNKNOWN ;;
esac
# --- v2_vuln_module ---
if [ "$opt_live" = 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
pstatus red YES
else
v2_vuln_module=0
pstatus green NO
fi
fi
# --- SMT state (used in verdict) ---
is_cpu_smt_enabled
smt_enabled=$?
# smt_enabled: 0=enabled, 1=disabled, 2=unknown
elif [ "$sys_interface_available" = 0 ]; then elif [ "$sys_interface_available" = 0 ]; then
# we have no sysfs but were asked to use it only! # we have no sysfs but were asked to use it only!
msg="/sys vulnerability interface use forced, but it's not available!" msg="/sys vulnerability interface use forced, but it's not available!"
@@ -600,53 +834,251 @@ check_CVE_2017_5715_linux() {
elif [ -z "$msg" ]; then elif [ -z "$msg" ]; then
if [ "$opt_sysfs_only" != 1 ]; then if [ "$opt_sysfs_only" != 1 ]; then
# --- own logic using Phase 2 variables --- # --- own logic using Phase 2 variables ---
if [ "$retpoline" = 1 ] && [ "$retpoline_compiler" = 1 ] && [ "$retp_enabled" != 0 ] && [ -n "$g_ibpb_enabled" ] && [ "$g_ibpb_enabled" -ge 1 ] && (! is_vulnerable_to_empty_rsb || [ "$rsb_filling" = 1 ]); then # Helper: collect caveats for the verdict message
pvulnstatus "$cve" OK "Full retpoline + IBPB are mitigating the vulnerability" _v2_caveats=''
elif [ "$retpoline" = 1 ] && [ "$retpoline_compiler" = 1 ] && [ "$retp_enabled" != 0 ] && [ "$opt_paranoid" = 0 ] && (! is_vulnerable_to_empty_rsb || [ "$rsb_filling" = 1 ]); then _v2_add_caveat() { _v2_caveats="${_v2_caveats:+$_v2_caveats; }$1"; }
pvulnstatus "$cve" OK "Full retpoline is mitigating the vulnerability"
# ARM branch predictor hardening (unchanged)
if [ -n "$bp_harden" ]; then
pvulnstatus "$cve" OK "Branch predictor hardening mitigates the vulnerability"
elif [ -z "$bp_harden" ] && [ "$cpu_vendor" = ARM ]; then
pvulnstatus "$cve" VULN "Branch predictor hardening is needed to mitigate the vulnerability"
explain "Your kernel has not been compiled with the CONFIG_UNMAP_KERNEL_AT_EL0 option, recompile it with this option enabled."
# LFENCE-only is always VULN (reclassified in v5.17)
elif [ "$v2_base_mode" = "lfence" ]; then
pvulnstatus "$cve" VULN "LFENCE alone is not a sufficient Spectre v2 mitigation"
explain "LFENCE-based indirect branch mitigation was reclassified as vulnerable starting with Linux v5.17. Use retpoline (spectre_v2=retpoline) or IBRS-based mitigations (spectre_v2=eibrs or spectre_v2=ibrs) instead. If your CPU supports Enhanced IBRS, that is the preferred option."
# eIBRS paths (eibrs / eibrs_lfence / eibrs_retpoline)
elif [ "$v2_base_mode" = "eibrs" ] || [ "$v2_base_mode" = "eibrs_lfence" ] || [ "$v2_base_mode" = "eibrs_retpoline" ]; then
_v2_caveats=''
_v2_ok=1
# BHI check: eIBRS alone doesn't protect against BHI
if [ "$v2_bhi_status" = "vulnerable" ]; then
_v2_ok=0
_v2_add_caveat "BHI vulnerable"
elif [ "$v2_bhi_status" = "unknown" ] && is_intel && [ "$cap_rrsba" != 0 ]; then
_v2_add_caveat "BHI status unknown (kernel may lack BHI mitigation)"
fi
# PBRSB check (only matters for VMM hosts)
if [ "$v2_pbrsb_status" = "vulnerable" ]; then
if [ "$g_has_vmm" != 0 ] || [ "$opt_paranoid" = 1 ]; then
_v2_ok=0
_v2_add_caveat "PBRSB-eIBRS vulnerable"
fi
fi
# AutoIBRS: needs explicit STIBP (does NOT provide implicit cross-thread protection)
if [ "$v2_is_autoibrs" = 1 ] && [ "$smt_enabled" = 0 ]; then
if [ "$v2_stibp_status" = "disabled" ] || [ "$v2_stibp_status" = "unavailable" ]; then
_v2_ok=0
_v2_add_caveat "STIBP not active with SMT on AMD AutoIBRS"
fi
fi
# Vulnerable module check
if [ "$v2_vuln_module" = 1 ]; then
_v2_add_caveat "non-retpoline module loaded"
fi
# Paranoid mode
if [ "$opt_paranoid" = 1 ]; then
if [ "$v2_ibpb_mode" != "always-on" ]; then
_v2_ok=0
_v2_add_caveat "IBPB not always-on"
fi
if [ "$smt_enabled" = 0 ]; then
_v2_ok=0
_v2_add_caveat "SMT enabled"
fi
fi
# Build the base description
case "$v2_base_mode" in
eibrs) _v2_desc="Enhanced / Automatic IBRS" ;;
eibrs_lfence) _v2_desc="Enhanced / Automatic IBRS + LFENCE" ;;
eibrs_retpoline) _v2_desc="Enhanced / Automatic IBRS + Retpolines" ;;
esac
if [ "$_v2_ok" = 1 ]; then
if [ -n "$_v2_caveats" ]; then
pvulnstatus "$cve" OK "$_v2_desc mitigates the vulnerability ($_v2_caveats)"
else
pvulnstatus "$cve" OK "$_v2_desc mitigates the vulnerability"
fi
# eBPF caveat: we cannot detect unprivileged eBPF status
if [ "$v2_base_mode" = "eibrs" ] || [ "$v2_base_mode" = "eibrs_lfence" ]; then
pr_info " NOTE: eIBRS is considered vulnerable by the kernel when unprivileged eBPF is enabled."
pr_info " This script cannot detect unprivileged eBPF status. Check \`sysctl kernel.unprivileged_bpf_disabled\`."
fi
else
pvulnstatus "$cve" VULN "$_v2_desc active but insufficient: $_v2_caveats"
explain "Your system uses $_v2_desc but has gaps in sub-mitigations: $_v2_caveats. Update your kernel and microcode to the latest versions. If BHI is vulnerable, a kernel with CONFIG_MITIGATION_SPECTRE_BHI or BHI_DIS_S microcode support is needed. If PBRSB-eIBRS is vulnerable, update the kernel for RSB VM exit mitigation. If STIBP is disabled on AMD AutoIBRS with SMT, add \`spectre_v2_user=on\` or disable SMT with \`nosmt\`. In paranoid mode, disable SMT with \`nosmt\` and set \`spectre_v2_user=on\` for IBPB always-on."
fi
# Kernel IBRS path
elif [ "$v2_base_mode" = "ibrs" ]; then
_v2_caveats=''
_v2_ok=1
# IBRS needs IBPB for cross-process protection
if [ "$v2_ibpb_mode" = "disabled" ]; then
_v2_ok=0
_v2_add_caveat "IBPB disabled"
fi
# IBRS needs STIBP or SMT-off for cross-thread protection
if [ "$smt_enabled" = 0 ] && { [ "$v2_stibp_status" = "disabled" ] || [ "$v2_stibp_status" = "unavailable" ]; }; then
_v2_ok=0
_v2_add_caveat "STIBP not active with SMT enabled"
fi
# RSB filling on Skylake+
if is_vulnerable_to_empty_rsb && [ "$rsb_filling" != 1 ]; then
_v2_ok=0
_v2_add_caveat "RSB filling missing on Skylake+"
fi
# BHI check
if [ "$v2_bhi_status" = "vulnerable" ]; then
_v2_ok=0
_v2_add_caveat "BHI vulnerable"
fi
# Vulnerable module check
if [ "$v2_vuln_module" = 1 ]; then
_v2_add_caveat "non-retpoline module loaded"
fi
# Paranoid mode
if [ "$opt_paranoid" = 1 ]; then
if [ "$v2_ibpb_mode" != "always-on" ]; then
_v2_ok=0
_v2_add_caveat "IBPB not always-on"
fi
if [ "$smt_enabled" = 0 ]; then
_v2_ok=0
_v2_add_caveat "SMT enabled"
fi
fi
if [ "$_v2_ok" = 1 ]; then
pvulnstatus "$cve" OK "IBRS mitigates the vulnerability"
else
pvulnstatus "$cve" VULN "IBRS active but insufficient: $_v2_caveats"
explain "Your system uses kernel IBRS but has gaps: $_v2_caveats. Ensure IBPB is enabled (spectre_v2_user=on or spectre_v2_user=prctl,ibpb). If STIBP is disabled with SMT, add spectre_v2_user=on or disable SMT with \`nosmt\`. If RSB filling is missing, update the kernel. If BHI is vulnerable, update kernel/microcode for BHI mitigation."
fi
# Retpoline path
elif [ "$v2_base_mode" = "retpoline" ]; then
_v2_caveats=''
_v2_ok=1
# Retpoline compiler check
if [ "$retpoline_compiler" = 0 ]; then
_v2_ok=0
_v2_add_caveat "not compiled with retpoline-aware compiler"
fi
# Red Hat runtime disable check
if [ "$retp_enabled" = 0 ]; then
_v2_ok=0
_v2_add_caveat "retpoline disabled at runtime"
fi
# RSB filling on Skylake+ (empty RSB falls back to BTB)
if is_vulnerable_to_empty_rsb && [ "$rsb_filling" != 1 ]; then
_v2_ok=0
_v2_add_caveat "RSB filling missing on Skylake+"
fi
# BHI: retpoline only mitigates BHI if RRSBA is disabled
if [ "$v2_bhi_status" = "vulnerable" ]; then
_v2_ok=0
_v2_add_caveat "BHI vulnerable"
elif [ "$v2_bhi_status" = "unknown" ] && is_intel && [ "$cap_rrsba" = 1 ]; then
_v2_add_caveat "BHI status unknown with RRSBA"
fi
# Vulnerable module
if [ "$v2_vuln_module" = 1 ]; then
_v2_ok=0
_v2_add_caveat "non-retpoline module loaded"
fi
# IBPB check: retpoline without IBPB is weaker
if [ "$v2_ibpb_mode" = "disabled" ] || { [ -z "$g_ibpb_enabled" ] || [ "$g_ibpb_enabled" = 0 ]; }; then
if [ "$opt_paranoid" = 1 ]; then
_v2_ok=0
_v2_add_caveat "IBPB disabled"
else
_v2_add_caveat "IBPB disabled (recommended)"
fi
fi
# Paranoid mode: require SMT off, IBPB always-on
if [ "$opt_paranoid" = 1 ]; then
if [ "$v2_ibpb_mode" != "always-on" ] && [ "$v2_ibpb_mode" != "disabled" ]; then
_v2_ok=0
_v2_add_caveat "IBPB not always-on"
fi
if [ "$smt_enabled" = 0 ]; then
_v2_ok=0
_v2_add_caveat "SMT enabled"
fi
fi
if [ "$_v2_ok" = 1 ]; then
if [ -n "$_v2_caveats" ]; then
pvulnstatus "$cve" OK "Retpolines mitigate the vulnerability ($_v2_caveats)"
if echo "$_v2_caveats" | grep -q 'IBPB'; then
if [ -n "$cap_ibpb" ]; then if [ -n "$cap_ibpb" ]; then
pr_warn "You should enable IBPB to complete retpoline as a Variant 2 mitigation" pr_warn "You should enable IBPB to complete retpoline as a Variant 2 mitigation"
else else
pr_warn "IBPB is considered as a good addition to retpoline for Variant 2 mitigation, but your CPU microcode doesn't support it" pr_warn "IBPB is considered as a good addition to retpoline for Variant 2 mitigation, but your CPU microcode doesn't support it"
fi fi
elif [ -n "$g_ibrs_enabled" ] && [ -n "$g_ibpb_enabled" ] && [ "$g_ibrs_enabled" -ge 1 ] && [ "$g_ibpb_enabled" -ge 1 ]; then fi
else
pvulnstatus "$cve" OK "Retpolines + IBPB mitigate the vulnerability"
fi
else
pvulnstatus "$cve" VULN "Retpoline active but insufficient: $_v2_caveats"
explain "Your system uses retpoline but has gaps: $_v2_caveats. Ensure the kernel was compiled with a retpoline-aware compiler. Enable IBPB (spectre_v2_user=on). If RSB filling is missing on Skylake+, update the kernel. If BHI is vulnerable, update kernel/microcode. In paranoid mode, disable SMT with \`nosmt\` and set \`spectre_v2_user=on\`."
fi
# Legacy fallback: IBRS+IBPB from debugfs on old systems without sysfs
elif [ -n "$g_ibrs_enabled" ] && [ "$g_ibrs_enabled" -ge 1 ] 2>/dev/null && [ -n "$g_ibpb_enabled" ] && [ "$g_ibpb_enabled" -ge 1 ] 2>/dev/null; then
if [ "$g_ibrs_enabled" = 4 ]; then if [ "$g_ibrs_enabled" = 4 ]; then
pvulnstatus "$cve" OK "Enhanced IBRS + IBPB are mitigating the vulnerability" pvulnstatus "$cve" OK "Enhanced IBRS + IBPB are mitigating the vulnerability"
else else
pvulnstatus "$cve" OK "IBRS + IBPB are mitigating the vulnerability" pvulnstatus "$cve" OK "IBRS + IBPB are mitigating the vulnerability"
fi fi
elif [ "$g_ibpb_enabled" = 2 ] && ! is_cpu_smt_enabled; then elif [ "$g_ibpb_enabled" = 2 ] && [ "$smt_enabled" != 0 ]; then
pvulnstatus "$cve" OK "Full IBPB is mitigating the vulnerability" pvulnstatus "$cve" OK "Full IBPB is mitigating the vulnerability"
elif [ -n "$bp_harden" ]; then
pvulnstatus "$cve" OK "Branch predictor hardening mitigates the vulnerability" # Offline mode fallback
elif [ -z "$bp_harden" ] && [ "$cpu_vendor" = ARM ]; then
pvulnstatus "$cve" VULN "Branch predictor hardening is needed to mitigate the vulnerability"
explain "Your kernel has not been compiled with the CONFIG_UNMAP_KERNEL_AT_EL0 option, recompile it with this option enabled."
elif [ "$opt_live" != 1 ]; then elif [ "$opt_live" != 1 ]; then
if [ "$retpoline" = 1 ] && [ -n "$g_ibpb_supported" ]; then if [ "$retpoline" = 1 ] && [ -n "$g_ibpb_supported" ]; then
pvulnstatus "$cve" OK "offline mode: kernel supports retpoline + IBPB to mitigate the vulnerability" pvulnstatus "$cve" OK "offline mode: kernel supports retpoline + IBPB to mitigate the vulnerability"
elif [ -n "$g_ibrs_supported" ] && [ -n "$g_ibpb_supported" ]; then elif [ -n "$g_ibrs_supported" ] && [ -n "$g_ibpb_supported" ]; then
pvulnstatus "$cve" OK "offline mode: kernel supports IBRS + IBPB to mitigate the vulnerability" pvulnstatus "$cve" OK "offline mode: kernel supports IBRS + IBPB to mitigate the vulnerability"
elif [ "$cap_ibrs_all" = 1 ] || [ "$cap_autoibrs" = 1 ]; then
pvulnstatus "$cve" OK "offline mode: CPU supports Enhanced / Automatic IBRS"
elif [ "$g_ibrs_can_tell" != 1 ]; then elif [ "$g_ibrs_can_tell" != 1 ]; then
pvulnstatus "$cve" UNK "offline mode: not enough information" pvulnstatus "$cve" UNK "offline mode: not enough information"
explain "Re-run this script with root privileges, and give it the kernel image (--kernel), the kernel configuration (--config) and the System.map file (--map) corresponding to the kernel you would like to inspect." explain "Re-run this script with root privileges, and give it the kernel image (--kernel), the kernel configuration (--config) and the System.map file (--map) corresponding to the kernel you would like to inspect."
fi fi
fi fi
# if we arrive here and didn't already call pvulnstatus, then it's VULN, let's explain why # Catch-all: if no verdict was reached above, it's VULN
if [ "$g_pvulnstatus_last_cve" != "$cve" ]; then if [ "$g_pvulnstatus_last_cve" != "$cve" ]; then
# explain what's needed for this CPU if is_intel || is_amd || is_hygon; then
if is_vulnerable_to_empty_rsb; then pvulnstatus "$cve" VULN "Your CPU is affected and no sufficient mitigation was detected"
pvulnstatus "$cve" VULN "IBRS+IBPB or retpoline+IBPB+RSB filling, is needed to mitigate the vulnerability" explain "To mitigate this vulnerability, you need one of: (1) Enhanced IBRS / Automatic IBRS (eIBRS) -- requires CPU microcode support; preferred for modern CPUs. (2) Kernel IBRS (spectre_v2=ibrs) + IBPB -- requires IBRS-capable microcode. (3) Retpoline (spectre_v2=retpoline) + IBPB -- requires a retpoline-aware compiler and IBPB-capable microcode. For Skylake+ CPUs, options 1 or 2 are preferred as retpoline needs RSB filling. Update your kernel and CPU microcode to the latest versions."
explain "To mitigate this vulnerability, you need either IBRS + IBPB, both requiring hardware support from your CPU microcode in addition to kernel support, or a kernel compiled with retpoline and IBPB, with retpoline requiring a retpoline-aware compiler (re-run this script with -v to know if your version of gcc is retpoline-aware) and IBPB requiring hardware support from your CPU microcode. You also need a recent-enough kernel that supports RSB filling if you plan to use retpoline. For Skylake+ CPUs, the IBRS + IBPB approach is generally preferred as it guarantees complete protection, and the performance impact is not as high as with older CPUs in comparison with retpoline. More information about how to enable the missing bits for those two possible mitigations on your system follow. You only need to take one of the two approaches."
elif is_zen_cpu || is_moksha_cpu; then
pvulnstatus "$cve" VULN "retpoline+IBPB is needed to mitigate the vulnerability"
explain "To mitigate this vulnerability, You need a kernel compiled with retpoline + IBPB support, with retpoline requiring a retpoline-aware compiler (re-run this script with -v to know if your version of gcc is retpoline-aware) and IBPB requiring hardware support from your CPU microcode."
elif is_intel || is_amd || is_hygon; then
pvulnstatus "$cve" VULN "IBRS+IBPB or retpoline+IBPB is needed to mitigate the vulnerability"
explain "To mitigate this vulnerability, you need either IBRS + IBPB, both requiring hardware support from your CPU microcode in addition to kernel support, or a kernel compiled with retpoline and IBPB, with retpoline requiring a retpoline-aware compiler (re-run this script with -v to know if your version of gcc is retpoline-aware) and IBPB requiring hardware support from your CPU microcode. The retpoline + IBPB approach is generally preferred as the performance impact is lower. More information about how to enable the missing bits for those two possible mitigations on your system follow. You only need to take one of the two approaches."
else else
# in that case, we might want to trust sysfs if it's there
if [ "$sys_interface_available" = 1 ]; then if [ "$sys_interface_available" = 1 ]; then
pvulnstatus "$cve" "$status" "$ret_sys_interface_check_fullmsg" pvulnstatus "$cve" "$status" "$ret_sys_interface_check_fullmsg"
else else
@@ -654,66 +1086,6 @@ check_CVE_2017_5715_linux() {
fi fi
fi fi
fi fi
# if we are in live mode, we can check for a lot more stuff and explain further
if [ "$opt_live" = 1 ] && [ "$vulnstatus" != "OK" ]; then
explain_hypervisor="An updated CPU microcode will have IBRS/IBPB capabilities indicated in the Hardware Check section above. If you're running under a hypervisor (KVM, Xen, VirtualBox, VMware, ...), the hypervisor needs to be up to date to be able to export the new host CPU flags to the guest. You can run this script on the host to check if the host CPU is IBRS/IBPB. If it is, and it doesn't show up in the guest, upgrade the hypervisor. You may need to reconfigure your VM to use a CPU model that has IBRS capability; in Libvirt, such CPUs are listed with an IBRS suffix."
# IBPB (amd & intel)
if { [ -z "$g_ibpb_enabled" ] || [ "$g_ibpb_enabled" = 0 ]; } && { is_intel || is_amd || is_hygon; }; then
if [ -z "$cap_ibpb" ]; then
explain "The microcode of your CPU needs to be upgraded to be able to use IBPB. This is usually done at boot time by your kernel (the upgrade is not persistent across reboots which is why it's done at each boot). If you're using a distro, make sure you are up to date, as microcode updates are usually shipped alongside with the distro kernel. Availability of a microcode update for you CPU model depends on your CPU vendor. You can usually find out online if a microcode update is available for your CPU by searching for your CPUID (indicated in the Hardware Check section). $explain_hypervisor"
fi
if [ -z "$g_ibpb_supported" ]; then
explain "Your kernel doesn't have IBPB support, so you need to either upgrade your kernel (if you're using a distro) or recompiling a more recent kernel."
fi
if [ -n "$cap_ibpb" ] && [ -n "$g_ibpb_supported" ]; then
if [ -e "$g_specex_knob_dir/g_ibpb_enabled" ]; then
# newer (April 2018) Red Hat kernels have g_ibpb_enabled as ro, and automatically enables it with retpoline
if [ ! -w "$g_specex_knob_dir/g_ibpb_enabled" ] && [ -e "$g_specex_knob_dir/retp_enabled" ]; then
explain "Both your CPU and your kernel have IBPB support, but it is currently disabled. You kernel should enable IBPB automatically if you enable retpoline. You may enable it with \`echo 1 > $g_specex_knob_dir/retp_enabled\`."
else
explain "Both your CPU and your kernel have IBPB support, but it is currently disabled. You may enable it with \`echo 1 > $g_specex_knob_dir/g_ibpb_enabled\`."
fi
else
explain "Both your CPU and your kernel have IBPB support, but it is currently disabled. You may enable it. Check in your distro's documentation on how to do this."
fi
fi
elif [ "$g_ibpb_enabled" = 2 ] && is_cpu_smt_enabled; then
explain "You have g_ibpb_enabled set to 2, but it only offers sufficient protection when simultaneous multi-threading (aka SMT or HyperThreading) is disabled. You should reboot your system with the kernel parameter \`nosmt\`."
fi
# /IBPB
# IBRS (amd & intel)
if { [ -z "$g_ibrs_enabled" ] || [ "$g_ibrs_enabled" = 0 ]; } && { is_intel || is_amd || is_hygon; }; then
if [ -z "$cap_ibrs" ]; then
explain "The microcode of your CPU needs to be upgraded to be able to use IBRS. This is usually done at boot time by your kernel (the upgrade is not persistent across reboots which is why it's done at each boot). If you're using a distro, make sure you are up to date, as microcode updates are usually shipped alongside with the distro kernel. Availability of a microcode update for you CPU model depends on your CPU vendor. You can usually find out online if a microcode update is available for your CPU by searching for your CPUID (indicated in the Hardware Check section). $explain_hypervisor"
fi
if [ -z "$g_ibrs_supported" ]; then
explain "Your kernel doesn't have IBRS support, so you need to either upgrade your kernel (if you're using a distro) or recompiling a more recent kernel."
fi
if [ -n "$cap_ibrs" ] && [ -n "$g_ibrs_supported" ]; then
if [ -e "$g_specex_knob_dir/g_ibrs_enabled" ]; then
explain "Both your CPU and your kernel have IBRS support, but it is currently disabled. You may enable it with \`echo 1 > $g_specex_knob_dir/g_ibrs_enabled\`."
else
explain "Both your CPU and your kernel have IBRS support, but it is currently disabled. You may enable it. Check in your distro's documentation on how to do this."
fi
fi
fi
# /IBRS
unset explain_hypervisor
# RETPOLINE (amd & intel &hygon )
if is_amd || is_intel || is_hygon; then
if [ "$retpoline" = 0 ]; then
explain "Your kernel is not compiled with retpoline support, so you need to either upgrade your kernel (if you're using a distro) or recompile your kernel with the CONFIG_MITIGATION_RETPOLINE option enabled (was named CONFIG_RETPOLINE before kernel 6.9-rc1). You also need to compile your kernel with a retpoline-aware compiler (re-run this script with -v to know if your version of gcc is retpoline-aware)."
elif [ "$retpoline" = 1 ] && [ "$retpoline_compiler" = 0 ]; then
explain "Your kernel is compiled with retpoline, but without a retpoline-aware compiler (re-run this script with -v to know if your version of gcc is retpoline-aware)."
elif [ "$retpoline" = 1 ] && [ "$retpoline_compiler" = 1 ] && [ "$retp_enabled" = 0 ]; then
explain "Your kernel has retpoline support and has been compiled with a retpoline-aware compiler, but retpoline is disabled. You should enable it with \`echo 1 > $g_specex_knob_dir/retp_enabled\`."
fi
fi
# /RETPOLINE
fi
else else
# --sysfs-only: Phase 2 variables are unset, fall back to the # --sysfs-only: Phase 2 variables are unset, fall back to the
# raw sysfs result (status + fullmsg were set in Phase 1). # raw sysfs result (status + fullmsg were set in Phase 1).