# vim: set ts=4 sw=4 sts=4 et:
###############################
# CVE-2017-5715, Spectre V2, Branch Target Injection
# Sets: vulnstatus
check_CVE_2017_5715() {
check_cve 'CVE-2017-5715'
}
# 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
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
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
sys_interface_available=0
msg=''
if sys_interface_check "$VULN_SYSFS_BASE/spectre_v2"; then
sys_interface_available=1
status=$ret_sys_interface_check_status
#
# Complete sysfs message inventory for spectre_v2, traced via git blame
# on mainline (~/linux) and stable (~/linux-stable):
#
# all versions:
# "Not affected" (cpu_show_common, 61dc0f555b5c)
# "Vulnerable" (cpu_show_common fallthrough / spectre_v2_strings[NONE], 61dc0f555b5c)
#
# The output is a composite string: [][][][][][][]
# where comes from spectre_v2_strings[] (or an early-return override),
# and the remaining fields are appended by helper functions.
# Before v6.9-rc4 (0cd01ac5dcb1), fields were separated by ", ".
# From v6.9-rc4 onward, fields are separated by "; ".
#
# --- base string (spectre_v2_strings[]) ---
#
# da285121560e (v4.15, initial spectre_v2 sysfs):
# "Vulnerable"
# "Mitigation: None" (documented in spectre.rst 5ad3eb113245,
# not found in code but listed as a possible value; treat as vulnerable / sysfs override)
# "Vulnerable: Minimal generic ASM retpoline"
# "Vulnerable: Minimal AMD ASM retpoline"
# "Mitigation: Full generic retpoline"
# "Mitigation: Full AMD retpoline"
# 706d51681d63 (v4.19, added Enhanced IBRS):
# + "Mitigation: Enhanced IBRS"
# ef014aae8f1c (v4.20-rc5, removed minimal retpoline):
# - "Vulnerable: Minimal generic ASM retpoline"
# - "Vulnerable: Minimal AMD ASM retpoline"
# d45476d98324 (v5.17-rc8, renamed retpoline variants):
# "Mitigation: Full generic retpoline" -> "Mitigation: Retpolines"
# "Mitigation: Full AMD retpoline" -> "Mitigation: LFENCE" (in array)
# NOTE: "Mitigation: LFENCE" was the actual sysfs output for a brief window
# (between d45476d98324 and eafd987d4a82, both in v5.17-rc8) before the
# show_state override reclassified it as "Vulnerable: LFENCE". LFENCE alone
# is now considered an insufficient mitigation for Spectre v2. Any kernel
# reporting "Mitigation: LFENCE" is from this narrow window and should be
# treated as vulnerable.
# 1e19da8522c8 (v5.17-rc8, split Enhanced IBRS into 3 modes):
# "Mitigation: Enhanced IBRS" -> "Mitigation: Enhanced IBRS" (EIBRS alone)
# + "Mitigation: Enhanced IBRS + LFENCE"
# + "Mitigation: Enhanced IBRS + Retpolines"
# 7c693f54c873 (v5.19-rc7, added kernel IBRS):
# + "Mitigation: IBRS"
# e7862eda309e (v6.3-rc1, AMD Automatic IBRS):
# "Mitigation: Enhanced IBRS" -> "Mitigation: Enhanced / Automatic IBRS"
# "Mitigation: Enhanced IBRS + LFENCE" -> "Mitigation: Enhanced / Automatic IBRS + LFENCE"
# "Mitigation: Enhanced IBRS + Retpolines" -> "Mitigation: Enhanced / Automatic IBRS + Retpolines"
# d1cc1baef67a (v6.18-rc1, fixed LFENCE in array):
# "Mitigation: LFENCE" -> "Vulnerable: LFENCE" (in array, matching the
# show_state override that had been correcting the sysfs output since v5.17)
#
# --- early-return overrides in spectre_v2_show_state() ---
#
# These bypass the base string + suffix format entirely.
#
# eafd987d4a82 (v5.17-rc8, LFENCE override):
# "Vulnerable: LFENCE"
# (overrode the array's "Mitigation: LFENCE"; removed in v6.18-rc1 when the array
# was fixed to say "Vulnerable: LFENCE" directly)
# 44a3918c8245 (v5.17-rc8, created spectre_v2_show_state, eIBRS+eBPF):
# "Vulnerable: Unprivileged eBPF enabled"
# (immediately renamed below)
# 0de05d056afd (v5.17-rc8, renamed + added eIBRS+LFENCE case):
# "Vulnerable: Unprivileged eBPF enabled" -> "Vulnerable: eIBRS with unprivileged eBPF"
# + "Vulnerable: eIBRS+LFENCE with unprivileged eBPF and SMT"
#
# --- suffix (ibpb_state()) ---
#
# 20ffa1caecca (v4.16-rc1, initial IBPB):
# ", IBPB" (present/absent)
# a8f76ae41cd6 (v4.20-rc5, extracted ibpb_state()):
# ", IBPB: disabled" | ", IBPB: always-on"
# 7cc765a67d8e (v4.20-rc5, conditional IBPB):
# + ", IBPB: conditional"
# 0cd01ac5dcb1 (v6.9-rc4, separator change):
# ", IBPB: ..." -> "; IBPB: ..."
#
# --- suffix ---
#
# dd84441a7971 (v4.16-rc4):
# ", IBRS_FW" (present/absent)
# 0cd01ac5dcb1 (v6.9-rc4, separator change):
# ", IBRS_FW" -> "; IBRS_FW"
#
# --- suffix (stibp_state()) ---
#
# 53c613fe6349 (v4.20-rc1, initial STIBP):
# ", STIBP" (present/absent)
# a8f76ae41cd6 (v4.20-rc5, extracted stibp_state()):
# ", STIBP: disabled" | ", STIBP: forced"
# 9137bb27e60e (v4.20-rc5, added prctl):
# + ", STIBP: conditional" (when prctl/seccomp + switch_to_cond_stibp)
# 20c3a2c33e9f (v5.0-rc1, added always-on preferred):
# + ", STIBP: always-on"
# 34bce7c9690b (v4.20-rc5, eIBRS suppresses STIBP):
# returns "" when eIBRS is in use
# fd470a8beed8 (v6.5-rc4, AutoIBRS exception):
# returns "" only when eIBRS AND not AutoIBRS (AutoIBRS shows STIBP)
# 0cd01ac5dcb1 (v6.9-rc4, separator change):
# ", STIBP: ..." -> "; STIBP: ..."
#
# --- suffix ---
#
# bb4b3b776273 (v4.20-rc1):
# ", RSB filling" (present/absent)
# 53c613fe6349 (v4.20-rc1, temporarily removed, re-added in a8f76ae41cd6)
# 0cd01ac5dcb1 (v6.9-rc4, separator change):
# ", RSB filling" -> "; RSB filling"
#
# --- suffix (pbrsb_eibrs_state()) ---
#
# 2b1299322016 (v6.0-rc1):
# ", PBRSB-eIBRS: Not affected" | ", PBRSB-eIBRS: SW sequence" | ", PBRSB-eIBRS: Vulnerable"
# 0cd01ac5dcb1 (v6.9-rc4, separator change):
# ", PBRSB-eIBRS: ..." -> "; PBRSB-eIBRS: ..."
#
# --- suffix (spectre_bhi_state()) ---
#
# ec9404e40e8f (v6.9-rc4):
# "; BHI: Not affected" | "; BHI: BHI_DIS_S" | "; BHI: SW loop, KVM: SW loop"
# | "; BHI: Retpoline" | "; BHI: Vulnerable"
# 95a6ccbdc719 (v6.9-rc4, KVM default):
# + "; BHI: Vulnerable, KVM: SW loop"
# 5f882f3b0a8b (v6.9-rc4, clarified syscall hardening):
# removed "; BHI: Vulnerable (Syscall hardening enabled)"
# removed "; BHI: Syscall hardening, KVM: SW loop"
# (both replaced by "; BHI: Vulnerable" / "; BHI: Vulnerable, KVM: SW loop")
#
# --- suffix (spectre_v2_module_string()) ---
#
# caf7501a1b4e (v4.16-rc1):
# " - vulnerable module loaded" (present/absent)
#
# --- stable backports ---
#
# 3.2.y: old-style base strings ("Full generic/AMD retpoline", "Minimal generic/AMD
# ASM retpoline"). Suffixes: ", IBPB" only (no STIBP/IBRS_FW/RSB). Format: %s%s\n.
# Has "Mitigation: Enhanced IBRS" (no "/ Automatic") in later releases.
# 3.16.y, 4.4.y: old-style base strings. ibpb_state()/stibp_state()/IBRS_FW/RSB filling.
# 3.16.y lacks STIBP "always-on". Comma separators.
# Both have "Mitigation: Enhanced IBRS" (no "/ Automatic"). No LFENCE/EIBRS modes.
# 4.9.y: has spectre_v2_show_state() with LFENCE override ("Vulnerable: LFENCE"),
# eIBRS+eBPF overrides. "Mitigation: Enhanced IBRS" (no "/ Automatic").
# No SPECTRE_V2_IBRS. No pbrsb or BHI. Comma separators.
# 4.14.y: like 4.9.y but also has SPECTRE_V2_IBRS and pbrsb_eibrs_state().
# "Mitigation: Enhanced IBRS" (no "/ Automatic"). No BHI. Comma separators.
# 4.19.y: like 4.14.y but has "Enhanced / Automatic IBRS". No BHI. Comma separators.
# 5.4.y, 5.10.y: like 4.19.y. No BHI. Comma separators.
# 5.15.y, 6.1.y, 6.6.y, 6.12.y: match mainline (semicolons, BHI, all fields).
#
# --- Red Hat / CentOS / Rocky kernels ---
#
# Red Hat kernels carry their own spectre_v2 mitigation implementation that differs
# significantly from mainline. The following strings are unique to Red Hat kernels:
#
# centos6 (RHEL 6, kernel 2.6.32): base strings are a superset of mainline v4.15:
# "Vulnerable: Minimal ASM retpoline" (no "generic" qualifier)
# "Vulnerable: Minimal AMD ASM retpoline"
# "Vulnerable: Retpoline without IBPB"
# "Vulnerable: Retpoline on Skylake+" (removed in later centos6 releases)
# "Vulnerable: Retpoline with unsafe module(s)"
# "Mitigation: Full AMD retpoline"
# "Mitigation: Full retpoline" (no "generic" qualifier)
# "Mitigation: Full retpoline and IBRS (user space)"
# "Mitigation: IBRS (kernel)"
# "Mitigation: IBRS (kernel and user space)"
# "Mitigation: IBP disabled"
# Suffixes: ", IBPB" only. Format: %s%s\n.
#
# centos7 (RHEL 7, kernel 3.10): early releases have all centos6 strings plus
# "Vulnerable: Retpoline on Skylake+". Later releases removed Skylake+ and
# Minimal AMD, and changed AMD retpoline to:
# "Vulnerable: AMD retpoline (LFENCE/JMP)"
# Added "Mitigation: Enhanced IBRS". Suffixes: ibpb_state() + stibp_state()
# with simple ", IBPB" / ", STIBP" strings. No IBRS_FW/RSB/pbrsb/BHI.
#
# centos8 (RHEL 8, kernel 4.18): uses mainline v5.17+ style enum names
# (RETPOLINE/LFENCE/EIBRS) but retains RHEL-specific entries:
# "Mitigation: IBRS (kernel)" (SPECTRE_V2_IBRS)
# "Mitigation: Full retpoline and IBRS (user space)" (SPECTRE_V2_RETPOLINE_IBRS_USER)
# "Mitigation: IBRS (kernel and user space)" (SPECTRE_V2_IBRS_ALWAYS)
# "Mitigation: Enhanced IBRS" (no "/ Automatic"). spectre_v2_show_state() with
# LFENCE/eIBRS+eBPF overrides. Comma separators. No pbrsb/BHI.
#
# rocky9 (RHEL 9, kernel 5.14): matches mainline. Semicolons, BHI, all fields.
# rocky10 (RHEL 10, kernel 6.12): matches mainline.
#
#
# --- Kconfig symbols ---
# 76b043848fd2 (v4.15-rc8): CONFIG_RETPOLINE
# f43b9876e857 (v5.19-rc7): CONFIG_CPU_IBRS_ENTRY (kernel IBRS on entry)
# aefb2f2e619b (v6.9-rc1): renamed CONFIG_RETPOLINE => CONFIG_MITIGATION_RETPOLINE
# 1da8d2172ce5 (v6.9-rc1): renamed CONFIG_CPU_IBRS_ENTRY => CONFIG_MITIGATION_IBRS_ENTRY
# ec9404e40e8f (v6.9-rc4): CONFIG_SPECTRE_BHI_ON / CONFIG_SPECTRE_BHI_OFF
# 4f511739c54b (v6.9-rc4): replaced by CONFIG_MITIGATION_SPECTRE_BHI
# 72c70f480a70 (v6.12-rc1): CONFIG_MITIGATION_SPECTRE_V2 (top-level on/off)
# 8754e67ad4ac (v6.15-rc7): CONFIG_MITIGATION_ITS (indirect target selection)
# stable 5.4.y-6.6.y: CONFIG_RETPOLINE (pre-rename)
# stable 6.12.y: CONFIG_MITIGATION_RETPOLINE, CONFIG_MITIGATION_SPECTRE_V2
#
# --- kernel functions (for $opt_map / System.map) ---
# da285121560e (v4.15-rc8): spectre_v2_select_mitigation(),
# spectre_v2_parse_cmdline(), nospectre_v2_parse_cmdline()
# 20ffa1caecca (v4.16-rc1): spectre_v2_module_string(), retpoline_module_ok()
# a8f76ae41cd6 (v4.20-rc5): spectre_v2_user_select_mitigation(),
# spectre_v2_user_parse_cmdline()
# 7c693f54c873 (v5.19-rc7): spectre_v2_in_ibrs_mode(), spectre_v2_in_eibrs_mode()
# 44a3918c8245 (v5.17-rc8): spectre_v2_show_state()
# 480e803dacf8 (v6.16-rc1): split into spectre_v2_select_mitigation() +
# spectre_v2_apply_mitigation() + spectre_v2_update_mitigation() +
# spectre_v2_user_apply_mitigation() + spectre_v2_user_update_mitigation()
#
# --- CPU affection logic (for is_cpu_affected) ---
# X86_BUG_SPECTRE_V2 is set for ALL x86 CPUs except:
# - CPUs matching NO_SPECULATION: family 4 (all vendors), Centaur/Intel/NSC/Vortex
# family 5, Intel Atom Bonnell/Saltwell
# - CPUs matching NO_SPECTRE_V2: Centaur family 7, Zhaoxin family 7
# 99c6fa2511d8 (v4.15-rc8): unconditional for all x86 CPUs
# 1e41a766c98b (v5.6-rc1): added NO_SPECTRE_V2 exemption for Centaur/Zhaoxin
# 98c7a713db91 (v6.15-rc1): added X86_BUG_SPECTRE_V2_USER as separate bit
# No MSR/CPUID immunity bits — purely whitelist-based.
# vendor scope: all x86 vendors affected (Intel, AMD, Hygon, etc.)
# except Centaur family 7 and Zhaoxin family 7.
#
# all messages start with either "Not affected", "Mitigation", or "Vulnerable"
fi
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"
g_ibrs_can_tell=0
g_ibrs_supported=''
g_ibrs_enabled=''
g_ibpb_can_tell=0
g_ibpb_supported=''
g_ibpb_enabled=''
if [ "$opt_live" = 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
mount_debugfs
for dir in \
$DEBUGFS_BASE \
$DEBUGFS_BASE/x86 \
"$g_procfs/sys/kernel"; do
if [ -e "$dir/ibrs_enabled" ]; then
# if the file is there, we have IBRS compiled-in
# $DEBUGFS_BASE/ibrs_enabled: vanilla
# $DEBUGFS_BASE/x86/ibrs_enabled: Red Hat (see https://access.redhat.com/articles/3311301)
# /proc/sys/kernel/ibrs_enabled: OpenSUSE tumbleweed
g_specex_knob_dir=$dir
g_ibrs_supported="$dir/ibrs_enabled exists"
g_ibrs_enabled=$(cat "$dir/ibrs_enabled" 2>/dev/null)
pr_debug "ibrs: found $dir/ibrs_enabled=$g_ibrs_enabled"
# if ibrs_enabled is there, ibpb_enabled will be in the same dir
if [ -e "$dir/ibpb_enabled" ]; then
# if the file is there, we have IBPB compiled-in (see note above for IBRS)
g_ibpb_supported="$dir/ibpb_enabled exists"
g_ibpb_enabled=$(cat "$dir/ibpb_enabled" 2>/dev/null)
pr_debug "ibpb: found $dir/ibpb_enabled=$g_ibpb_enabled"
else
pr_debug "ibpb: $dir/ibpb_enabled file doesn't exist"
fi
break
else
pr_debug "ibrs: $dir/ibrs_enabled file doesn't exist"
fi
done
# on some newer kernels, the spec_ctrl_ibrs flag in "$g_procfs/cpuinfo"
# is set when ibrs has been administratively enabled (usually from cmdline)
# which in that case means ibrs is supported *and* enabled for kernel & user
# as per the ibrs patch series v3
if [ -z "$g_ibrs_supported" ]; then
if grep ^flags "$g_procfs/cpuinfo" | grep -qw spec_ctrl_ibrs; then
pr_debug "ibrs: found spec_ctrl_ibrs flag in $g_procfs/cpuinfo"
g_ibrs_supported="spec_ctrl_ibrs flag in $g_procfs/cpuinfo"
# enabled=2 -> kernel & user
g_ibrs_enabled=2
# XXX and what about ibpb ?
fi
fi
if [ -n "$ret_sys_interface_check_fullmsg" ]; then
# when IBPB is enabled on 4.15+, we can see it in sysfs
if echo "$ret_sys_interface_check_fullmsg" | grep -q 'IBPB'; then
pr_debug "ibpb: found enabled in sysfs"
[ -z "$g_ibpb_supported" ] && g_ibpb_supported='IBPB found enabled in sysfs'
[ -z "$g_ibpb_enabled" ] && g_ibpb_enabled=1
fi
# when IBRS_FW is enabled on 4.15+, we can see it in sysfs
if echo "$ret_sys_interface_check_fullmsg" | grep -q '[,;] IBRS_FW'; then
pr_debug "ibrs: found IBRS_FW in sysfs"
[ -z "$g_ibrs_supported" ] && g_ibrs_supported='found IBRS_FW in sysfs'
g_ibrs_fw_enabled=1
fi
# when IBRS is enabled on 4.15+, we can see it in sysfs
# on a more recent kernel, classic "IBRS" is not even longer an option, because of the performance impact.
# only "Enhanced IBRS" is available (on CPUs with the IBRS_ALL flag)
if echo "$ret_sys_interface_check_fullmsg" | grep -q -e '\' -e 'Indirect Branch Restricted Speculation'; then
pr_debug "ibrs: found IBRS in sysfs"
[ -z "$g_ibrs_supported" ] && g_ibrs_supported='found IBRS in sysfs'
[ -z "$g_ibrs_enabled" ] && g_ibrs_enabled=3
fi
# checking for 'Enhanced IBRS' in sysfs, enabled on CPUs with IBRS_ALL
if echo "$ret_sys_interface_check_fullmsg" | grep -q -e 'Enhanced IBRS'; then
[ -z "$g_ibrs_supported" ] && g_ibrs_supported='found Enhanced IBRS in sysfs'
# 4 isn't actually a valid value of the now extinct "g_ibrs_enabled" flag file,
# that only went from 0 to 3, so we use 4 as "enhanced ibrs is enabled"
g_ibrs_enabled=4
fi
fi
# in live mode, if ibrs or ibpb is supported and we didn't find these are enabled, then they are not
[ -n "$g_ibrs_supported" ] && [ -z "$g_ibrs_enabled" ] && g_ibrs_enabled=0
[ -n "$g_ibpb_supported" ] && [ -z "$g_ibpb_enabled" ] && g_ibpb_enabled=0
fi
if [ -z "$g_ibrs_supported" ]; then
check_redhat_canonical_spectre
if [ "$g_redhat_canonical_spectre" = 1 ]; then
g_ibrs_supported="Red Hat/Ubuntu variant"
g_ibpb_supported="Red Hat/Ubuntu variant"
fi
fi
if [ -z "$g_ibrs_supported" ] && [ -n "$g_kernel" ]; then
if ! command -v "${opt_arch_prefix}strings" >/dev/null 2>&1; then
:
else
g_ibrs_can_tell=1
g_ibrs_supported=$("${opt_arch_prefix}strings" "$g_kernel" | grep -Fw -e '[,;] IBRS_FW' | head -n1)
if [ -n "$g_ibrs_supported" ]; then
pr_debug "ibrs: found ibrs evidence in kernel image ($g_ibrs_supported)"
g_ibrs_supported="found '$g_ibrs_supported' in kernel image"
fi
fi
fi
if [ -z "$g_ibrs_supported" ] && [ -n "$opt_map" ]; then
g_ibrs_can_tell=1
if grep -q spec_ctrl "$opt_map"; then
g_ibrs_supported="found spec_ctrl in symbols file"
pr_debug "ibrs: found '*spec_ctrl*' symbol in $opt_map"
elif grep -q -e spectre_v2_select_mitigation -e spectre_v2_apply_mitigation "$opt_map"; then
# spectre_v2_select_mitigation exists since v4.15; split into
# spectre_v2_select_mitigation + spectre_v2_apply_mitigation in v6.16
g_ibrs_supported="found spectre_v2 mitigation function in symbols file"
pr_debug "ibrs: found spectre_v2_*_mitigation symbol in $opt_map"
fi
fi
# CONFIG_CPU_IBRS_ENTRY (v5.19) / CONFIG_MITIGATION_IBRS_ENTRY (v6.9): kernel IBRS on entry
if [ -z "$g_ibrs_supported" ] && [ -n "$opt_config" ] && [ -r "$opt_config" ]; then
g_ibrs_can_tell=1
if grep -q '^CONFIG_\(CPU_\|MITIGATION_\)IBRS_ENTRY=y' "$opt_config"; then
g_ibrs_supported="CONFIG_CPU_IBRS_ENTRY/CONFIG_MITIGATION_IBRS_ENTRY found in kernel config"
pr_debug "ibrs: found IBRS entry config option in $opt_config"
fi
fi
# recent (4.15) vanilla kernels have IBPB but not IBRS, and without the debugfs tunables of Red Hat
# we can detect it directly in the image
if [ -z "$g_ibpb_supported" ] && [ -n "$g_kernel" ]; then
if ! command -v "${opt_arch_prefix}strings" >/dev/null 2>&1; then
:
else
g_ibpb_can_tell=1
g_ibpb_supported=$("${opt_arch_prefix}strings" "$g_kernel" | grep -Fw -e 'ibpb' -e ', IBPB' | head -n1)
if [ -n "$g_ibpb_supported" ]; then
pr_debug "ibpb: found ibpb evidence in kernel image ($g_ibpb_supported)"
g_ibpb_supported="found '$g_ibpb_supported' in kernel image"
fi
fi
fi
pr_info_nol " * Kernel is compiled with IBRS support: "
if [ -z "$g_ibrs_supported" ]; then
if [ "$g_ibrs_can_tell" = 1 ]; then
pstatus yellow NO
else
# problem obtaining/inspecting kernel or strings not installed, but if the later is true,
# then readelf is not installed either (both in binutils) which makes the former true, so
# either way g_kernel_err should be set
pstatus yellow UNKNOWN "couldn't check ($g_kernel_err)"
fi
else
if [ "$opt_verbose" -ge 2 ]; then
pstatus green YES "$g_ibrs_supported"
else
pstatus green YES
fi
fi
pr_info_nol " * IBRS enabled and active: "
if [ "$opt_live" = 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"
else
# 0 means disabled
# 1 is enabled only for kernel space
# 2 is enabled for kernel and user space
# 3 is enabled
# 4 is enhanced ibrs enabled
case "$g_ibrs_enabled" in
0)
if [ "$g_ibrs_fw_enabled" = 1 ]; then
pstatus blue YES "for firmware code only"
else
pstatus yellow NO
fi
;;
1) if [ "$g_ibrs_fw_enabled" = 1 ]; then pstatus green YES "for kernel space and firmware code"; else pstatus green YES "for kernel space"; fi ;;
2) if [ "$g_ibrs_fw_enabled" = 1 ]; then pstatus green YES "for kernel, user space, and firmware code"; else pstatus green YES "for both kernel and user space"; fi ;;
3) if [ "$g_ibrs_fw_enabled" = 1 ]; then pstatus green YES "for kernel and firmware code"; else pstatus green YES; fi ;;
4) pstatus green YES "Enhanced flavor, performance impact will be greatly reduced" ;;
*) if [ "$cap_ibrs" != 'SPEC_CTRL' ] && [ "$cap_ibrs" != 'IBRS_SUPPORT' ] && [ "$cap_spec_ctrl" != -1 ]; then
pstatus yellow NO
pr_debug "ibrs: known cpu not supporting SPEC-CTRL or IBRS"
else
pstatus yellow UNKNOWN
fi ;;
esac
fi
else
pstatus blue N/A "not testable in offline mode"
fi
pr_info_nol " * Kernel is compiled with IBPB support: "
if [ -z "$g_ibpb_supported" ]; then
if [ "$g_ibpb_can_tell" = 1 ]; then
pstatus yellow NO
else
# if we're in offline mode without System.map, we can't really know
pstatus yellow UNKNOWN "in offline mode, we need the kernel image to be able to tell"
fi
else
if [ "$opt_verbose" -ge 2 ]; then
pstatus green YES "$g_ibpb_supported"
else
pstatus green YES
fi
fi
pr_info_nol " * IBPB enabled and active: "
if [ "$opt_live" = 1 ]; then
case "$g_ibpb_enabled" in
"")
if [ "$g_ibrs_supported" = 1 ]; then
pstatus yellow UNKNOWN
else
pstatus yellow NO
fi
;;
0)
pstatus yellow NO
;;
1) pstatus green YES ;;
2) pstatus green YES "IBPB used instead of IBRS in all kernel entrypoints" ;;
*) pstatus yellow UNKNOWN ;;
esac
else
pstatus blue N/A "not testable in offline mode"
fi
pr_info "* Mitigation 2"
pr_info_nol " * Kernel has branch predictor hardening (arm): "
bp_harden_can_tell=0
bp_harden=''
if [ -r "$opt_config" ]; then
bp_harden_can_tell=1
bp_harden=$(grep -w 'CONFIG_HARDEN_BRANCH_PREDICTOR=y' "$opt_config")
if [ -n "$bp_harden" ]; then
pstatus green YES
pr_debug "bp_harden: found '$bp_harden' in $opt_config"
fi
fi
if [ -z "$bp_harden" ] && [ -n "$opt_map" ]; then
bp_harden_can_tell=1
bp_harden=$(grep -w bp_hardening_data "$opt_map")
if [ -n "$bp_harden" ]; then
pstatus green YES
pr_debug "bp_harden: found '$bp_harden' in $opt_map"
fi
fi
if [ -z "$bp_harden" ]; then
if [ "$bp_harden_can_tell" = 1 ]; then
pstatus yellow NO
else
pstatus yellow UNKNOWN
fi
fi
pr_info_nol " * Kernel compiled with retpoline option: "
# We check the RETPOLINE kernel options
retpoline=0
if [ -r "$opt_config" ]; then
if grep -q '^CONFIG_\(MITIGATION_\)\?RETPOLINE=y' "$opt_config"; then
pstatus green YES
retpoline=1
# shellcheck disable=SC2046
pr_debug 'retpoline: found '$(grep '^CONFIG_\(MITIGATION_\)\?RETPOLINE' "$opt_config")" in $opt_config"
else
pstatus yellow NO
fi
else
pstatus yellow UNKNOWN "couldn't read your kernel configuration"
fi
if [ "$retpoline" = 1 ]; then
# Now check if the compiler used to compile the kernel knows how to insert retpolines in generated asm
# For gcc, this is -mindirect-branch=thunk-extern (detected by the kernel makefiles)
# See gcc commit https://github.com/hjl-tools/gcc/commit/23b517d4a67c02d3ef80b6109218f2aadad7bd79
# In latest retpoline LKML patches, the noretpoline_setup symbol exists only if CONFIG_MITIGATION_RETPOLINE is set
# *AND* if the compiler is retpoline-compliant, so look for that symbol. The name of this kernel config
# option before version 6.9-rc1 is CONFIG_RETPOLINE.
#
# if there is "retpoline" in the file and NOT "minimal", then it's full retpoline
# (works for vanilla and Red Hat variants)
#
# 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 [ "$opt_live" = 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
retpoline_compiler_reason="kernel reports minimal retpoline compilation"
else
retpoline_compiler=1
retpoline_compiler_reason="kernel reports full retpoline compilation"
fi
fi
elif [ -n "$opt_map" ]; then
# look for the symbol
if grep -qw noretpoline_setup "$opt_map"; then
retpoline_compiler=1
retpoline_compiler_reason="noretpoline_setup symbol found in System.map"
fi
elif [ -n "$g_kernel" ]; then
# look for the symbol
if command -v "${opt_arch_prefix}nm" >/dev/null 2>&1; then
# the proper way: use nm and look for the symbol
if "${opt_arch_prefix}nm" "$g_kernel" 2>/dev/null | grep -qw 'noretpoline_setup'; then
retpoline_compiler=1
retpoline_compiler_reason="noretpoline_setup found in kernel symbols"
fi
elif grep -q noretpoline_setup "$g_kernel"; then
# if we don't have nm, nevermind, the symbol name is long enough to not have
# any false positive using good old grep directly on the binary
retpoline_compiler=1
retpoline_compiler_reason="noretpoline_setup found in kernel"
fi
fi
if [ -n "$retpoline_compiler" ]; then
pr_info_nol " * Kernel compiled with a retpoline-aware compiler: "
if [ "$retpoline_compiler" = 1 ]; then
if [ -n "$retpoline_compiler_reason" ]; then
pstatus green YES "$retpoline_compiler_reason"
else
pstatus green YES
fi
else
if [ -n "$retpoline_compiler_reason" ]; then
pstatus red NO "$retpoline_compiler_reason"
else
pstatus red NO
fi
fi
fi
fi
# only Red Hat has a tunable to disable it on runtime
retp_enabled=-1
if [ "$opt_live" = 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"
pr_info_nol " * Retpoline is enabled: "
if [ "$retp_enabled" = 1 ]; then
pstatus green YES
else
pstatus yellow NO
fi
fi
fi
# only for information, in verbose mode
if [ "$opt_verbose" -ge 2 ]; then
pr_info_nol " * Local gcc is retpoline-aware: "
if command -v gcc >/dev/null 2>&1; then
if [ -n "$(gcc -mindirect-branch=thunk-extern --version 2>&1 >/dev/null)" ]; then
pstatus blue NO
else
pstatus green YES
fi
else
pstatus blue NO "gcc is not installed"
fi
fi
if is_vulnerable_to_empty_rsb || [ "$opt_verbose" -ge 2 ]; then
pr_info_nol " * Kernel supports RSB filling: "
rsb_filling=0
if [ "$opt_live" = 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
pstatus green YES
fi
fi
if [ "$rsb_filling" = 0 ]; then
if [ -n "$g_kernel_err" ]; then
pstatus yellow UNKNOWN "couldn't check ($g_kernel_err)"
else
if grep -qw -e 'Filling RSB on context switch' "$g_kernel"; then
rsb_filling=1
pstatus green YES
else
rsb_filling=0
pstatus yellow NO
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
# --- SMT state (used in STIBP inference and verdict) ---
is_cpu_smt_enabled
smt_enabled=$?
# smt_enabled: 0=enabled, 1=disabled, 2=unknown
# --- 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
if [ "$cap_bhi" = 1 ]; then
v2_bhi_status=bhi_dis_s
else
v2_bhi_status=sw-loop
fi
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
elif [ "$sys_interface_available" = 0 ]; then
# we have no sysfs but were asked to use it only!
msg="/sys vulnerability interface use forced, but it's not available!"
status=UNK
fi
if ! is_cpu_affected "$cve"; then
# override status & msg in case CPU is not vulnerable after all
pvulnstatus "$cve" OK "your CPU vendor reported your CPU model as not affected"
elif [ -z "$msg" ]; then
if [ "$opt_sysfs_only" != 1 ]; then
# --- own logic using Phase 2 variables ---
# Helper: collect caveats for the verdict message
_v2_caveats=''
# Append a caveat string to the _v2_caveats list
# Callers: check_CVE_2017_5715_linux (eIBRS, IBRS, retpoline verdict paths)
_v2_add_caveat() { _v2_caveats="${_v2_caveats:+$_v2_caveats; }$1"; }
# 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; then
if [ "$cap_bhi" = 0 ]; then
_v2_ok=0
_v2_add_caveat "BHI vulnerable (no BHI_DIS_S hardware support, no kernel mitigation detected)"
elif [ "$cap_rrsba" != 0 ]; then
_v2_add_caveat "BHI status unknown (kernel may lack BHI mitigation)"
fi
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
# eBPF caveat: eIBRS without retpoline is insufficient when unprivileged eBPF is enabled
_ebpf_disabled=''
if [ "$v2_base_mode" = "eibrs" ] || [ "$v2_base_mode" = "eibrs_lfence" ]; then
# shellcheck disable=SC2154
if [ -n "${SMC_MOCK_UNPRIVILEGED_BPF_DISABLED:-}" ]; then
_ebpf_disabled="$SMC_MOCK_UNPRIVILEGED_BPF_DISABLED"
g_mocked=1
elif [ "$opt_live" = 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
# In paranoid mode, enabled unprivileged eBPF makes eIBRS insufficient
if [ "$_v2_ok" = 1 ] && [ "$_ebpf_disabled" = 0 ] && [ "$opt_paranoid" = 1 ]; then
_v2_ok=0
_v2_add_caveat "unprivileged eBPF enabled (eIBRS insufficient)"
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
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."
if [ "$_ebpf_disabled" = 0 ]; then
pr_info " Unprivileged eBPF is currently ENABLED (kernel.unprivileged_bpf_disabled=0): this system may be vulnerable!"
elif [ "$_ebpf_disabled" = 1 ] || [ "$_ebpf_disabled" = 2 ]; then
pr_info " Unprivileged eBPF is currently disabled (kernel.unprivileged_bpf_disabled=$_ebpf_disabled): eIBRS is sufficient."
else
pr_info " Could not read kernel.unprivileged_bpf_disabled, check it manually with \`sysctl kernel.unprivileged_bpf_disabled\`."
fi
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\`. If unprivileged eBPF is enabled, disable it with \`sysctl -w kernel.unprivileged_bpf_disabled=1\`. 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"
elif [ "$v2_bhi_status" = "unknown" ] && is_intel && [ "$cap_bhi" = 0 ]; then
_v2_ok=0
_v2_add_caveat "BHI vulnerable (no BHI_DIS_S hardware support, no kernel mitigation detected)"
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; then
if [ "$cap_bhi" = 0 ] && [ "$cap_rrsba" = 1 ]; then
_v2_ok=0
_v2_add_caveat "BHI vulnerable (no BHI_DIS_S hardware support, RRSBA bypasses retpoline)"
elif [ "$cap_rrsba" = 1 ]; then
_v2_add_caveat "BHI status unknown with RRSBA"
fi
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
pr_warn "You should enable IBPB to complete retpoline as a Variant 2 mitigation"
else
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
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
pvulnstatus "$cve" OK "Enhanced IBRS + IBPB are mitigating the vulnerability"
else
pvulnstatus "$cve" OK "IBRS + IBPB are mitigating the vulnerability"
fi
elif [ "$g_ibpb_enabled" = 2 ] && [ "$smt_enabled" != 0 ]; then
pvulnstatus "$cve" OK "Full IBPB is mitigating the vulnerability"
# Offline mode fallback
elif [ "$opt_live" != 1 ]; then
if [ "$retpoline" = 1 ] && [ -n "$g_ibpb_supported" ]; then
pvulnstatus "$cve" OK "offline mode: kernel supports retpoline + IBPB to mitigate the vulnerability"
elif [ -n "$g_ibrs_supported" ] && [ -n "$g_ibpb_supported" ]; then
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"
# CONFIG_MITIGATION_SPECTRE_V2 (v6.12+): top-level on/off for all Spectre V2 mitigations
elif [ -n "$opt_config" ] && [ -r "$opt_config" ] && grep -q '^CONFIG_MITIGATION_SPECTRE_V2=y' "$opt_config"; then
pvulnstatus "$cve" OK "offline mode: kernel has Spectre V2 mitigation framework enabled (CONFIG_MITIGATION_SPECTRE_V2)"
elif [ "$g_ibrs_can_tell" != 1 ]; then
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."
fi
fi
# Catch-all: if no verdict was reached above, it's VULN
if [ "$g_pvulnstatus_last_cve" != "$cve" ]; then
if is_intel || is_amd || is_hygon; then
pvulnstatus "$cve" VULN "Your CPU is affected and no sufficient mitigation was detected"
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."
else
if [ "$sys_interface_available" = 1 ]; then
pvulnstatus "$cve" "$status" "$ret_sys_interface_check_fullmsg"
else
pvulnstatus "$cve" VULN "no known mitigation exists for your CPU vendor ($cpu_vendor)"
fi
fi
fi
else
# --sysfs-only: Phase 2 variables are unset, fall back to the
# raw sysfs result (status + fullmsg were set in Phase 1).
pvulnstatus "$cve" "$status" "$ret_sys_interface_check_fullmsg"
fi
else
# msg was explicitly set by the "sysfs not available" elif above.
pvulnstatus "$cve" "$status" "$msg"
fi
}
# Sets: vulnstatus
check_CVE_2017_5715_bsd() {
local ibrs_disabled ibrs_active retpoline nb_thunks
pr_info "* Mitigation 1"
pr_info_nol " * Kernel supports IBRS: "
ibrs_disabled=$(sysctl -n hw.ibrs_disable 2>/dev/null)
if [ -z "$ibrs_disabled" ]; then
pstatus yellow NO
else
pstatus green YES
fi
pr_info_nol " * IBRS enabled and active: "
ibrs_active=$(sysctl -n hw.ibrs_active 2>/dev/null)
if [ "$ibrs_active" = 1 ]; then
pstatus green YES
else
pstatus yellow NO
fi
pr_info "* Mitigation 2"
pr_info_nol " * Kernel compiled with RETPOLINE: "
retpoline=0
if [ -n "$g_kernel_err" ]; then
pstatus yellow UNKNOWN "couldn't check ($g_kernel_err)"
else
if ! command -v "${opt_arch_prefix}readelf" >/dev/null 2>&1; then
pstatus yellow UNKNOWN "missing '${opt_arch_prefix}readelf' tool, please install it, usually it's in the binutils package"
else
nb_thunks=$("${opt_arch_prefix}readelf" -s "$g_kernel" | grep -c -e __llvm_retpoline_ -e __llvm_external_retpoline_ -e __x86_indirect_thunk_)
if [ "$nb_thunks" -gt 0 ]; then
retpoline=1
pstatus green YES "found $nb_thunks thunk(s)"
else
pstatus yellow NO
fi
fi
fi
if ! is_cpu_affected "$cve"; then
# override status & msg in case CPU is not vulnerable after all
pvulnstatus "$cve" OK "your CPU vendor reported your CPU model as not affected"
elif [ "$retpoline" = 1 ]; then
pvulnstatus "$cve" OK "Retpoline mitigates the vulnerability"
elif [ "$ibrs_active" = 1 ]; then
pvulnstatus "$cve" OK "IBRS mitigates the vulnerability"
elif [ "$ibrs_disabled" = 0 ]; then
pvulnstatus "$cve" VULN "IBRS is supported by your kernel but your CPU microcode lacks support"
explain "The microcode of your CPU needs to be upgraded to be able to use IBRS. 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). To do a microcode update, you can search the ports for the \`cpupdate\` tool. Microcode updates done this way are not reboot-proof, so be sure to do it every time the system boots up."
elif [ "$ibrs_disabled" = 1 ]; then
pvulnstatus "$cve" VULN "IBRS is supported but administratively disabled on your system"
explain "To enable IBRS, use \`sysctl hw.ibrs_disable=0\`"
else
pvulnstatus "$cve" VULN "IBRS is needed to mitigate the vulnerability but your kernel is missing support"
explain "You need to either upgrade your kernel or recompile yourself a more recent version having IBRS support"
fi
}