# vim: set ts=4 sw=4 sts=4 et:
###############################
# CVE-2018-3639, Variant 4, SSB, Speculative Store Bypass

check_CVE_2018_3639() {
    check_cve 'CVE-2018-3639'
}

check_CVE_2018_3639_linux() {
    local status sys_interface_available msg kernel_ssb kernel_ssbd_enabled mitigated_processes
    status=UNK
    sys_interface_available=0
    msg=''
    if sys_interface_check "$VULN_SYSFS_BASE/spec_store_bypass"; then
        # this kernel has the /sys interface, trust it over everything
        sys_interface_available=1
        status=$ret_sys_interface_check_status
    fi
    if [ "$opt_sysfs_only" != 1 ]; then
        pr_info_nol "* Kernel supports disabling speculative store bypass (SSB): "
        if [ "$g_mode" = live ]; then
            if grep -Eq 'Speculation.?Store.?Bypass:' "$g_procfs/self/status" 2>/dev/null; then
                kernel_ssb="found in $g_procfs/self/status"
                pr_debug "found Speculation.Store.Bypass: in $g_procfs/self/status"
            fi
        fi
        # spec_store_bypass is x86-specific; ARM kernels use ARM64_SSBD instead
        if [ -z "$kernel_ssb" ] && [ -n "$g_kernel" ] && is_x86_kernel; then
            kernel_ssb=$("${opt_arch_prefix}strings" "$g_kernel" | grep spec_store_bypass | head -n1)
            [ -n "$kernel_ssb" ] && kernel_ssb="found $kernel_ssb in kernel"
        fi
        # spec_store_bypass is x86-specific; ARM kernels use ARM64_SSBD instead
        if [ -z "$kernel_ssb" ] && [ -n "$opt_map" ] && is_x86_kernel; then
            kernel_ssb=$(grep spec_store_bypass "$opt_map" | awk '{print $3}' | head -n1)
            [ -n "$kernel_ssb" ] && kernel_ssb="found $kernel_ssb in System.map"
        fi
        # arm64 only:
        if [ -z "$kernel_ssb" ] && [ -n "$opt_map" ]; then
            kernel_ssb=$(grep -w cpu_enable_ssbs "$opt_map" | awk '{print $3}' | head -n1)
            [ -n "$kernel_ssb" ] && kernel_ssb="found $kernel_ssb in System.map"
        fi
        if [ -z "$kernel_ssb" ] && [ -n "$opt_config" ]; then
            kernel_ssb=$(grep -w 'CONFIG_ARM64_SSBD=y' "$opt_config")
            [ -n "$kernel_ssb" ] && kernel_ssb="CONFIG_ARM64_SSBD enabled in kconfig"
        fi
        if [ -z "$kernel_ssb" ] && [ -n "$g_kernel" ]; then
            # this string only appears in kernel if CONFIG_ARM64_SSBD is set
            kernel_ssb=$(grep -w "Speculative Store Bypassing Safe (SSBS)" "$g_kernel")
            [ -n "$kernel_ssb" ] && kernel_ssb="found 'Speculative Store Bypassing Safe (SSBS)' in kernel"
        fi
        # /arm64 only

        if [ -n "$kernel_ssb" ]; then
            pstatus green YES "$kernel_ssb"
        else
            pstatus yellow NO
        fi

        kernel_ssbd_enabled=-1
        if [ "$g_mode" = live ]; then
            # https://elixir.bootlin.com/linux/v5.0/source/fs/proc/array.c#L340
            pr_info_nol "* SSB mitigation is enabled and active: "
            if grep -Eq 'Speculation.?Store.?Bypass:[[:space:]]+thread' "$g_procfs/self/status" 2>/dev/null; then
                kernel_ssbd_enabled=1
                pstatus green YES "per-thread through prctl"
            elif grep -Eq 'Speculation.?Store.?Bypass:[[:space:]]+globally mitigated' "$g_procfs/self/status" 2>/dev/null; then
                kernel_ssbd_enabled=2
                pstatus green YES "global"
            elif grep -Eq 'Speculation.?Store.?Bypass:[[:space:]]+vulnerable' "$g_procfs/self/status" 2>/dev/null; then
                kernel_ssbd_enabled=0
                pstatus yellow NO
            elif grep -Eq 'Speculation.?Store.?Bypass:[[:space:]]+not vulnerable' "$g_procfs/self/status" 2>/dev/null; then
                kernel_ssbd_enabled=-2
                pstatus blue NO "not vulnerable"
            elif grep -Eq 'Speculation.?Store.?Bypass:[[:space:]]+unknown' "$g_procfs/self/status" 2>/dev/null; then
                kernel_ssbd_enabled=0
                pstatus blue NO
            else
                pstatus blue UNKNOWN "unknown value: $(grep -E 'Speculation.?Store.?Bypass:' "$g_procfs/self/status" 2>/dev/null | cut -d: -f2-)"
            fi

            if [ "$kernel_ssbd_enabled" = 1 ]; then
                pr_info_nol "* SSB mitigation currently active for selected processes: "
                # silence grep's stderr here to avoid ENOENT errors from processes that have exited since the shell's expansion of the *
                mitigated_processes=$(find /proc -mindepth 2 -maxdepth 2 -type f -name status -print0 2>/dev/null |
                    xargs -r0 grep -El 'Speculation.?Store.?Bypass:[[:space:]]+thread (force )?mitigated' 2>/dev/null |
                    sed s/status/exe/ | xargs -r -n1 readlink -f 2>/dev/null | xargs -r -n1 basename | sort -u | tr "\n" " " | sed 's/ $//')
                if [ -n "$mitigated_processes" ]; then
                    pstatus green YES "$mitigated_processes"
                else
                    pstatus yellow NO "no process found using SSB mitigation through prctl"
                fi
            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" ] || [ "$msg" = "Vulnerable" ]; then
        # if msg is empty, sysfs check didn't fill it, rely on our own test
        if [ -n "$cap_ssbd" ]; then
            if [ -n "$kernel_ssb" ]; then
                if [ "$g_mode" = live ]; then
                    if [ "$kernel_ssbd_enabled" -gt 0 ]; then
                        pvulnstatus "$cve" OK "your CPU and kernel both support SSBD and mitigation is enabled"
                    else
                        pvulnstatus "$cve" VULN "your CPU and kernel both support SSBD but the mitigation is not active"
                    fi
                else
                    pvulnstatus "$cve" OK "your system provides the necessary tools for software mitigation"
                fi
            else
                pvulnstatus "$cve" VULN "your kernel needs to be updated"
                explain "You have a recent-enough CPU microcode but your kernel is too old to use the new features exported by your CPU's microcode. If you're using a distro kernel, upgrade your distro to get the latest kernel available. Otherwise, recompile the kernel from recent-enough sources."
            fi
        else
            if [ -n "$kernel_ssb" ]; then
                if is_arm_kernel; then
                    pvulnstatus "$cve" VULN "no SSB mitigation is active on your system"
                    explain "ARM CPUs mitigate SSB either through a hardware SSBS bit (ARMv8.5+ CPUs) or through firmware support for SMCCC ARCH_WORKAROUND_2. Your kernel reports SSB status but neither mechanism appears to be active. For CPUs predating ARMv8.5 (such as Cortex-A57 or Cortex-A72), check with your board or SoC vendor for a firmware update that provides SMCCC ARCH_WORKAROUND_2 support."
                else
                    pvulnstatus "$cve" VULN "Your CPU doesn't support SSBD"
                    explain "Your kernel is recent enough to use the CPU microcode features for mitigation, but your CPU microcode doesn't actually provide the necessary features for the kernel to use. The microcode of your CPU hence needs to be upgraded. This is usually done at boot time by your kernel (the upgrade is not persistent across reboots which is why it's done at each boot). If you're using a distro, make sure you are up to date, as microcode updates are usually shipped alongside with the distro kernel. Availability of a microcode update for you CPU model depends on your CPU vendor. You can usually find out online if a microcode update is available for your CPU by searching for your CPUID (indicated in the Hardware Check section)."
                fi
            else
                if is_arm_kernel; then
                    pvulnstatus "$cve" VULN "your kernel and firmware do not support SSB mitigation"
                    explain "ARM SSB mitigation requires kernel support (CONFIG_ARM64_SSBD) combined with either a hardware SSBS bit (ARMv8.5+ CPUs) or firmware support for SMCCC ARCH_WORKAROUND_2. Ensure you are running a recent kernel compiled with CONFIG_ARM64_SSBD. For CPUs predating ARMv8.5, also check with your board or SoC vendor for a firmware update providing SMCCC ARCH_WORKAROUND_2 support."
                else
                    pvulnstatus "$cve" VULN "Neither your CPU nor your kernel support SSBD"
                    explain "Both your CPU microcode and your kernel are lacking support for mitigation. If you're using a distro kernel, upgrade your distro to get the latest kernel available. Otherwise, recompile the kernel from recent-enough sources. The microcode of your CPU also needs to be upgraded. This is usually done at boot time by your kernel (the upgrade is not persistent across reboots which is why it's done at each boot). If you're using a distro, make sure you are up to date, as microcode updates are usually shipped alongside with the distro kernel. Availability of a microcode update for you CPU model depends on your CPU vendor. You can usually find out online if a microcode update is available for your CPU by searching for your CPUID (indicated in the Hardware Check section)."
                fi
            fi
        fi
    else
        pvulnstatus "$cve" "$status" "$msg"
    fi
}

check_CVE_2018_3639_bsd() {
    local kernel_ssb ssb_enabled ssb_active
    pr_info_nol "* Kernel supports speculation store bypass: "
    if sysctl hw.spec_store_bypass_disable >/dev/null 2>&1; then
        kernel_ssb=1
        pstatus green YES
    else
        kernel_ssb=0
        pstatus yellow NO
    fi

    pr_info_nol "* Speculation store bypass is administratively enabled: "
    ssb_enabled=$(sysctl -n hw.spec_store_bypass_disable 2>/dev/null)
    pr_debug "hw.spec_store_bypass_disable=$ssb_enabled"
    case "$ssb_enabled" in
        0) pstatus yellow NO "disabled" ;;
        1) pstatus green YES "enabled" ;;
        2) pstatus green YES "auto mode" ;;
        *) pstatus yellow NO "unavailable" ;;
    esac

    pr_info_nol "* Speculation store bypass is currently active: "
    ssb_active=$(sysctl -n hw.spec_store_bypass_disable_active 2>/dev/null)
    pr_debug "hw.spec_store_bypass_disable_active=$ssb_active"
    case "$ssb_active" in
        1) pstatus green YES ;;
        *) pstatus yellow NO ;;
    esac

    if ! is_cpu_affected "$cve"; then
        pvulnstatus "$cve" OK "your CPU vendor reported your CPU model as not affected"
    else
        if [ "$ssb_active" = 1 ]; then
            pvulnstatus "$cve" OK "SSBD mitigates the vulnerability"
        elif [ -n "$cap_ssbd" ]; then
            if [ "$kernel_ssb" = 1 ]; then
                pvulnstatus "$cve" VULN "you need to enable SSBD through sysctl to mitigate the vulnerability"
                explain "To enable SSBD right now, you can run \`sysctl hw.spec_store_bypass_disable=2'. To make this change persistent across reboots, you can add 'sysctl hw.spec_store_bypass_disable=2' to /etc/sysctl.conf."
            else
                pvulnstatus "$cve" VULN "your kernel needs to be updated"
            fi
        else
            if [ "$kernel_ssb" = 1 ]; then
                pvulnstatus "$cve" VULN "Your CPU doesn't support SSBD"
            else
                pvulnstatus "$cve" VULN "Neither your CPU nor your kernel support SSBD"
            fi
        fi
    fi
}
