# vim: set ts=4 sw=4 sts=4 et:

# Helpers for is_cpu_affected: encode the 4 patterns for setting affected_* variables.
# Each function takes the variable suffix as $1 (e.g. "variantl1tf", not "affected_variantl1tf").
# Variables hold 1 (not affected / immune) or 0 (affected / vuln); empty = not yet decided.

# Set affected_$1 to 1 (not affected) unconditionally.
# Use for: hardware capability bits (cap_rdcl_no, cap_ssb_no, cap_gds_no, cap_tsa_*_no),
#          is_cpu_specex_free results, and vendor-wide immune facts (AMD/L1TF, Cavium, etc.).
# This always wins and cannot be overridden by _infer_vuln (which only fires on empty).
# Must not be followed by _set_vuln for the same variable in the same code path.
_set_immune() { eval "affected_$1=1"; }

# Set affected_$1 to 0 (affected) unconditionally.
# Use for: confirmed-vuln model/erratum lists, ARM unknown-CPU fallback.
# Note: intentionally overrides a prior _infer_immune (1) — this is required for ARM
#       big.LITTLE cumulative logic where a second vuln core must override a prior safe core.
# Must not be called after _set_immune for the same variable in the same code path.
_set_vuln() { eval "affected_$1=0"; }

# Set affected_$1 to 1 (not affected) only if not yet decided (currently empty).
# Use for: model/family whitelists, per-part ARM immune inferences,
#          AMD/ARM partial immunity (immune on this variant axis but not others).
_infer_immune() { eval "[ -z \"\$affected_$1\" ] && affected_$1=1 || :"; }

# Set affected_$1 to 0 (affected) only if not yet decided (currently empty).
# Use for: family-level catch-all fallbacks (Intel L1TF non-whitelist, itlbmh non-whitelist).
_infer_vuln() { eval "[ -z \"\$affected_$1\" ] && affected_$1=0 || :"; }

# Return 0 (true) if a CVE's arch tag matches the current context (host CPU
# and/or target kernel), so the check is worth running. Untagged CVEs are
# always relevant.
# - In no-hw mode the host CPU is ignored: gate only on target kernel arch.
# - Otherwise a match on either the host CPU or the target kernel is enough
#   (they normally agree in live mode; if they disagree, check_kernel_cpu_arch_mismatch
#    has already forced no-hw, handled by the branch above).
# Args: $1=cve_id
# Callers: src/main.sh (CVE dispatch loop), check_cpu_vulnerabilities
_is_cve_relevant_arch() {
    local arch
    arch=$(_cve_registry_field "$1" 5)
    # Untagged CVE: always relevant
    [ -z "$arch" ] && return 0
    case "$arch" in
        x86)
            [ "$g_mode" != no-hw ] && is_x86_cpu && return 0
            is_x86_kernel && return 0
            return 1
            ;;
        arm)
            [ "$g_mode" != no-hw ] && is_arm_cpu && return 0
            is_arm_kernel && return 0
            return 1
            ;;
    esac
    # Unknown tag value: don't gate (fail open)
    return 0
}

# Return the cached affected_* status for a given CVE
# Args: $1=cve_id
# Returns: 0 if affected, 1 if not affected
# Callers: is_cpu_affected
_is_cpu_affected_cached() {
    local suffix
    suffix=$(_cve_registry_field "$1" 3)
    # shellcheck disable=SC2086
    eval "return \$affected_${suffix}"
}

# Determine whether the current CPU is affected by a given CVE using whitelist logic
# Args: $1=cve_id (one of the $g_supported_cve_list items)
# Returns: 0 if affected, 1 if not affected
is_cpu_affected() {
    local result cpuid_hex reptar_ucode_list bpi_ucode_list tuple fixed_ucode_ver affected_fmspi affected_fms ucode_platformid_mask affected_cpuid i cpupart cpuarch

    # if CPU is Intel and is in our dump of the Intel official affected CPUs page, use it:
    if is_intel; then
        cpuid_hex=$(printf "0x%08X" $((cpu_cpuid)))
        if [ "${g_intel_line:-}" = "no" ]; then
            pr_debug "is_cpu_affected: $cpuid_hex not in Intel database (cached)"
        elif [ -z "$g_intel_line" ]; then
            # Try hybrid-specific entry first (H=0 or H=1), fall back to unqualified entry
            g_intel_line=$(read_inteldb | grep -F "$cpuid_hex,H=$cpu_hybrid," | head -n1)
            if [ -z "$g_intel_line" ]; then
                # No hybrid-specific entry, try unqualified (no H= field)
                g_intel_line=$(read_inteldb | grep -F "$cpuid_hex," | grep -v ',H=' | head -n1)
            fi
            if [ -z "$g_intel_line" ]; then
                g_intel_line=no
                pr_debug "is_cpu_affected: $cpuid_hex not in Intel database"
            fi
        fi
        if [ "$g_intel_line" != "no" ]; then
            result=$(echo "$g_intel_line" | grep -Eo ,"$(echo "$1" | cut -c5-)"'=[^,]+' | cut -d= -f2)
            pr_debug "is_cpu_affected: inteldb for $1 says '$result'"

            # handle special case for Foreshadow SGX (CVE-2018-3615):
            # even if we are affected to L1TF (CVE-2018-3620/CVE-2018-3646), if there's no SGX on our CPU,
            # then we're not affected to the original Foreshadow.
            if [ "$1" = "CVE-2018-3615" ] && [ "$cap_sgx" = 0 ]; then
                # not affected
                return 1
            fi
            # /special case

            if [ "$result" = "N" ]; then
                # not affected
                return 1
            elif [ -n "$result" ]; then
                # non-empty string != N means affected
                return 0
            fi
        fi
    fi

    # Otherwise, do it ourselves

    if [ "$g_is_cpu_affected_cached" = 1 ]; then
        _is_cpu_affected_cached "$1"
        return $?
    fi

    affected_variant1=''
    affected_variant2=''
    affected_variant3=''
    affected_variant3a=''
    affected_variant4=''
    affected_variantl1tf=''
    affected_msbds=''
    affected_mfbds=''
    affected_mlpds=''
    affected_mdsum=''
    affected_taa=''
    affected_itlbmh=''
    affected_srbds=''
    affected_mmio=''
    affected_sls=''
    # ARM64 speculation-related errata (ARM Ltd, implementer 0x41); non-ARM systems are immune below.
    affected_arm_spec_at=''
    affected_arm_spec_unpriv_load=''
    affected_arm_ssbs_nosync=''
    # DIV0, FPDSS, Zenbleed and Inception are all AMD specific, look for "is_amd" below:
    _set_immune div0
    _set_immune fpdss
    _set_immune zenbleed
    _set_immune inception
    # TSA is AMD specific (Zen 3/4), look for "is_amd" below:
    _set_immune tsa
    # Retbleed: AMD (CVE-2022-29900) and Intel (CVE-2022-29901) specific:
    _set_immune retbleed
    # Downfall, Reptar, RFDS, ITS & BPI are Intel specific, look for "is_intel" below:
    _set_immune downfall
    _set_immune reptar
    _set_immune rfds
    _set_immune its
    _set_immune bpi
    # VMScape affects Intel, AMD and Hygon — set immune, overridden below:
    _set_immune vmscape

    if is_cpu_mds_free; then
        _infer_immune msbds
        _infer_immune mfbds
        _infer_immune mlpds
        _infer_immune mdsum
        pr_debug "is_cpu_affected: cpu not affected by Microarchitectural Data Sampling"
    elif is_cpu_msbds_only; then
        _infer_immune mfbds
        _infer_immune mlpds
        _infer_immune mdsum
        pr_debug "is_cpu_affected: cpu only affected by MSBDS, not MFBDS/MLPDS/MDSUM"
    fi

    if is_cpu_taa_free; then
        _infer_immune taa
        pr_debug "is_cpu_affected: cpu not affected by TSX Asynhronous Abort"
    fi

    if is_cpu_srbds_free; then
        _infer_immune srbds
        pr_debug "is_cpu_affected: cpu not affected by Special Register Buffer Data Sampling"
    fi

    if is_cpu_mmio_free; then
        _infer_immune mmio
        pr_debug "is_cpu_affected: cpu not affected by MMIO Stale Data"
    fi

    # NO_SPECTRE_V2: Centaur family 7 and Zhaoxin family 7 are immune to Spectre V2
    # kernel commit 1e41a766c98b (v5.6-rc1): added NO_SPECTRE_V2 exemption
    # Zhaoxin vendor_id is "  Shanghai  " in cpuinfo (parsed as "Shanghai" by awk)
    if { [ "$cpu_vendor" = "CentaurHauls" ] || [ "$cpu_vendor" = "Shanghai" ]; } && [ "$cpu_family" = 7 ]; then
        _infer_immune variant2
        pr_debug "is_cpu_affected: Centaur/Zhaoxin family 7 immune to Spectre V2 (NO_SPECTRE_V2)"
    fi

    if is_cpu_specex_free; then
        _set_immune variant1
        _set_immune variant2
        _set_immune variant3
        _set_immune variant3a
        _set_immune variant4
        _set_immune variantl1tf
        _set_immune msbds
        _set_immune mfbds
        _set_immune mlpds
        _set_immune mdsum
        _set_immune taa
        _set_immune srbds
        _set_immune mmio
    elif is_intel; then
        # Intel
        # https://github.com/crozone/SpectrePoC/issues/1 ^F E5200 => spectre 2 not affected
        # https://github.com/paboldin/meltdown-exploit/issues/19 ^F E5200 => meltdown affected
        # model name : Pentium(R) Dual-Core  CPU      E5200  @ 2.50GHz
        if echo "$cpu_friendly_name" | grep -qE 'Pentium\(R\) Dual-Core[[:space:]]+CPU[[:space:]]+E[0-9]{4}K?'; then
            _set_vuln variant1
            _infer_immune variant2
            _set_vuln variant3
        fi
        if [ "$cap_rdcl_no" = 1 ]; then
            # capability bit for future Intel processor that will explicitly state
            # that they're not affected to Meltdown
            # this var is set in check_cpu()
            _set_immune variant3
            _set_immune variantl1tf
            pr_debug "is_cpu_affected: RDCL_NO is set so not vuln to meltdown nor l1tf"
        fi
        if [ "$cap_ssb_no" = 1 ]; then
            # capability bit for future Intel processor that will explicitly state
            # that they're not affected to Variant 4
            # this var is set in check_cpu()
            _set_immune variant4
            pr_debug "is_cpu_affected: SSB_NO is set so not vuln to affected_variant4"
        fi
        if is_cpu_ssb_free; then
            _infer_immune variant4
            pr_debug "is_cpu_affected: cpu not affected by speculative store bypass so not vuln to affected_variant4"
        fi
        # variant 3a
        if [ "$cpu_family" = 6 ]; then
            if [ "$cpu_model" = "$INTEL_FAM6_XEON_PHI_KNL" ] || [ "$cpu_model" = "$INTEL_FAM6_XEON_PHI_KNM" ]; then
                pr_debug "is_cpu_affected: xeon phi immune to variant 3a"
                _infer_immune variant3a
            elif [ "$cpu_model" = "$INTEL_FAM6_ATOM_SILVERMONT" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_SILVERMONT_MID" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_SILVERMONT_D" ]; then
                # https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00115.html
                # https://github.com/speed47/spectre-meltdown-checker/issues/310
                # => silvermont CPUs (aka cherry lake for tablets and brawsell for mobile/desktop) don't seem to be affected
                # => goldmont ARE affected
                pr_debug "is_cpu_affected: silvermont immune to variant 3a"
                _infer_immune variant3a
            fi
        fi
        # L1TF (cap_rdcl_no already checked above)
        if [ "$cpu_family" = 6 ]; then
            if [ "$cpu_model" = "$INTEL_FAM6_ATOM_SALTWELL" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_SALTWELL_TABLET" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_SALTWELL_MID" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_BONNELL" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_BONNELL_MID" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_SILVERMONT" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_SILVERMONT_MID" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_SILVERMONT_D" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_AIRMONT" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_SILVERMONT_MID2" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_AIRMONT_NP" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_GOLDMONT" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_GOLDMONT_D" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_GOLDMONT_PLUS" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_TREMONT_D" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_XEON_PHI_KNL" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_XEON_PHI_KNM" ]; then

                pr_debug "is_cpu_affected: intel family 6 but model known to be immune to l1tf"
                _infer_immune variantl1tf
            else
                pr_debug "is_cpu_affected: intel family 6 is vuln to l1tf"
                _infer_vuln variantl1tf
            fi
        elif [ "$cpu_family" -lt 6 ]; then
            pr_debug "is_cpu_affected: intel family < 6 is immune to l1tf"
            _infer_immune variantl1tf
        fi
        # Downfall
        if [ "$cap_gds_no" = 1 ]; then
            # capability bit for future Intel processors that will explicitly state
            # that they're unaffected by GDS. Also set by hypervisors on virtual CPUs
            # so that the guest kernel doesn't try to mitigate GDS when it's already mitigated on the host
            pr_debug "is_cpu_affected: downfall: not affected (GDS_NO)"
            _set_immune downfall
        elif [ "$cpu_family" = 6 ]; then
            # model blacklist from the kernel (arch/x86/kernel/cpu/common.c cpu_vuln_blacklist):
            # 8974eb588283 (initial list) + c9f4c45c8ec3 (added Skylake/Skylake_L client)
            set -u
            if [ "$cpu_model" = "$INTEL_FAM6_SKYLAKE_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_SKYLAKE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_SKYLAKE_X" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_KABYLAKE_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_KABYLAKE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ICELAKE_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ICELAKE_D" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ICELAKE_X" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_COMETLAKE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_COMETLAKE_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_TIGERLAKE_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_TIGERLAKE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ROCKETLAKE" ]; then
                pr_debug "is_cpu_affected: downfall: affected"
                _set_vuln downfall
            elif [ "$cap_avx2" = 0 ] && [ "$cap_avx512" = 0 ]; then
                pr_debug "is_cpu_affected: downfall: no avx; immune"
                _infer_immune downfall
            else
                # Intel family 6 CPU with AVX2 or AVX512, not in the known-affected list
                # and GDS_NO not set: assume affected (whitelist principle)
                pr_debug "is_cpu_affected: downfall: unknown AVX-capable CPU, defaulting to affected"
                _infer_vuln downfall
            fi
            set +u
        fi
        # RFDS (Register File Data Sampling, CVE-2023-28746)
        # kernel cpu_vuln_blacklist (8076fcde016c, initial model list)
        # immunity: ARCH_CAP_RFDS_NO (bit 27 of IA32_ARCH_CAPABILITIES)
        # vendor scope: Intel only (family 6), Atom/hybrid cores
        if [ "$cap_rfds_no" = 1 ]; then
            pr_debug "is_cpu_affected: rfds: not affected (RFDS_NO)"
            _set_immune rfds
        elif [ "$cpu_family" = 6 ]; then
            set -u
            if [ "$cpu_model" = "$INTEL_FAM6_ATOM_GOLDMONT" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_GOLDMONT_D" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_GOLDMONT_PLUS" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_TREMONT_D" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_TREMONT" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_TREMONT_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_GRACEMONT" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ALDERLAKE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ALDERLAKE_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_RAPTORLAKE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_RAPTORLAKE_P" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_RAPTORLAKE_S" ]; then
                pr_debug "is_cpu_affected: rfds: affected"
                _set_vuln rfds
            fi
            set +u
        fi
        # ITS (Indirect Target Selection, CVE-2024-28956)
        # kernel vulnerable_to_its() + cpu_vuln_blacklist (159013a7ca18)
        # immunity: ARCH_CAP_ITS_NO (bit 62 of IA32_ARCH_CAPABILITIES)
        # immunity: X86_FEATURE_BHI_CTRL (none of the affected CPUs have this)
        # vendor scope: Intel only (family 6), with stepping constraints on some models
        if [ "$cap_its_no" = 1 ]; then
            pr_debug "is_cpu_affected: its: not affected (ITS_NO)"
            _set_immune its
        elif [ "$cpu_family" = 6 ]; then
            set -u
            if { [ "$cpu_model" = "$INTEL_FAM6_SKYLAKE_X" ] && [ "$cpu_stepping" -gt 5 ]; } ||
                { [ "$cpu_model" = "$INTEL_FAM6_KABYLAKE_L" ] && [ "$cpu_stepping" -gt 11 ]; } ||
                { [ "$cpu_model" = "$INTEL_FAM6_KABYLAKE" ] && [ "$cpu_stepping" -gt 12 ]; } ||
                [ "$cpu_model" = "$INTEL_FAM6_ICELAKE_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ICELAKE_D" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ICELAKE_X" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_COMETLAKE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_COMETLAKE_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_TIGERLAKE_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_TIGERLAKE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ROCKETLAKE" ]; then
                pr_debug "is_cpu_affected: its: affected"
                _set_vuln its
            fi
            set +u
        fi
        # Reptar
        # the only way to know whether a CPU is vuln, is to check whether there is a known ucode update for it,
        # as the mitigation is only ucode-based and there's no flag exposed by the kernel or by an updated ucode.
        # we have to hardcode the truthtable of affected CPUs vs updated ucodes...
        # https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/advisory-guidance/redundant-prefix-issue.html
        # list initially taken from:
        # https://github.com/intel/Intel-Linux-Processor-Microcode-Data-Files/commit/ece0d294a29a1375397941a4e6f2f7217910bc89#diff-e6fad0f2abbac6c9603b2e8f88fe1d151a83de708aeca1c1d93d881c958ecba4R26
        # updated 2026-04 with Intel affected processor list + releasenote.md:
        # added 06-9a-04/40 (AZB), 06-bd-01/80 (Lunar Lake, post-dates Reptar: first ucode already includes fix)
        g_reptar_fixed_ucode_version=''
        reptar_ucode_list='
06-97-02/07,00000032
06-97-05/07,00000032
06-9a-03/80,00000430
06-9a-04/80,00000430
06-9a-04/40,00000005
06-6c-01/10,01000268
06-6a-06/87,0d0003b9
06-7e-05/80,000000c2
06-ba-02/e0,0000411c
06-b7-01/32,0000011d
06-a7-01/02,0000005d
06-bf-05/07,00000032
06-bf-02/07,00000032
06-ba-03/e0,0000411c
06-8f-08/87,2b0004d0
06-8f-07/87,2b0004d0
06-8f-06/87,2b0004d0
06-8f-05/87,2b0004d0
06-8f-04/87,2b0004d0
06-8f-08/10,2c000290
06-8c-01/80,000000b4
06-8c-00/ff,000000b4
06-8d-01/c2,0000004e
06-8d-00/c2,0000004e
06-8c-02/c2,00000034
06-bd-01/80,0000011f
'
        for tuple in $reptar_ucode_list; do
            fixed_ucode_ver=$((0x$(echo "$tuple" | cut -d, -f2)))
            affected_fmspi=$(echo "$tuple" | cut -d, -f1)
            affected_fms=$(echo "$affected_fmspi" | cut -d/ -f1)
            ucode_platformid_mask=0x$(echo "$affected_fmspi" | cut -d/ -f2)
            affected_cpuid=$(
                fms2cpuid \
                    0x"$(echo "$affected_fms" | cut -d- -f1)" \
                    0x"$(echo "$affected_fms" | cut -d- -f2)" \
                    0x"$(echo "$affected_fms" | cut -d- -f3)"
            )
            if [ "$cpu_cpuid" = "$affected_cpuid" ] && [ $((cpu_platformid & ucode_platformid_mask)) -gt 0 ]; then
                _set_vuln reptar
                g_reptar_fixed_ucode_version=$fixed_ucode_ver
                break
            fi
        done
        # if we didn't match the ucode list above, also check the model blacklist:
        # Intel never tells about their EOL CPUs, so more CPUs might be affected
        # than the ones that received a microcode update (e.g. steppings with
        # different platform IDs that were dropped before the Reptar fix).
        if [ -z "$g_reptar_fixed_ucode_version" ] && [ "$cpu_family" = 6 ]; then
            set -u
            if [ "$cpu_model" = "$INTEL_FAM6_ALDERLAKE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ALDERLAKE_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ICELAKE_X" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ICELAKE_D" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ICELAKE_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ROCKETLAKE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_TIGERLAKE_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_TIGERLAKE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_SAPPHIRERAPIDS_X" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_RAPTORLAKE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_RAPTORLAKE_P" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_RAPTORLAKE_S" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_LUNARLAKE_M" ]; then
                pr_debug "is_cpu_affected: reptar: affected (model match, no known fixing ucode)"
                _set_vuln reptar
            fi
            set +u
        fi

        # Retbleed (Intel, CVE-2022-29901): Skylake through Rocket Lake, or any CPU with RSBA
        # kernel cpu_vuln_blacklist for RETBLEED (6b80b59b3555, 6ad0ad2bf8a6, f54d45372c6a)
        # plus ARCH_CAP_RSBA catch-all (bit 2 of IA32_ARCH_CAPABILITIES)
        if [ "$cap_rsba" = 1 ]; then
            _set_vuln retbleed
        elif [ "$cpu_family" = 6 ]; then
            if [ "$cpu_model" = "$INTEL_FAM6_SKYLAKE_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_SKYLAKE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_SKYLAKE_X" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_KABYLAKE_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_KABYLAKE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_CANNONLAKE_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ICELAKE_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_COMETLAKE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_COMETLAKE_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_LAKEFIELD" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ROCKETLAKE" ]; then
                _set_vuln retbleed
            fi
        fi

        # VMScape (CVE-2025-40300): Intel model blacklist
        # kernel cpu_vuln_blacklist VMSCAPE (a508cec6e521 + 8a68d64bb103)
        # immunity: no ARCH_CAP bits (purely blacklist-based)
        # note: kernel only sets bug on bare metal (!X86_FEATURE_HYPERVISOR)
        # vendor scope: Intel + AMD + Hygon (AMD/Hygon handled below)
        if [ "$cpu_family" = 6 ]; then
            set -u
            if [ "$cpu_model" = "$INTEL_FAM6_SANDYBRIDGE_X" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_SANDYBRIDGE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_IVYBRIDGE_X" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_IVYBRIDGE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_HASWELL" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_HASWELL_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_HASWELL_G" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_HASWELL_X" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_BROADWELL_D" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_BROADWELL_X" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_BROADWELL_G" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_BROADWELL" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_SKYLAKE_X" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_SKYLAKE_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_SKYLAKE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_KABYLAKE_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_KABYLAKE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_CANNONLAKE_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_COMETLAKE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_COMETLAKE_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ALDERLAKE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ALDERLAKE_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_RAPTORLAKE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_RAPTORLAKE_P" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_RAPTORLAKE_S" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_METEORLAKE_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ARROWLAKE_H" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ARROWLAKE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ARROWLAKE_U" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_LUNARLAKE_M" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_SAPPHIRERAPIDS_X" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_GRANITERAPIDS_X" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_EMERALDRAPIDS_X" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_GRACEMONT" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_CRESTMONT_X" ]; then
                pr_debug "is_cpu_affected: vmscape: affected"
                _set_vuln vmscape
            fi
            set +u
        fi

        # BPI (Branch Privilege Injection, CVE-2024-45332)
        # microcode-only fix, no kernel X86_BUG flag, no CPUID/MSR indicator for the fix.
        # We have to hardcode the truthtable of affected CPUs vs fixing ucodes,
        # same approach as Reptar (see above).
        # https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/advisory-guidance/indirect-branch-predictor-delayed-updates.html
        # list taken from Intel affected processor list + Intel-Linux-Processor-Microcode-Data-Files releasenote.md
        # format: FF-MM-SS/platformid_mask,fixed_ucode_version
        g_bpi_fixed_ucode_version=''
        bpi_ucode_list='
06-9e-0d/22,00000104
06-8e-0a/c0,000000f6
06-8e-0b/d0,000000f6
06-8e-0c/94,00000100
06-a5-02/20,00000100
06-a5-03/22,00000100
06-a5-05/22,00000100
06-a6-00/80,00000102
06-a6-01/80,00000100
06-a7-01/02,00000065
06-7e-05/80,000000cc
06-6a-06/87,0d000421
06-6c-01/10,010002f1
06-8c-01/80,000000be
06-8c-02/c2,0000003e
06-8d-01/c2,00000058
06-97-02/07,0000003e
06-97-05/07,0000003e
06-9a-03/80,0000043b
06-9a-04/80,0000043b
06-9a-04/40,0000000c
06-be-00/19,00000021
06-b7-01/32,00000133
06-ba-02/e0,00006134
06-ba-03/e0,00006134
06-bf-02/07,0000003e
06-bf-05/07,0000003e
06-aa-04/e6,00000028
06-b5-00/80,0000000d
06-c5-02/82,0000011b
06-c6-02/82,0000011b
06-bd-01/80,00000125
06-55-07/bf,05003901
06-55-0b/bf,07002b01
06-8f-07/87,2b000661
06-8f-08/87,2b000661
06-8f-08/10,2c000421
06-cf-02/87,210002d3
06-7a-08/01,00000026
'
        for tuple in $bpi_ucode_list; do
            fixed_ucode_ver=$((0x$(echo "$tuple" | cut -d, -f2)))
            affected_fmspi=$(echo "$tuple" | cut -d, -f1)
            affected_fms=$(echo "$affected_fmspi" | cut -d/ -f1)
            ucode_platformid_mask=0x$(echo "$affected_fmspi" | cut -d/ -f2)
            affected_cpuid=$(
                fms2cpuid \
                    0x"$(echo "$affected_fms" | cut -d- -f1)" \
                    0x"$(echo "$affected_fms" | cut -d- -f2)" \
                    0x"$(echo "$affected_fms" | cut -d- -f3)"
            )
            if [ "$cpu_cpuid" = "$affected_cpuid" ] && [ $((cpu_platformid & ucode_platformid_mask)) -gt 0 ]; then
                _set_vuln bpi
                g_bpi_fixed_ucode_version=$fixed_ucode_ver
                break
            fi
        done
        # if we didn't match the ucode list above, also check the model blacklist:
        # Intel never tells about their EOL CPUs, so more CPUs might be affected
        # than the ones that received a microcode update. In that case, we flag
        # the CPU as affected but g_bpi_fixed_ucode_version stays empty (the CVE
        # check will handle this by reporting VULN with no known fix).
        if [ -z "$g_bpi_fixed_ucode_version" ] && [ "$cpu_family" = 6 ]; then
            set -u
            if [ "$cpu_model" = "$INTEL_FAM6_KABYLAKE_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_KABYLAKE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_COMETLAKE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_COMETLAKE_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ROCKETLAKE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ICELAKE_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ICELAKE_X" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ICELAKE_D" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_TIGERLAKE_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_TIGERLAKE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ALDERLAKE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ALDERLAKE_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_GRACEMONT" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_RAPTORLAKE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_RAPTORLAKE_P" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_RAPTORLAKE_S" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_METEORLAKE_L" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ARROWLAKE_H" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ARROWLAKE" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ARROWLAKE_U" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_LUNARLAKE_M" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_SKYLAKE_X" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_SAPPHIRERAPIDS_X" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_EMERALDRAPIDS_X" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_GOLDMONT_PLUS" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_CRESTMONT" ]; then
                pr_debug "is_cpu_affected: bpi: affected (model match, no known fixing ucode)"
                _set_vuln bpi
            fi
            set +u
        fi

    elif is_amd || is_hygon; then
        # AMD revised their statement about affected_variant2 => affected
        # https://www.amd.com/en/corporate/speculative-execution
        _set_vuln variant1
        _set_vuln variant2
        _infer_immune variant3
        # https://www.amd.com/en/corporate/security-updates
        # "We have not identified any AMD x86 products susceptible to the Variant 3a vulnerability in our analysis to-date."
        _infer_immune variant3a
        if is_cpu_ssb_free; then
            _infer_immune variant4
            pr_debug "is_cpu_affected: cpu not affected by speculative store bypass so not vuln to affected_variant4"
        fi
        _set_immune variantl1tf

        # DIV0 (Zen1/Zen+)
        # 77245f1c3c64 (v6.5, initial model list): family 0x17 models 0x00-0x2f, 0x50-0x5f
        # bfff3c6692ce (v6.8): moved to init_amd_zen1(), unconditional for all ZEN1-flagged CPUs
        # The kernel's X86_FEATURE_ZEN1 covers family 0x17 models 0x00-0x2f and 0x50-0x5f,
        # which spans both Zen1 (Summit Ridge, Naples, Raven Ridge, Snowy Owl) and Zen+
        # (Pinnacle Ridge, Picasso, Dali, Colfax) products -- all using the same divider silicon.
        amd_legacy_erratum "$(amd_model_range 0x17 0x00 0x0 0x2f 0xf)" && _set_vuln div0
        amd_legacy_erratum "$(amd_model_range 0x17 0x50 0x0 0x5f 0xf)" && _set_vuln div0

        # FPDSS: same Zen1/Zen+ cohort as DIV0 (both applied unconditionally in init_amd_zen1()).
        # e55d98e77561 (v7.1): unconditional in init_amd_zen1(); CVE-2025-54505 / AMD-SB-7053.
        # AMD-SB-7053 only enumerates a subset (EPYC 7001, EPYC Embedded 3000, Athlon/Ryzen 3000
        # with Radeon, Ryzen PRO 3000 with Radeon Vega), but the kernel mitigates the full
        # ZEN1 cohort, so we flag all of it to match the kernel's behavior.
        # shellcheck disable=SC2154
        [ "$affected_div0" = 0 ] && _set_vuln fpdss

        # Zenbleed
        amd_legacy_erratum "$(amd_model_range 0x17 0x30 0x0 0x4f 0xf)" && _set_vuln zenbleed
        amd_legacy_erratum "$(amd_model_range 0x17 0x60 0x0 0x7f 0xf)" && _set_vuln zenbleed
        amd_legacy_erratum "$(amd_model_range 0x17 0xa0 0x0 0xaf 0xf)" && _set_vuln zenbleed

        # Inception (according to kernel, zen 1 to 4)
        if [ "$cpu_family" = $((0x17)) ] || [ "$cpu_family" = $((0x19)) ]; then
            _set_vuln inception
        fi

        # TSA (Zen 3/4 are affected, unless CPUID says otherwise)
        if [ "$cap_tsa_sq_no" = 1 ] && [ "$cap_tsa_l1_no" = 1 ]; then
            # capability bits for AMD processors that explicitly state
            # they're not affected to TSA-SQ and TSA-L1
            # these vars are set in check_cpu()
            pr_debug "is_cpu_affected: TSA_SQ_NO and TSA_L1_NO are set so not vuln to TSA"
            _set_immune tsa
        elif [ "$cpu_family" = $((0x19)) ]; then
            _set_vuln tsa
        fi

        # Retbleed (AMD, CVE-2022-29900): families 0x15-0x17 (kernel X86_BUG_RETBLEED)
        if [ "$cpu_family" = $((0x15)) ] || [ "$cpu_family" = $((0x16)) ] || [ "$cpu_family" = $((0x17)) ]; then
            _set_vuln retbleed
        fi

        # VMScape (CVE-2025-40300): AMD families 0x17/0x19/0x1a, Hygon family 0x18
        # kernel cpu_vuln_blacklist VMSCAPE (a508cec6e521)
        if is_amd; then
            if [ "$cpu_family" = $((0x17)) ] || [ "$cpu_family" = $((0x19)) ] || [ "$cpu_family" = $((0x1a)) ]; then
                pr_debug "is_cpu_affected: vmscape: AMD family $cpu_family affected"
                _set_vuln vmscape
            fi
        elif is_hygon; then
            if [ "$cpu_family" = $((0x18)) ]; then
                pr_debug "is_cpu_affected: vmscape: Hygon family $cpu_family affected"
                _set_vuln vmscape
            fi
        fi

    elif [ "$cpu_vendor" = CAVIUM ]; then
        _set_immune variant3
        _set_immune variant3a
        _set_immune variantl1tf
    elif [ "$cpu_vendor" = PHYTIUM ]; then
        _set_immune variant3
        _set_immune variant3a
        _set_immune variantl1tf
    elif [ "$cpu_vendor" = ARM ]; then
        # ARM
        # reference: https://developer.arm.com/support/security-update
        # some devices (phones or other) have several ARMs and as such different part numbers,
        # an example is "bigLITTLE". we shouldn't rely on the first CPU only, so we check the whole list
        i=0
        for cpupart in $cpu_part_list; do
            i=$((i + 1))
            # do NOT quote $cpu_arch_list below
            # shellcheck disable=SC2086
            cpuarch=$(echo $cpu_arch_list | awk '{ print $'$i' }')
            pr_debug "checking cpu$i: <$cpupart> <$cpuarch>"
            # some kernels report AArch64 instead of 8
            [ "$cpuarch" = "AArch64" ] && cpuarch=8
            # some kernels report architecture with suffix (e.g. "5TEJ" for ARMv5TEJ), extract numeric prefix
            cpuarch=$(echo "$cpuarch" | grep -oE '^[0-9]+')
            if [ -n "$cpupart" ] && [ -n "$cpuarch" ]; then
                # Cortex-R7 and Cortex-R8 are real-time and only used in medical devices or such
                # I can't find their CPU part number, but it's probably not that useful anyway
                # model R7 R8 A8  A9  A12 A15 A17 A57 A72 A73 A75 A76 A77 Neoverse-N1 Neoverse-V1 Neoverse-N1 Neoverse-V2 Neoverse-V3 Neoverse-V3AE
                # part   ?  ? c08 c09 c0d c0f c0e d07 d08 d09 d0a d0b d0d d0c         d40	  d49	      d4f	  d84	      d83
                # arch  7? 7? 7   7   7   7   7   8   8   8   8   8   8   8           8		  8	      8		  8	      8
                #
                # Whitelist identified non-affected processors, use vulnerability information from
                # https://developer.arm.com/support/arm-security-updates/speculative-processor-vulnerability
                # Partnumbers can be found here:
                # https://github.com/gcc-mirror/gcc/blob/master/gcc/config/arm/arm-cpus.in
                #
                # Maintain cumulative check of vulnerabilities -
                # if at least one of the cpu is affected, then the system is affected
                if [ "$cpuarch" = 7 ] && echo "$cpupart" | grep -q -w -e 0xc08 -e 0xc09 -e 0xc0d -e 0xc0e; then
                    _set_vuln variant1
                    _set_vuln variant2
                    _infer_immune variant3
                    _infer_immune variant3a
                    _infer_immune variant4
                    pr_debug "checking cpu$i: armv7 A8/A9/A12/A17 non affected to variants 3, 3a & 4"
                elif [ "$cpuarch" = 7 ] && echo "$cpupart" | grep -q -w -e 0xc0f; then
                    _set_vuln variant1
                    _set_vuln variant2
                    _infer_immune variant3
                    _set_vuln variant3a
                    _infer_immune variant4
                    pr_debug "checking cpu$i: armv7 A15 non affected to variants 3 & 4"
                elif [ "$cpuarch" = 8 ] && echo "$cpupart" | grep -q -w -e 0xd07 -e 0xd08; then
                    _set_vuln variant1
                    _set_vuln variant2
                    _infer_immune variant3
                    _set_vuln variant3a
                    _set_vuln variant4
                    pr_debug "checking cpu$i: armv8 A57/A72 non affected to variants 3"
                elif [ "$cpuarch" = 8 ] && echo "$cpupart" | grep -q -w -e 0xd09; then
                    _set_vuln variant1
                    _set_vuln variant2
                    _infer_immune variant3
                    _infer_immune variant3a
                    _set_vuln variant4
                    pr_debug "checking cpu$i: armv8 A73 non affected to variants 3 & 3a"
                elif [ "$cpuarch" = 8 ] && echo "$cpupart" | grep -q -w -e 0xd0a; then
                    _set_vuln variant1
                    _set_vuln variant2
                    _set_vuln variant3
                    _infer_immune variant3a
                    _set_vuln variant4
                    pr_debug "checking cpu$i: armv8 A75 non affected to variant 3a"
                elif [ "$cpuarch" = 8 ] && echo "$cpupart" | grep -q -w -e 0xd0b -e 0xd0c -e 0xd0d; then
                    _set_vuln variant1
                    _infer_immune variant2
                    _infer_immune variant3
                    _infer_immune variant3a
                    _set_vuln variant4
                    pr_debug "checking cpu$i: armv8 A76/A77/NeoverseN1 non affected to variant 2, 3 & 3a"
                elif [ "$cpuarch" = 8 ] && echo "$cpupart" | grep -q -w -e 0xd40 -e 0xd49 -e 0xd4f -e 0xd84 -e 0xd83; then
                    _set_vuln variant1
                    _infer_immune variant2
                    _infer_immune variant3
                    _infer_immune variant3a
                    _infer_immune variant4
                    pr_debug "checking cpu$i: armv8 NeoverseN2/V1/V2/V3/V3AE non affected to variant 2, 3, 3a & 4"
                elif [ "$cpuarch" -le 7 ] || { [ "$cpuarch" = 8 ] && [ $((cpupart)) -lt $((0xd07)) ]; }; then
                    _infer_immune variant1
                    _infer_immune variant2
                    _infer_immune variant3
                    _infer_immune variant3a
                    _infer_immune variant4
                    pr_debug "checking cpu$i: arm arch$cpuarch, all immune (v7 or v8 and model < 0xd07)"
                else
                    _set_vuln variant1
                    _set_vuln variant2
                    _set_vuln variant3
                    _set_vuln variant3a
                    _set_vuln variant4
                    pr_debug "checking cpu$i: arm unknown arch$cpuarch part$cpupart, considering vuln"
                fi
            fi
            pr_debug "is_cpu_affected: for cpu$i and so far, we have <$affected_variant1> <$affected_variant2> <$affected_variant3> <$affected_variant3a> <$affected_variant4>"
        done
        _set_immune variantl1tf
    fi

    # we handle iTLB Multihit here (not linked to is_specex_free)
    if is_intel; then
        # commit f9aa6b73a407b714c9aac44734eb4045c893c6f7
        if [ "$cpu_family" = 6 ]; then
            if [ "$cpu_model" = "$INTEL_FAM6_ATOM_SALTWELL" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_SALTWELL_TABLET" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_SALTWELL_MID" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_BONNELL" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_BONNELL_MID" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_SILVERMONT" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_SILVERMONT_D" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_SILVERMONT_MID" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_AIRMONT" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_XEON_PHI_KNL" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_XEON_PHI_KNM" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_SILVERMONT_MID2" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_GOLDMONT" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_GOLDMONT_D" ] ||
                [ "$cpu_model" = "$INTEL_FAM6_ATOM_GOLDMONT_PLUS" ]; then
                pr_debug "is_cpu_affected: intel family 6 but model known to be immune to itlbmh"
                _infer_immune itlbmh
            else
                pr_debug "is_cpu_affected: intel family 6 is vuln to itlbmh"
                _infer_vuln itlbmh
            fi
        elif [ "$cpu_family" -lt 6 ]; then
            pr_debug "is_cpu_affected: intel family < 6 is immune to itlbmh"
            _infer_immune itlbmh
        fi
    else
        pr_debug "is_cpu_affected: non-intel not affected to itlbmh"
        _infer_immune itlbmh
    fi

    # SLS (Straight-Line Speculation):
    # - x86_64: all CPUs are affected (compile-time mitigation CONFIG_MITIGATION_SLS)
    # - arm64 (CVE-2020-13844): Cortex-A32/A34/A35/A53/A57/A72/A73 confirmed affected,
    #   and broadly all speculative Armv8-A cores. No kernel mitigation merged.
    #   Part numbers: A32=0xd01 A34=0xd02 A53=0xd03 A35=0xd04 A57=0xd07 A72=0xd08 A73=0xd09
    #   Plus later speculative cores: A75=0xd0a A76=0xd0b A77=0xd0d N1=0xd0c V1=0xd40 N2=0xd49 V2=0xd4f V3=0xd84 V3AE=0xd83
    if is_intel || is_amd; then
        _infer_vuln sls
    elif [ "$cpu_vendor" = ARM ]; then
        for cpupart in $cpu_part_list; do
            if echo "$cpupart" | grep -q -w -e 0xd01 -e 0xd02 -e 0xd03 -e 0xd04 \
                -e 0xd07 -e 0xd08 -e 0xd09 -e 0xd0a -e 0xd0b -e 0xd0c -e 0xd0d \
                -e 0xd40 -e 0xd49 -e 0xd4f -e 0xd84 -e 0xd83; then
                _set_vuln sls
            fi
        done
        # non-speculative ARM cores (arch <= 7, or early v8 models) are not affected
        _infer_immune sls
    else
        _infer_immune sls
    fi

    # ARM64 silicon errata (speculation/security-relevant, no CVE assignments).
    # References: arch/arm64/Kconfig (ARM64_ERRATUM_*), arch/arm64/kernel/cpu_errata.c MIDR lists.
    # Iterates per-core (impl, part, variant, revision) tuples. Implementers currently handled:
    #   0x41 ARM Ltd; 0x51 Qualcomm (Kryo4xx Silver for erratum 1530923).
    # Revision ranges mirror the kernel's MIDR_RANGE/MIDR_REV_RANGE/MIDR_REV macros. A variant
    # 'v' and revision 'p' are packed as (v<<4)|p for range compares — equivalent to the kernel's
    # layout (MIDR_VARIANT_SHIFT=20, MIDR_REVISION_MASK=0xf) under the same order semantics.
    # Unknown variant/revision ⇒ treat as in range (whitelist principle, DEVELOPMENT.md rule 5).
    if [ -n "$cpu_part_list" ]; then
        i=0
        for cpupart in $cpu_part_list; do
            i=$((i + 1))
            # shellcheck disable=SC2086
            cpuimpl=$(echo $cpu_impl_list | awk '{print $'$i'}')
            # shellcheck disable=SC2086
            cpuvar=$(echo $cpu_variant_list | awk '{print $'$i'}')
            # shellcheck disable=SC2086
            cpurev=$(echo $cpu_revision_list | awk '{print $'$i'}')
            packed=''
            [ -n "$cpuvar" ] && [ -n "$cpurev" ] && packed=$(((cpuvar << 4) | cpurev))

            # Speculative AT TLB corruption (errata 1165522, 1319367, 1319537, 1530923)
            if [ "$cpuimpl" = 0x41 ]; then
                if echo "$cpupart" | grep -q -w -e 0xd07 -e 0xd08; then
                    # Cortex-A57 (0xd07) / A72 (0xd08): all revisions
                    _set_vuln arm_spec_at
                elif echo "$cpupart" | grep -q -w -e 0xd05 -e 0xd0b; then
                    # Cortex-A55 (0xd05) / A76 (0xd0b): r0p0..r2p0  (packed 0..32)
                    if [ -z "$packed" ] || [ "$packed" -le 32 ]; then
                        _set_vuln arm_spec_at
                    fi
                fi
            elif [ "$cpuimpl" = 0x51 ] && [ "$cpupart" = 0x805 ]; then
                # Qualcomm Kryo4xx Silver: kernel matches MIDR_REV(var 0xd, rev 0xe) only — packed 0xde = 222
                if [ -z "$packed" ] || [ "$packed" = 222 ]; then
                    _set_vuln arm_spec_at
                fi
            fi

            # Speculative unprivileged load (errata 2966298 A520, 3117295 A510) — ARM Ltd only
            if [ "$cpuimpl" = 0x41 ]; then
                if [ "$cpupart" = 0xd46 ]; then
                    # Cortex-A510: all revisions
                    _set_vuln arm_spec_unpriv_load
                elif [ "$cpupart" = 0xd80 ]; then
                    # Cortex-A520: r0p0..r0p1  (packed 0..1)
                    if [ -z "$packed" ] || [ "$packed" -le 1 ]; then
                        _set_vuln arm_spec_unpriv_load
                    fi
                fi
            fi

            # MSR SSBS not self-synchronizing (erratum 3194386 + siblings) — ARM Ltd only, all revisions.
            # A76/A77/A78/A78C/A710/A715/A720/A720AE/A725, X1/X1C/X2/X3/X4/X925, N1/N2/N3, V1/V2/V3/V3AE
            if [ "$cpuimpl" = 0x41 ]; then
                if echo "$cpupart" | grep -q -w \
                    -e 0xd0b -e 0xd0d -e 0xd41 -e 0xd4b \
                    -e 0xd47 -e 0xd4d -e 0xd81 -e 0xd89 -e 0xd87 \
                    -e 0xd44 -e 0xd4c -e 0xd48 -e 0xd4e -e 0xd82 -e 0xd85 \
                    -e 0xd0c -e 0xd49 -e 0xd8e \
                    -e 0xd40 -e 0xd4f -e 0xd84 -e 0xd83; then
                    _set_vuln arm_ssbs_nosync
                fi
            fi
        done
    fi
    # Default everything else to immune (covers non-ARM, and ARM cores not in the affected lists)
    _infer_immune arm_spec_at
    _infer_immune arm_spec_unpriv_load
    _infer_immune arm_ssbs_nosync

    # shellcheck disable=SC2154
    {
        pr_debug "is_cpu_affected: final results: variant1=$affected_variant1 variant2=$affected_variant2 variant3=$affected_variant3 variant3a=$affected_variant3a"
        pr_debug "is_cpu_affected: final results: variant4=$affected_variant4 variantl1tf=$affected_variantl1tf msbds=$affected_msbds mfbds=$affected_mfbds"
        pr_debug "is_cpu_affected: final results: mlpds=$affected_mlpds mdsum=$affected_mdsum taa=$affected_taa itlbmh=$affected_itlbmh srbds=$affected_srbds"
        pr_debug "is_cpu_affected: final results: div0=$affected_div0 fpdss=$affected_fpdss zenbleed=$affected_zenbleed inception=$affected_inception retbleed=$affected_retbleed tsa=$affected_tsa downfall=$affected_downfall reptar=$affected_reptar rfds=$affected_rfds its=$affected_its"
        pr_debug "is_cpu_affected: final results: vmscape=$affected_vmscape bpi=$affected_bpi sls=$affected_sls mmio=$affected_mmio"
        pr_debug "is_cpu_affected: final results: arm_spec_at=$affected_arm_spec_at arm_spec_unpriv_load=$affected_arm_spec_unpriv_load arm_ssbs_nosync=$affected_arm_ssbs_nosync"
    }
    affected_variantl1tf_sgx="$affected_variantl1tf"
    # even if we are affected to L1TF, if there's no SGX, we're not affected to the original foreshadow
    [ "$cap_sgx" = 0 ] && _set_immune variantl1tf_sgx
    pr_debug "is_cpu_affected: variantl1tf_sgx=<$affected_variantl1tf_sgx>"
    g_is_cpu_affected_cached=1
    _is_cpu_affected_cached "$1"
    return $?
}
