# vim: set ts=4 sw=4 sts=4 et:
###############################
# CVE-2023-20569, Inception, SRSO, Return Address Security

check_CVE_2023_20569() {
    check_cve 'CVE-2023-20569'
}

check_CVE_2023_20569_linux() {
    local status sys_interface_available msg kernel_sro kernel_sro_err kernel_srso kernel_ibpb_entry kernel_ibpb_no_ret smt_enabled
    status=UNK
    sys_interface_available=0
    msg=''

    if sys_interface_check "$VULN_SYSFS_BASE/spec_rstack_overflow"; then
        # this kernel has the /sys interface, trust it over everything
        sys_interface_available=1
        status=$ret_sys_interface_check_status
        # kernels before the fix from dc6306ad5b0d (v6.6-rc6, backported to v6.5.6)
        # incorrectly reported "Mitigation: safe RET, no microcode" as mitigated,
        # when in fact userspace is still vulnerable because IBPB doesn't flush
        # branch type predictions without the extending microcode.
        # override the sysfs status in that case.
        if echo "$ret_sys_interface_check_fullmsg" | grep -qi 'Mitigation:.*safe RET.*no microcode'; then
            status=VULN
            msg="Vulnerable: Safe RET, no microcode (your kernel incorrectly reports this as mitigated, it was fixed in more recent kernels)"
        fi
        # kernels before the IBPB_NO_RET fix (v6.12, backported to v6.11.5/v6.6.58/v6.1.114/v5.15.169/v5.10.228)
        # don't fill the RSB after IBPB, so when sysfs reports an IBPB-based mitigation, the return predictor
        # can still be poisoned cross-process (PB-Inception). Override sysfs in that case.
        if [ "$status" = OK ] && echo "$ret_sys_interface_check_fullmsg" | grep -qi 'IBPB'; then
            if [ "$cap_ibpb_ret" != 1 ] && ! grep -q 'ibpb_no_ret' "$g_kernel" 2>/dev/null; then
                status=VULN
                msg="Vulnerable: IBPB-based mitigation active but kernel lacks return prediction clearing after IBPB (PB-Inception, upgrade to kernel 6.12+)"
            fi
        fi
    fi

    if [ "$opt_sysfs_only" != 1 ]; then
        pr_info_nol "* Kernel supports mitigation: "
        if [ -n "$g_kernel_err" ]; then
            kernel_sro_err="$g_kernel_err"
        elif grep -q 'spec_rstack_overflow' "$g_kernel"; then
            kernel_sro="found spec_rstack_overflow in kernel image"
        fi
        if [ -n "$kernel_sro" ]; then
            pstatus green YES "$kernel_sro"
        elif [ -n "$kernel_sro_err" ]; then
            pstatus yellow UNKNOWN "$kernel_sro_err"
        else
            pstatus yellow NO
        fi

        pr_info_nol "* Kernel compiled with SRSO support: "
        if [ -r "$opt_config" ]; then
            # CONFIG_CPU_SRSO: Linux < 6.9
            # CONFIG_MITIGATION_SRSO: Linux >= 6.9
            if grep -Eq '^CONFIG_(CPU|MITIGATION)_SRSO=y' "$opt_config"; then
                pstatus green YES
                kernel_srso="CONFIG_(CPU|MITIGATION)_SRSO=y found in kernel config"
            else
                pstatus yellow NO "required for safe RET and ibpb_on_vmexit mitigations"
            fi
        else
            # https://github.com/torvalds/linux/commit/138bcddb86d8a4f842e4ed6f0585abc9b1a764ff#diff-17bd24a7a7850613cced545790ac30646097e8d6207348c2bd1845f397acb390R2313
            if [ -n "$g_kernel_err" ]; then
                pstatus yellow UNKNOWN "$g_kernel_err"
            elif grep -Eq 'WARNING: kernel not compiled with (CPU|MITIGATION)_SRSO' "$g_kernel"; then
                # this msg is optimized out at compile time if the option is not enabled, see commit referenced above
                # if it's present, then SRSO is NOT compiled in
                pstatus yellow NO "kernel not compiled with (CPU|MITIGATION)_SRSO"
            else
                # if it's not present, then SRSO is compiled in IF kernel_sro is set, otherwise we're just
                # in front of an old kernel that doesn't have the mitigation logic at all
                if [ -n "$kernel_sro" ]; then
                    kernel_srso="SRSO mitigation logic is compiled in the kernel"
                    pstatus green OK "$kernel_srso"
                else
                    pstatus yellow NO "your kernel is too old and doesn't have the mitigation logic"
                fi
            fi
        fi

        # check whether the running kernel has the corrected SRSO reporting
        # (dc6306ad5b0d, v6.6-rc6, backported to v6.5.6): kernels with the fix
        # contain the string "Vulnerable: Safe RET, no microcode" in their image,
        # while older kernels only have "safe RET" (and append ", no microcode" dynamically).
        pr_info_nol "* Kernel has accurate SRSO reporting: "
        if [ -n "$g_kernel_err" ]; then
            pstatus yellow UNKNOWN "$g_kernel_err"
        elif grep -q 'Vulnerable: Safe RET, no microcode' "$g_kernel"; then
            pstatus green YES
        elif [ -n "$kernel_sro" ]; then
            pstatus yellow NO "your kernel reports partial SRSO mitigations as fully mitigated, upgrade recommended"
        else
            pstatus yellow NO "your kernel is too old and doesn't have the SRSO mitigation logic"
        fi

        pr_info_nol "* Kernel compiled with IBPB_ENTRY support: "
        if [ -r "$opt_config" ]; then
            # CONFIG_CPU_IBPB_ENTRY: Linux < 6.9
            # CONFIG_MITIGATION_IBPB_ENTRY: Linux >= 6.9
            if grep -Eq '^CONFIG_(CPU|MITIGATION)_IBPB_ENTRY=y' "$opt_config"; then
                pstatus green YES
                kernel_ibpb_entry="CONFIG_(CPU|MITIGATION)_IBPB_ENTRY=y found in kernel config"
            else
                pstatus yellow NO
            fi
        else
            # https://github.com/torvalds/linux/commit/138bcddb86d8a4f842e4ed6f0585abc9b1a764ff#diff-17bd24a7a7850613cced545790ac30646097e8d6207348c2bd1845f397acb390R2325
            if [ -n "$g_kernel_err" ]; then
                pstatus yellow UNKNOWN "$g_kernel_err"
            elif grep -Eq 'WARNING: kernel not compiled with (CPU|MITIGATION)_IBPB_ENTRY' "$g_kernel"; then
                # this msg is optimized out at compile time if the option is not enabled, see commit referenced above
                # if it's present, then IBPB_ENTRY is NOT compiled in
                pstatus yellow NO "kernel not compiled with (CPU|MITIGATION)_IBPB_ENTRY"
            else
                # if it's not present, then IBPB_ENTRY is compiled in IF kernel_sro is set, otherwise we're just
                # in front of an old kernel that doesn't have the mitigation logic at all
                if [ -n "$kernel_sro" ]; then
                    kernel_ibpb_entry="IBPB_ENTRY mitigation logic is compiled in the kernel"
                    pstatus green OK "$kernel_ibpb_entry"
                else
                    pstatus yellow NO "your kernel is too old and doesn't have the mitigation logic"
                fi
            fi
        fi

        # check whether the kernel is aware of the IBPB return predictor bypass (PB-Inception).
        # kernels with the fix (v6.12+, backported) contain the "ibpb_no_ret" bug flag string,
        # and add an RSB fill after every IBPB on affected CPUs (Zen 1-3).
        pr_info_nol "* Kernel is aware of IBPB return predictor bypass: "
        if [ -n "$g_kernel_err" ]; then
            pstatus yellow UNKNOWN "$g_kernel_err"
        elif grep -q 'ibpb_no_ret' "$g_kernel"; then
            kernel_ibpb_no_ret="ibpb_no_ret found in kernel image"
            pstatus green YES "$kernel_ibpb_no_ret"
        else
            pstatus yellow NO
        fi

        # Zen & Zen2 : if the right IBPB microcode applied + SMT off --> not vuln
        if [ "$cpu_family" = $((0x17)) ]; then
            pr_info_nol "* CPU supports IBPB: "
            if [ -n "$cap_ibpb" ]; then
                pstatus green YES "$cap_ibpb"
            else
                pstatus yellow NO
            fi

            pr_info_nol "* Hyper-Threading (SMT) is enabled: "
            is_cpu_smt_enabled
            smt_enabled=$?
            if [ "$smt_enabled" = 0 ]; then
                pstatus yellow YES
            else
                pstatus green NO
            fi
        # Zen 3/4 microcode brings SBPB mitigation
        elif [ "$cpu_family" = $((0x19)) ]; then
            pr_info_nol "* CPU supports SBPB: "
            if [ "$cap_sbpb" = 1 ]; then
                pstatus green YES
            elif [ "$cap_sbpb" = 3 ]; then
                pstatus yellow UNKNOWN "cannot write MSR, rerun with --allow-msr-write"
            else
                pstatus yellow 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 msg is empty, sysfs check didn't fill it, rely on our own test
        if [ "$opt_sysfs_only" != 1 ]; then
            # Zen/Zen2
            if [ "$cpu_family" = $((0x17)) ]; then
                if [ "$smt_enabled" = 0 ]; then
                    pvulnstatus "$cve" VULN "SMT is enabled on your Zen/Zen2 CPU, which makes mitigation ineffective"
                    explain "For Zen/Zen2 CPUs, proper mitigation needs an up to date microcode, and SMT needs to be disabled (this can be done by adding \`nosmt\` to your kernel command line)"
                elif [ -z "$kernel_sro" ]; then
                    pvulnstatus "$cve" VULN "Your kernel is too old and doesn't have the SRSO mitigation logic"
                elif [ -n "$cap_ibpb" ]; then
                    if [ "$cap_ibpb_ret" != 1 ] && [ -z "$kernel_ibpb_no_ret" ]; then
                        pvulnstatus "$cve" VULN "IBPB alone doesn't flush return predictions on this CPU, kernel update needed (PB-Inception, fixed in 6.12+)"
                    else
                        pvulnstatus "$cve" OK "SMT is disabled and both your kernel and microcode support mitigation"
                    fi
                else
                    pvulnstatus "$cve" VULN "Your microcode is too old"
                fi
            # Zen3/Zen4
            elif [ "$cpu_family" = $((0x19)) ]; then
                if [ -z "$kernel_sro" ]; then
                    pvulnstatus "$cve" VULN "Your kernel is too old and doesn't have the SRSO mitigation logic"
                elif [ -z "$kernel_srso" ] && [ -z "$kernel_ibpb_entry" ]; then
                    pvulnstatus "$cve" VULN "Your kernel doesn't have either SRSO or IBPB_ENTRY compiled-in"
                elif [ "$cap_sbpb" = 3 ]; then
                    pvulnstatus "$cve" UNK "Couldn't verify if your microcode supports IBPB (rerun with --allow-msr-write)"
                elif [ "$cap_sbpb" = 2 ]; then
                    pvulnstatus "$cve" VULN "Your microcode doesn't support SBPB"
                else
                    if [ "$cap_ibpb_ret" != 1 ] && [ -z "$kernel_ibpb_no_ret" ] && [ -n "$kernel_ibpb_entry" ]; then
                        pvulnstatus "$cve" VULN "IBPB alone doesn't flush return predictions on this CPU, kernel update needed (PB-Inception, fixed in 6.12+)"
                    else
                        pvulnstatus "$cve" OK "Your kernel and microcode both support mitigation"
                    fi
                fi
            else
                # not supposed to happen, as normally this CPU should not be affected and not run this code
                pvulnstatus "$cve" OK "your CPU vendor reported your CPU model as not affected"
            fi
        else
            pvulnstatus "$cve" "$status" "$ret_sys_interface_check_fullmsg"
        fi
    else
        pvulnstatus "$cve" "$status" "$msg"
        if echo "$msg" | grep -qi 'your kernel incorrectly reports this as mitigated'; then
            explain "Your kernel's /sys interface reports 'Mitigation: safe RET, no microcode' for the SRSO vulnerability.\n" \
                "This was a bug in the kernel's reporting (fixed in v6.5.6/v6.6-rc6, commit dc6306ad5b0d):\n" \
                "the Safe RET mitigation alone only protects the kernel from userspace attacks, but without\n" \
                "the IBPB-extending microcode, userspace itself remains vulnerable because IBPB doesn't flush\n" \
                "branch type predictions. Newer kernels correctly report this as 'Vulnerable: Safe RET, no microcode'.\n" \
                "To fully mitigate, you need both the Safe RET kernel support AND an updated CPU microcode.\n" \
                "Updating your kernel to v6.5.6+ or v6.6+ will also give you accurate vulnerability reporting."
        fi
    fi
}

check_CVE_2023_20569_bsd() {
    if ! is_cpu_affected "$cve"; then
        pvulnstatus "$cve" OK "your CPU vendor reported your CPU model as not affected"
    else
        pvulnstatus "$cve" UNK "your CPU is affected, but mitigation detection has not yet been implemented for BSD in this script"
    fi
}
