# 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 }