# vim: set ts=4 sw=4 sts=4 et: ############################### # CVE-2017-5753, Spectre V1, Bounds Check Bypass # Sets: (none directly, delegates to check_cve) check_CVE_2017_5753() { check_cve 'CVE-2017-5753' } # Sets: g_redhat_canonical_spectre (via check_redhat_canonical_spectre) check_CVE_2017_5753_linux() { local status sys_interface_available msg v1_kernel_mitigated v1_kernel_mitigated_err v1_mask_nospec ret explain_text status=UNK sys_interface_available=0 msg='' if sys_interface_check "$VULN_SYSFS_BASE/spectre_v1"; then # this kernel has the /sys interface, trust it over everything # v0.33+: don't. some kernels have backported the array_index_mask_nospec() workaround without # modifying the vulnerabilities/spectre_v1 file. that's bad. we can't trust it when it says Vulnerable :( # see "silent backport" detection at the bottom of this func sys_interface_available=1 # # Complete sysfs message inventory for spectre_v1, traced via git blame: # # all versions: # "Not affected" (cpu_show_common, pre-existing) # # --- x86 mainline --- # 61dc0f555b5c (v4.15, initial spectre_v1 sysfs): # "Vulnerable" # edfbae53dab8 (v4.16, report get_user mitigation): # "Mitigation: __user pointer sanitization" # a2059825986a (v5.3, swapgs awareness via spectre_v1_strings[]): # "Vulnerable: __user pointer sanitization and usercopy barriers only; no swapgs barriers" # "Mitigation: usercopy/swapgs barriers and __user pointer sanitization" # ca01c0d8d030 (v6.12, CONFIG_MITIGATION_SPECTRE_V1 controls default): # same strings as v5.3+ # All stable branches (4.4.y through 6.12.y) have v5.3+ strings backported. # # --- x86 RHEL (centos6, centos7 branches) --- # "Vulnerable: Load fences, __user pointer sanitization and usercopy barriers only; no swapgs barriers" # "Mitigation: Load fences, usercopy/swapgs barriers and __user pointer sanitization" # # --- ARM64 --- # 3891ebccace1 (v5.2, first arm64 spectre_v1 sysfs, backported to 4.14.y+): # "Mitigation: __user pointer sanitization" (hardcoded) # 455697adefdb (v5.10, moved to proton-pack.c): # same string # Before v5.2: no sysfs override (generic "Not affected" fallback). # Actual mitigation (array_index_mask_nospec with CSDB) landed in v4.16. # # --- ARM32 --- # 9dd78194a372 (v5.17+): # "Mitigation: __user pointer sanitization" (hardcoded) # # all messages start with either "Not affected", "Mitigation", or "Vulnerable" status=$ret_sys_interface_check_status fi if [ "$opt_sysfs_only" != 1 ]; then # no /sys interface (or no-runtime mode), fallback to our own ways # Primary detection: grep for sysfs mitigation strings in the kernel binary. # The string "__user pointer sanitization" is present in all kernel versions # that have spectre_v1 sysfs support (x86 v4.16+, ARM64 v5.2+, ARM32 v5.17+), # including RHEL "Load fences" variants. This is cheap and works offline. pr_info_nol "* Kernel has spectre_v1 mitigation (kernel image): " v1_kernel_mitigated='' v1_kernel_mitigated_err='' if [ -n "$g_kernel_err" ]; then v1_kernel_mitigated_err="$g_kernel_err" elif grep -q '__user pointer sanitization' "$g_kernel"; then if grep -q 'usercopy/swapgs barriers' "$g_kernel"; then v1_kernel_mitigated="usercopy/swapgs barriers and target sanitization" elif grep -q 'Load fences' "$g_kernel"; then v1_kernel_mitigated="RHEL Load fences mitigation" else v1_kernel_mitigated="__user pointer sanitization" fi fi if [ -z "$v1_kernel_mitigated" ] && [ -r "$opt_config" ]; then if grep -q '^CONFIG_MITIGATION_SPECTRE_V1=y' "$opt_config"; then v1_kernel_mitigated="CONFIG_MITIGATION_SPECTRE_V1 found in kernel config" fi fi if [ -z "$v1_kernel_mitigated" ] && [ -n "$opt_map" ]; then if grep -q 'spectre_v1_select_mitigation' "$opt_map"; then v1_kernel_mitigated="found spectre_v1_select_mitigation in System.map" fi fi if [ -n "$v1_kernel_mitigated" ]; then pstatus green YES "$v1_kernel_mitigated" elif [ -n "$v1_kernel_mitigated_err" ]; then pstatus yellow UNKNOWN "couldn't check ($v1_kernel_mitigated_err)" else pstatus yellow NO fi # Fallback for v4.15-era kernels: binary pattern matching for array_index_mask_nospec(). # The sysfs mitigation strings were not present in the kernel image until v4.16 (x86) # and v5.2 (ARM64), but the actual mitigation code landed in v4.15 (x86) and v4.16 (ARM64). # For offline analysis of these old kernels, match the specific instruction patterns. if [ -z "$v1_kernel_mitigated" ]; then pr_info_nol "* Kernel has array_index_mask_nospec (v4.15 binary pattern): " # vanilla: look for the Linus' mask aka array_index_mask_nospec() # that is inlined at least in raw_copy_from_user (__get_user_X symbols) #mov PER_CPU_VAR(current_task), %_ASM_DX #cmp TASK_addr_limit(%_ASM_DX),%_ASM_AX #jae bad_get_user # /* array_index_mask_nospec() are the 2 opcodes that follow */ #+sbb %_ASM_DX, %_ASM_DX #+and %_ASM_DX, %_ASM_AX #ASM_STAC # x86 64bits: jae(0x0f 0x83 0x?? 0x?? 0x?? 0x??) sbb(0x48 0x19 0xd2) and(0x48 0x21 0xd0) # x86 32bits: cmp(0x3b 0x82 0x?? 0x?? 0x00 0x00) jae(0x73 0x??) sbb(0x19 0xd2) and(0x21 0xd0) # # arm32 ##ifdef CONFIG_THUMB2_KERNEL ##define CSDB ".inst.w 0xf3af8014" ##else ##define CSDB ".inst 0xe320f014" e320f014 ##endif #asm volatile( # "cmp %1, %2\n" e1500003 #" sbc %0, %1, %1\n" e0c03000 #CSDB #: "=r" (mask) #: "r" (idx), "Ir" (sz) #: "cc"); # # http://git.arm.linux.org.uk/cgit/linux-arm.git/commit/?h=spectre&id=a78d156587931a2c3b354534aa772febf6c9e855 v1_mask_nospec='' if [ -n "$g_kernel_err" ]; then pstatus yellow UNKNOWN "couldn't check ($g_kernel_err)" elif ! command -v perl >/dev/null 2>&1; then pstatus yellow UNKNOWN "missing 'perl' binary, please install it" else perl -ne '/\x0f\x83....\x48\x19\xd2\x48\x21\xd0/ and $found++; END { exit($found ? 0 : 1) }' "$g_kernel" ret=$? if [ "$ret" -eq 0 ]; then pstatus green YES "x86 64 bits array_index_mask_nospec()" v1_mask_nospec="x86 64 bits array_index_mask_nospec" else perl -ne '/\x3b\x82..\x00\x00\x73.\x19\xd2\x21\xd0/ and $found++; END { exit($found ? 0 : 1) }' "$g_kernel" ret=$? if [ "$ret" -eq 0 ]; then pstatus green YES "x86 32 bits array_index_mask_nospec()" v1_mask_nospec="x86 32 bits array_index_mask_nospec" else ret=$("${opt_arch_prefix}objdump" "$g_objdump_options" "$g_kernel" | grep -w -e f3af8014 -e e320f014 -B2 | grep -B1 -w sbc | grep -w -c cmp) if [ "$ret" -gt 0 ]; then pstatus green YES "$ret occurrence(s) found of arm 32 bits array_index_mask_nospec()" v1_mask_nospec="arm 32 bits array_index_mask_nospec" else pstatus yellow NO fi fi fi fi fi pr_info_nol "* Kernel has the Red Hat/Ubuntu patch: " check_redhat_canonical_spectre if [ "$g_redhat_canonical_spectre" = -1 ]; then pstatus yellow UNKNOWN "missing '${opt_arch_prefix}strings' tool, please install it, usually it's in the binutils package" elif [ "$g_redhat_canonical_spectre" = -2 ]; then pstatus yellow UNKNOWN "couldn't check ($g_kernel_err)" elif [ "$g_redhat_canonical_spectre" = 1 ]; then pstatus green YES elif [ "$g_redhat_canonical_spectre" = 2 ]; then pstatus green YES "but without IBRS" else pstatus yellow NO fi pr_info_nol "* Kernel has mask_nospec64 (arm64): " #.macro mask_nospec64, idx, limit, tmp #sub \tmp, \idx, \limit #bic \tmp, \tmp, \idx #and \idx, \idx, \tmp, asr #63 #csdb #.endm #$ aarch64-linux-gnu-objdump -d vmlinux | grep -w bic -A1 -B1 | grep -w sub -A2 | grep -w and -B2 #ffffff8008082e44: cb190353 sub x19, x26, x25 #ffffff8008082e48: 8a3a0273 bic x19, x19, x26 #ffffff8008082e4c: 8a93ff5a and x26, x26, x19, asr #63 #ffffff8008082e50: d503229f hint #0x14 # /!\ can also just be "csdb" instead of "hint #0x14" for native objdump # # if we already have a detection, don't bother disassembling the kernel, the answer is no. if [ -n "$v1_kernel_mitigated" ] || [ -n "$v1_mask_nospec" ] || [ "$g_redhat_canonical_spectre" -gt 0 ]; then pstatus yellow NO elif [ -n "$g_kernel_err" ]; then pstatus yellow UNKNOWN "couldn't check ($g_kernel_err)" elif ! command -v perl >/dev/null 2>&1; then pstatus yellow UNKNOWN "missing 'perl' binary, please install it" elif ! command -v "${opt_arch_prefix}objdump" >/dev/null 2>&1; then pstatus yellow UNKNOWN "missing '${opt_arch_prefix}objdump' tool, please install it, usually it's in the binutils package" else "${opt_arch_prefix}objdump" "$g_objdump_options" "$g_kernel" | perl -ne 'push @r, $_; /\s(hint|csdb)\s/ && $r[0]=~/\ssub\s+(x\d+)/ && $r[1]=~/\sbic\s+$1,\s+$1,/ && $r[2]=~/\sand\s/ && exit(9); shift @r if @r>3' ret=$? if [ "$ret" -eq 9 ]; then pstatus green YES "mask_nospec64 macro is present and used" v1_mask_nospec="arm64 mask_nospec64" else pstatus yellow NO fi fi pr_info_nol "* Kernel has array_index_nospec (arm64): " # in 4.19+ kernels, the mask_nospec64 asm64 macro is replaced by array_index_nospec, defined in nospec.h, and used in invoke_syscall() # ffffff8008090a4c: 2a0203e2 mov w2, w2 # ffffff8008090a50: eb0200bf cmp x5, x2 # ffffff8008090a54: da1f03e2 ngc x2, xzr # ffffff8008090a58: d503229f hint #0x14 # /!\ can also just be "csdb" instead of "hint #0x14" for native objdump # # if we already have a detection, don't bother disassembling the kernel, the answer is no. if [ -n "$v1_kernel_mitigated" ] || [ -n "$v1_mask_nospec" ] || [ "$g_redhat_canonical_spectre" -gt 0 ]; then pstatus yellow NO elif [ -n "$g_kernel_err" ]; then pstatus yellow UNKNOWN "couldn't check ($g_kernel_err)" elif ! command -v perl >/dev/null 2>&1; then pstatus yellow UNKNOWN "missing 'perl' binary, please install it" elif ! command -v "${opt_arch_prefix}objdump" >/dev/null 2>&1; then pstatus yellow UNKNOWN "missing '${opt_arch_prefix}objdump' tool, please install it, usually it's in the binutils package" else "${opt_arch_prefix}objdump" "$g_objdump_options" "$g_kernel" | perl -ne 'push @r, $_; /\s(hint|csdb)\s/ && $r[0]=~/\smov\s+(w\d+),\s+(w\d+)/ && $r[1]=~/\scmp\s+(x\d+),\s+(x\d+)/ && $r[2]=~/\sngc\s+$2,/ && exit(9); shift @r if @r>3' ret=$? if [ "$ret" -eq 9 ]; then pstatus green YES "array_index_nospec macro is present and used" v1_mask_nospec="arm64 array_index_nospec" else pstatus yellow NO fi fi elif [ "$sys_interface_available" = 0 ]; then msg="/sys vulnerability interface use forced, but it's not available!" status=UNK fi # report status 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 if [ -n "$v1_kernel_mitigated" ]; then pvulnstatus "$cve" OK "Kernel source has been patched to mitigate the vulnerability ($v1_kernel_mitigated)" elif [ -n "$v1_mask_nospec" ]; then pvulnstatus "$cve" OK "Kernel source has been patched to mitigate the vulnerability ($v1_mask_nospec)" elif [ "$g_redhat_canonical_spectre" = 1 ] || [ "$g_redhat_canonical_spectre" = 2 ]; then pvulnstatus "$cve" OK "Kernel source has been patched to mitigate the vulnerability (Red Hat/Ubuntu patch)" elif [ -n "$g_kernel_err" ]; then pvulnstatus "$cve" UNK "Couldn't find kernel image or tools missing to execute the checks" explain "Re-run this script with root privileges, after installing the missing tools indicated above" else pvulnstatus "$cve" VULN "Kernel source needs to be patched to mitigate the vulnerability" explain "Your kernel is too old to have the mitigation for Variant 1, you should upgrade to a newer kernel. If you're using a Linux distro and didn't compile the kernel yourself, you should upgrade your distro to get a newer kernel." fi else pvulnstatus "$cve" "$status" "$ret_sys_interface_check_fullmsg" fi else if [ "$msg" = "Vulnerable" ] && { [ -n "$v1_kernel_mitigated" ] || [ -n "$v1_mask_nospec" ]; }; then pvulnstatus "$cve" OK "Kernel source has been patched to mitigate the vulnerability (silent backport of spectre_v1 mitigation)" else if [ "$msg" = "Vulnerable" ]; then msg="Kernel source needs to be patched to mitigate the vulnerability" explain_text="Your kernel is too old to have the mitigation for Variant 1, you should upgrade to a newer kernel. If you're using a Linux distro and didn't compile the kernel yourself, you should upgrade your distro to get a newer kernel." fi pvulnstatus "$cve" "$status" "$msg" [ -n "${explain_text:-}" ] && explain "$explain_text" unset explain_text fi fi } check_CVE_2017_5753_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 }