Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge commit 'its-for-linus-20250509-merge' into x86/core, to resolve conflicts

Conflicts:
Documentation/admin-guide/hw-vuln/index.rst
arch/x86/include/asm/cpufeatures.h
arch/x86/kernel/alternative.c
arch/x86/kernel/cpu/bugs.c
arch/x86/kernel/cpu/common.c
drivers/base/cpu.c
include/linux/cpu.h

Signed-off-by: Ingo Molnar <mingo@kernel.org>

Ingo Molnar c4070e19 7d40efd6

+1641 -55
+1
Documentation/ABI/testing/sysfs-devices-system-cpu
··· 511 511 512 512 What: /sys/devices/system/cpu/vulnerabilities 513 513 /sys/devices/system/cpu/vulnerabilities/gather_data_sampling 514 + /sys/devices/system/cpu/vulnerabilities/indirect_target_selection 514 515 /sys/devices/system/cpu/vulnerabilities/itlb_multihit 515 516 /sys/devices/system/cpu/vulnerabilities/l1tf 516 517 /sys/devices/system/cpu/vulnerabilities/mds
+1
Documentation/admin-guide/hw-vuln/index.rst
··· 24 24 reg-file-data-sampling 25 25 rsb 26 26 old_microcode 27 + indirect-target-selection
+168
Documentation/admin-guide/hw-vuln/indirect-target-selection.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0 2 + 3 + Indirect Target Selection (ITS) 4 + =============================== 5 + 6 + ITS is a vulnerability in some Intel CPUs that support Enhanced IBRS and were 7 + released before Alder Lake. ITS may allow an attacker to control the prediction 8 + of indirect branches and RETs located in the lower half of a cacheline. 9 + 10 + ITS is assigned CVE-2024-28956 with a CVSS score of 4.7 (Medium). 11 + 12 + Scope of Impact 13 + --------------- 14 + - **eIBRS Guest/Host Isolation**: Indirect branches in KVM/kernel may still be 15 + predicted with unintended target corresponding to a branch in the guest. 16 + 17 + - **Intra-Mode BTI**: In-kernel training such as through cBPF or other native 18 + gadgets. 19 + 20 + - **Indirect Branch Prediction Barrier (IBPB)**: After an IBPB, indirect 21 + branches may still be predicted with targets corresponding to direct branches 22 + executed prior to the IBPB. This is fixed by the IPU 2025.1 microcode, which 23 + should be available via distro updates. Alternatively microcode can be 24 + obtained from Intel's github repository [#f1]_. 25 + 26 + Affected CPUs 27 + ------------- 28 + Below is the list of ITS affected CPUs [#f2]_ [#f3]_: 29 + 30 + ======================== ============ ==================== =============== 31 + Common name Family_Model eIBRS Intra-mode BTI 32 + Guest/Host Isolation 33 + ======================== ============ ==================== =============== 34 + SKYLAKE_X (step >= 6) 06_55H Affected Affected 35 + ICELAKE_X 06_6AH Not affected Affected 36 + ICELAKE_D 06_6CH Not affected Affected 37 + ICELAKE_L 06_7EH Not affected Affected 38 + TIGERLAKE_L 06_8CH Not affected Affected 39 + TIGERLAKE 06_8DH Not affected Affected 40 + KABYLAKE_L (step >= 12) 06_8EH Affected Affected 41 + KABYLAKE (step >= 13) 06_9EH Affected Affected 42 + COMETLAKE 06_A5H Affected Affected 43 + COMETLAKE_L 06_A6H Affected Affected 44 + ROCKETLAKE 06_A7H Not affected Affected 45 + ======================== ============ ==================== =============== 46 + 47 + - All affected CPUs enumerate Enhanced IBRS feature. 48 + - IBPB isolation is affected on all ITS affected CPUs, and need a microcode 49 + update for mitigation. 50 + - None of the affected CPUs enumerate BHI_CTRL which was introduced in Golden 51 + Cove (Alder Lake and Sapphire Rapids). This can help guests to determine the 52 + host's affected status. 53 + - Intel Atom CPUs are not affected by ITS. 54 + 55 + Mitigation 56 + ---------- 57 + As only the indirect branches and RETs that have their last byte of instruction 58 + in the lower half of the cacheline are vulnerable to ITS, the basic idea behind 59 + the mitigation is to not allow indirect branches in the lower half. 60 + 61 + This is achieved by relying on existing retpoline support in the kernel, and in 62 + compilers. ITS-vulnerable retpoline sites are runtime patched to point to newly 63 + added ITS-safe thunks. These safe thunks consists of indirect branch in the 64 + second half of the cacheline. Not all retpoline sites are patched to thunks, if 65 + a retpoline site is evaluated to be ITS-safe, it is replaced with an inline 66 + indirect branch. 67 + 68 + Dynamic thunks 69 + ~~~~~~~~~~~~~~ 70 + From a dynamically allocated pool of safe-thunks, each vulnerable site is 71 + replaced with a new thunk, such that they get a unique address. This could 72 + improve the branch prediction accuracy. Also, it is a defense-in-depth measure 73 + against aliasing. 74 + 75 + Note, for simplicity, indirect branches in eBPF programs are always replaced 76 + with a jump to a static thunk in __x86_indirect_its_thunk_array. If required, 77 + in future this can be changed to use dynamic thunks. 78 + 79 + All vulnerable RETs are replaced with a static thunk, they do not use dynamic 80 + thunks. This is because RETs get their prediction from RSB mostly that does not 81 + depend on source address. RETs that underflow RSB may benefit from dynamic 82 + thunks. But, RETs significantly outnumber indirect branches, and any benefit 83 + from a unique source address could be outweighed by the increased icache 84 + footprint and iTLB pressure. 85 + 86 + Retpoline 87 + ~~~~~~~~~ 88 + Retpoline sequence also mitigates ITS-unsafe indirect branches. For this 89 + reason, when retpoline is enabled, ITS mitigation only relocates the RETs to 90 + safe thunks. Unless user requested the RSB-stuffing mitigation. 91 + 92 + RSB Stuffing 93 + ~~~~~~~~~~~~ 94 + RSB-stuffing via Call Depth Tracking is a mitigation for Retbleed RSB-underflow 95 + attacks. And it also mitigates RETs that are vulnerable to ITS. 96 + 97 + Mitigation in guests 98 + ^^^^^^^^^^^^^^^^^^^^ 99 + All guests deploy ITS mitigation by default, irrespective of eIBRS enumeration 100 + and Family/Model of the guest. This is because eIBRS feature could be hidden 101 + from a guest. One exception to this is when a guest enumerates BHI_DIS_S, which 102 + indicates that the guest is running on an unaffected host. 103 + 104 + To prevent guests from unnecessarily deploying the mitigation on unaffected 105 + platforms, Intel has defined ITS_NO bit(62) in MSR IA32_ARCH_CAPABILITIES. When 106 + a guest sees this bit set, it should not enumerate the ITS bug. Note, this bit 107 + is not set by any hardware, but is **intended for VMMs to synthesize** it for 108 + guests as per the host's affected status. 109 + 110 + Mitigation options 111 + ^^^^^^^^^^^^^^^^^^ 112 + The ITS mitigation can be controlled using the "indirect_target_selection" 113 + kernel parameter. The available options are: 114 + 115 + ======== =================================================================== 116 + on (default) Deploy the "Aligned branch/return thunks" mitigation. 117 + If spectre_v2 mitigation enables retpoline, aligned-thunks are only 118 + deployed for the affected RET instructions. Retpoline mitigates 119 + indirect branches. 120 + 121 + off Disable ITS mitigation. 122 + 123 + vmexit Equivalent to "=on" if the CPU is affected by guest/host isolation 124 + part of ITS. Otherwise, mitigation is not deployed. This option is 125 + useful when host userspace is not in the threat model, and only 126 + attacks from guest to host are considered. 127 + 128 + stuff Deploy RSB-fill mitigation when retpoline is also deployed. 129 + Otherwise, deploy the default mitigation. When retpoline mitigation 130 + is enabled, RSB-stuffing via Call-Depth-Tracking also mitigates 131 + ITS. 132 + 133 + force Force the ITS bug and deploy the default mitigation. 134 + ======== =================================================================== 135 + 136 + Sysfs reporting 137 + --------------- 138 + 139 + The sysfs file showing ITS mitigation status is: 140 + 141 + /sys/devices/system/cpu/vulnerabilities/indirect_target_selection 142 + 143 + Note, microcode mitigation status is not reported in this file. 144 + 145 + The possible values in this file are: 146 + 147 + .. list-table:: 148 + 149 + * - Not affected 150 + - The processor is not vulnerable. 151 + * - Vulnerable 152 + - System is vulnerable and no mitigation has been applied. 153 + * - Vulnerable, KVM: Not affected 154 + - System is vulnerable to intra-mode BTI, but not affected by eIBRS 155 + guest/host isolation. 156 + * - Mitigation: Aligned branch/return thunks 157 + - The mitigation is enabled, affected indirect branches and RETs are 158 + relocated to safe thunks. 159 + * - Mitigation: Retpolines, Stuffing RSB 160 + - The mitigation is enabled using retpoline and RSB stuffing. 161 + 162 + References 163 + ---------- 164 + .. [#f1] Microcode repository - https://github.com/intel/Intel-Linux-Processor-Microcode-Data-Files 165 + 166 + .. [#f2] Affected Processors list - https://www.intel.com/content/www/us/en/developer/topic-technology/software-security-guidance/processors-affected-consolidated-product-cpu-model.html 167 + 168 + .. [#f3] Affected Processors list (machine readable) - https://github.com/intel/Intel-affected-processor-list
+18
Documentation/admin-guide/kernel-parameters.txt
··· 2202 2202 different crypto accelerators. This option can be used 2203 2203 to achieve best performance for particular HW. 2204 2204 2205 + indirect_target_selection= [X86,Intel] Mitigation control for Indirect 2206 + Target Selection(ITS) bug in Intel CPUs. Updated 2207 + microcode is also required for a fix in IBPB. 2208 + 2209 + on: Enable mitigation (default). 2210 + off: Disable mitigation. 2211 + force: Force the ITS bug and deploy default 2212 + mitigation. 2213 + vmexit: Only deploy mitigation if CPU is affected by 2214 + guest/host isolation part of ITS. 2215 + stuff: Deploy RSB-fill mitigation when retpoline is 2216 + also deployed. Otherwise, deploy the default 2217 + mitigation. 2218 + 2219 + For details see: 2220 + Documentation/admin-guide/hw-vuln/indirect-target-selection.rst 2221 + 2205 2222 init= [KNL] 2206 2223 Format: <full_path> 2207 2224 Run specified binary instead of /sbin/init as init ··· 3710 3693 expose users to several CPU vulnerabilities. 3711 3694 Equivalent to: if nokaslr then kpti=0 [ARM64] 3712 3695 gather_data_sampling=off [X86] 3696 + indirect_target_selection=off [X86] 3713 3697 kvm.nx_huge_pages=off [X86] 3714 3698 l1tf=off [X86] 3715 3699 mds=off [X86]
+12
arch/x86/Kconfig
··· 2712 2712 of speculative execution in a similar way to the Meltdown and Spectre 2713 2713 security vulnerabilities. 2714 2714 2715 + config MITIGATION_ITS 2716 + bool "Enable Indirect Target Selection mitigation" 2717 + depends on CPU_SUP_INTEL && X86_64 2718 + depends on MITIGATION_RETPOLINE && MITIGATION_RETHUNK 2719 + select EXECMEM 2720 + default y 2721 + help 2722 + Enable Indirect Target Selection (ITS) mitigation. ITS is a bug in 2723 + BPU on some Intel CPUs that may allow Spectre V2 style attacks. If 2724 + disabled, mitigation cannot be enabled via cmdline. 2725 + See <file:Documentation/admin-guide/hw-vuln/indirect-target-selection.rst> 2726 + 2715 2727 endif 2716 2728 2717 2729 config ARCH_HAS_ADD_PAGES
+17 -3
arch/x86/entry/entry_64.S
··· 1525 1525 * ORC to unwind properly. 1526 1526 * 1527 1527 * The alignment is for performance and not for safety, and may be safely 1528 - * refactored in the future if needed. 1528 + * refactored in the future if needed. The .skips are for safety, to ensure 1529 + * that all RETs are in the second half of a cacheline to mitigate Indirect 1530 + * Target Selection, rather than taking the slowpath via its_return_thunk. 1529 1531 */ 1530 1532 SYM_FUNC_START(clear_bhb_loop) 1531 1533 ANNOTATE_NOENDBR ··· 1538 1536 call 1f 1539 1537 jmp 5f 1540 1538 .align 64, 0xcc 1539 + /* 1540 + * Shift instructions so that the RET is in the upper half of the 1541 + * cacheline and don't take the slowpath to its_return_thunk. 1542 + */ 1543 + .skip 32 - (.Lret1 - 1f), 0xcc 1541 1544 ANNOTATE_INTRA_FUNCTION_CALL 1542 1545 1: call 2f 1543 - RET 1546 + .Lret1: RET 1544 1547 .align 64, 0xcc 1548 + /* 1549 + * As above shift instructions for RET at .Lret2 as well. 1550 + * 1551 + * This should be ideally be: .skip 32 - (.Lret2 - 2f), 0xcc 1552 + * but some Clang versions (e.g. 18) don't like this. 1553 + */ 1554 + .skip 32 - 18, 0xcc 1545 1555 2: movl $5, %eax 1546 1556 3: jmp 4f 1547 1557 nop ··· 1561 1547 jnz 3b 1562 1548 sub $1, %ecx 1563 1549 jnz 1b 1564 - RET 1550 + .Lret2: RET 1565 1551 5: lfence 1566 1552 pop %rbp 1567 1553 RET
+32
arch/x86/include/asm/alternative.h
··· 6 6 #include <linux/stringify.h> 7 7 #include <linux/objtool.h> 8 8 #include <asm/asm.h> 9 + #include <asm/bug.h> 9 10 10 11 #define ALT_FLAGS_SHIFT 16 11 12 ··· 128 127 void *func, void *ip) 129 128 { 130 129 return 0; 130 + } 131 + #endif 132 + 133 + #ifdef CONFIG_MITIGATION_ITS 134 + extern void its_init_mod(struct module *mod); 135 + extern void its_fini_mod(struct module *mod); 136 + extern void its_free_mod(struct module *mod); 137 + extern u8 *its_static_thunk(int reg); 138 + #else /* CONFIG_MITIGATION_ITS */ 139 + static inline void its_init_mod(struct module *mod) { } 140 + static inline void its_fini_mod(struct module *mod) { } 141 + static inline void its_free_mod(struct module *mod) { } 142 + static inline u8 *its_static_thunk(int reg) 143 + { 144 + WARN_ONCE(1, "ITS not compiled in"); 145 + 146 + return NULL; 147 + } 148 + #endif 149 + 150 + #if defined(CONFIG_MITIGATION_RETHUNK) && defined(CONFIG_OBJTOOL) 151 + extern bool cpu_wants_rethunk(void); 152 + extern bool cpu_wants_rethunk_at(void *addr); 153 + #else 154 + static __always_inline bool cpu_wants_rethunk(void) 155 + { 156 + return false; 157 + } 158 + static __always_inline bool cpu_wants_rethunk_at(void *addr) 159 + { 160 + return false; 131 161 } 132 162 #endif 133 163
+3
arch/x86/include/asm/cpufeatures.h
··· 482 482 #define X86_FEATURE_AMD_WORKLOAD_CLASS (21*32+ 7) /* Workload Classification */ 483 483 #define X86_FEATURE_PREFER_YMM (21*32+ 8) /* Avoid ZMM registers due to downclocking */ 484 484 #define X86_FEATURE_APX (21*32+ 9) /* Advanced Performance Extensions */ 485 + #define X86_FEATURE_INDIRECT_THUNK_ITS (21*32+10) /* Use thunk for indirect branches in lower half of cacheline */ 485 486 486 487 /* 487 488 * BUG word(s) ··· 536 535 #define X86_BUG_IBPB_NO_RET X86_BUG( 1*32+ 4) /* "ibpb_no_ret" IBPB omits return target predictions */ 537 536 #define X86_BUG_SPECTRE_V2_USER X86_BUG( 1*32+ 5) /* "spectre_v2_user" CPU is affected by Spectre variant 2 attack between user processes */ 538 537 #define X86_BUG_OLD_MICROCODE X86_BUG( 1*32+ 6) /* "old_microcode" CPU has old microcode, it is surely vulnerable to something */ 538 + #define X86_BUG_ITS X86_BUG( 1*32+ 7) /* "its" CPU is affected by Indirect Target Selection */ 539 + #define X86_BUG_ITS_NATIVE_ONLY X86_BUG( 1*32+ 8) /* "its_native_only" CPU is affected by ITS, VMX is not affected */ 539 540 540 541 #endif /* _ASM_X86_CPUFEATURES_H */
+8
arch/x86/include/asm/msr-index.h
··· 211 211 * VERW clears CPU Register 212 212 * File. 213 213 */ 214 + #define ARCH_CAP_ITS_NO BIT_ULL(62) /* 215 + * Not susceptible to 216 + * Indirect Target Selection. 217 + * This bit is not set by 218 + * HW, but is synthesized by 219 + * VMMs for guests to know 220 + * their affected status. 221 + */ 214 222 215 223 #define MSR_IA32_FLUSH_CMD 0x0000010b 216 224 #define L1D_FLUSH BIT(0) /*
+10
arch/x86/include/asm/nospec-branch.h
··· 336 336 337 337 #else /* __ASSEMBLER__ */ 338 338 339 + #define ITS_THUNK_SIZE 64 340 + 339 341 typedef u8 retpoline_thunk_t[RETPOLINE_THUNK_SIZE]; 342 + typedef u8 its_thunk_t[ITS_THUNK_SIZE]; 340 343 extern retpoline_thunk_t __x86_indirect_thunk_array[]; 341 344 extern retpoline_thunk_t __x86_indirect_call_thunk_array[]; 342 345 extern retpoline_thunk_t __x86_indirect_jump_thunk_array[]; 346 + extern its_thunk_t __x86_indirect_its_thunk_array[]; 343 347 344 348 #ifdef CONFIG_MITIGATION_RETHUNK 345 349 extern void __x86_return_thunk(void); ··· 365 361 #else 366 362 static inline void srso_return_thunk(void) {} 367 363 static inline void srso_alias_return_thunk(void) {} 364 + #endif 365 + 366 + #ifdef CONFIG_MITIGATION_ITS 367 + extern void its_return_thunk(void); 368 + #else 369 + static inline void its_return_thunk(void) {} 368 370 #endif 369 371 370 372 extern void retbleed_return_thunk(void);
+316 -20
arch/x86/kernel/alternative.c
··· 5 5 #include <linux/perf_event.h> 6 6 #include <linux/vmalloc.h> 7 7 #include <linux/memory.h> 8 + #include <linux/execmem.h> 8 9 9 10 #include <asm/text-patching.h> 10 11 #include <asm/insn.h> 12 + #include <asm/ibt.h> 13 + #include <asm/set_memory.h> 11 14 #include <asm/nmi.h> 12 15 13 16 int __read_mostly alternatives_patched; ··· 104 101 x86nops + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10, 105 102 #endif 106 103 }; 104 + 105 + #ifdef CONFIG_FINEIBT 106 + static bool cfi_paranoid __ro_after_init; 107 + #endif 108 + 109 + #ifdef CONFIG_MITIGATION_ITS 110 + 111 + static struct module *its_mod; 112 + static void *its_page; 113 + static unsigned int its_offset; 114 + 115 + /* Initialize a thunk with the "jmp *reg; int3" instructions. */ 116 + static void *its_init_thunk(void *thunk, int reg) 117 + { 118 + u8 *bytes = thunk; 119 + int offset = 0; 120 + int i = 0; 121 + 122 + #ifdef CONFIG_FINEIBT 123 + if (cfi_paranoid) { 124 + /* 125 + * When ITS uses indirect branch thunk the fineibt_paranoid 126 + * caller sequence doesn't fit in the caller site. So put the 127 + * remaining part of the sequence (<ea> + JNE) into the ITS 128 + * thunk. 129 + */ 130 + bytes[i++] = 0xea; /* invalid instruction */ 131 + bytes[i++] = 0x75; /* JNE */ 132 + bytes[i++] = 0xfd; 133 + 134 + offset = 1; 135 + } 136 + #endif 137 + 138 + if (reg >= 8) { 139 + bytes[i++] = 0x41; /* REX.B prefix */ 140 + reg -= 8; 141 + } 142 + bytes[i++] = 0xff; 143 + bytes[i++] = 0xe0 + reg; /* jmp *reg */ 144 + bytes[i++] = 0xcc; 145 + 146 + return thunk + offset; 147 + } 148 + 149 + void its_init_mod(struct module *mod) 150 + { 151 + if (!cpu_feature_enabled(X86_FEATURE_INDIRECT_THUNK_ITS)) 152 + return; 153 + 154 + mutex_lock(&text_mutex); 155 + its_mod = mod; 156 + its_page = NULL; 157 + } 158 + 159 + void its_fini_mod(struct module *mod) 160 + { 161 + if (!cpu_feature_enabled(X86_FEATURE_INDIRECT_THUNK_ITS)) 162 + return; 163 + 164 + WARN_ON_ONCE(its_mod != mod); 165 + 166 + its_mod = NULL; 167 + its_page = NULL; 168 + mutex_unlock(&text_mutex); 169 + 170 + for (int i = 0; i < mod->its_num_pages; i++) { 171 + void *page = mod->its_page_array[i]; 172 + execmem_restore_rox(page, PAGE_SIZE); 173 + } 174 + } 175 + 176 + void its_free_mod(struct module *mod) 177 + { 178 + if (!cpu_feature_enabled(X86_FEATURE_INDIRECT_THUNK_ITS)) 179 + return; 180 + 181 + for (int i = 0; i < mod->its_num_pages; i++) { 182 + void *page = mod->its_page_array[i]; 183 + execmem_free(page); 184 + } 185 + kfree(mod->its_page_array); 186 + } 187 + 188 + static void *its_alloc(void) 189 + { 190 + void *page __free(execmem) = execmem_alloc(EXECMEM_MODULE_TEXT, PAGE_SIZE); 191 + 192 + if (!page) 193 + return NULL; 194 + 195 + if (its_mod) { 196 + void *tmp = krealloc(its_mod->its_page_array, 197 + (its_mod->its_num_pages+1) * sizeof(void *), 198 + GFP_KERNEL); 199 + if (!tmp) 200 + return NULL; 201 + 202 + its_mod->its_page_array = tmp; 203 + its_mod->its_page_array[its_mod->its_num_pages++] = page; 204 + 205 + execmem_make_temp_rw(page, PAGE_SIZE); 206 + } 207 + 208 + return no_free_ptr(page); 209 + } 210 + 211 + static void *its_allocate_thunk(int reg) 212 + { 213 + int size = 3 + (reg / 8); 214 + void *thunk; 215 + 216 + #ifdef CONFIG_FINEIBT 217 + /* 218 + * The ITS thunk contains an indirect jump and an int3 instruction so 219 + * its size is 3 or 4 bytes depending on the register used. If CFI 220 + * paranoid is used then 3 extra bytes are added in the ITS thunk to 221 + * complete the fineibt_paranoid caller sequence. 222 + */ 223 + if (cfi_paranoid) 224 + size += 3; 225 + #endif 226 + 227 + if (!its_page || (its_offset + size - 1) >= PAGE_SIZE) { 228 + its_page = its_alloc(); 229 + if (!its_page) { 230 + pr_err("ITS page allocation failed\n"); 231 + return NULL; 232 + } 233 + memset(its_page, INT3_INSN_OPCODE, PAGE_SIZE); 234 + its_offset = 32; 235 + } 236 + 237 + /* 238 + * If the indirect branch instruction will be in the lower half 239 + * of a cacheline, then update the offset to reach the upper half. 240 + */ 241 + if ((its_offset + size - 1) % 64 < 32) 242 + its_offset = ((its_offset - 1) | 0x3F) + 33; 243 + 244 + thunk = its_page + its_offset; 245 + its_offset += size; 246 + 247 + return its_init_thunk(thunk, reg); 248 + } 249 + 250 + u8 *its_static_thunk(int reg) 251 + { 252 + u8 *thunk = __x86_indirect_its_thunk_array[reg]; 253 + 254 + #ifdef CONFIG_FINEIBT 255 + /* Paranoid thunk starts 2 bytes before */ 256 + if (cfi_paranoid) 257 + return thunk - 2; 258 + #endif 259 + return thunk; 260 + } 261 + 262 + #endif 107 263 108 264 /* 109 265 * Nomenclature for variable names to simplify and clarify this code and ease ··· 714 552 return i; 715 553 } 716 554 717 - static int emit_call_track_retpoline(void *addr, struct insn *insn, int reg, u8 *bytes) 555 + static int __emit_trampoline(void *addr, struct insn *insn, u8 *bytes, 556 + void *call_dest, void *jmp_dest) 718 557 { 719 558 u8 op = insn->opcode.bytes[0]; 720 559 int i = 0; ··· 736 573 switch (op) { 737 574 case CALL_INSN_OPCODE: 738 575 __text_gen_insn(bytes+i, op, addr+i, 739 - __x86_indirect_call_thunk_array[reg], 576 + call_dest, 740 577 CALL_INSN_SIZE); 741 578 i += CALL_INSN_SIZE; 742 579 break; ··· 744 581 case JMP32_INSN_OPCODE: 745 582 clang_jcc: 746 583 __text_gen_insn(bytes+i, op, addr+i, 747 - __x86_indirect_jump_thunk_array[reg], 584 + jmp_dest, 748 585 JMP32_INSN_SIZE); 749 586 i += JMP32_INSN_SIZE; 750 587 break; ··· 758 595 759 596 return i; 760 597 } 598 + 599 + static int emit_call_track_retpoline(void *addr, struct insn *insn, int reg, u8 *bytes) 600 + { 601 + return __emit_trampoline(addr, insn, bytes, 602 + __x86_indirect_call_thunk_array[reg], 603 + __x86_indirect_jump_thunk_array[reg]); 604 + } 605 + 606 + #ifdef CONFIG_MITIGATION_ITS 607 + static int emit_its_trampoline(void *addr, struct insn *insn, int reg, u8 *bytes) 608 + { 609 + u8 *thunk = __x86_indirect_its_thunk_array[reg]; 610 + u8 *tmp = its_allocate_thunk(reg); 611 + 612 + if (tmp) 613 + thunk = tmp; 614 + 615 + return __emit_trampoline(addr, insn, bytes, thunk, thunk); 616 + } 617 + 618 + /* Check if an indirect branch is at ITS-unsafe address */ 619 + static bool cpu_wants_indirect_its_thunk_at(unsigned long addr, int reg) 620 + { 621 + if (!cpu_feature_enabled(X86_FEATURE_INDIRECT_THUNK_ITS)) 622 + return false; 623 + 624 + /* Indirect branch opcode is 2 or 3 bytes depending on reg */ 625 + addr += 1 + reg / 8; 626 + 627 + /* Lower-half of the cacheline? */ 628 + return !(addr & 0x20); 629 + } 630 + #else /* CONFIG_MITIGATION_ITS */ 631 + 632 + #ifdef CONFIG_FINEIBT 633 + static bool cpu_wants_indirect_its_thunk_at(unsigned long addr, int reg) 634 + { 635 + return false; 636 + } 637 + #endif 638 + 639 + #endif /* CONFIG_MITIGATION_ITS */ 761 640 762 641 /* 763 642 * Rewrite the compiler generated retpoline thunk calls. ··· 875 670 bytes[i++] = 0xe8; /* LFENCE */ 876 671 } 877 672 673 + #ifdef CONFIG_MITIGATION_ITS 674 + /* 675 + * Check if the address of last byte of emitted-indirect is in 676 + * lower-half of the cacheline. Such branches need ITS mitigation. 677 + */ 678 + if (cpu_wants_indirect_its_thunk_at((unsigned long)addr + i, reg)) 679 + return emit_its_trampoline(addr, insn, reg, bytes); 680 + #endif 681 + 878 682 ret = emit_indirect(op, reg, bytes + i); 879 683 if (ret < 0) 880 684 return ret; ··· 917 703 int len, ret; 918 704 u8 bytes[16]; 919 705 u8 op1, op2; 706 + u8 *dest; 920 707 921 708 ret = insn_decode_kernel(&insn, addr); 922 709 if (WARN_ON_ONCE(ret < 0)) ··· 934 719 935 720 case CALL_INSN_OPCODE: 936 721 case JMP32_INSN_OPCODE: 722 + /* Check for cfi_paranoid + ITS */ 723 + dest = addr + insn.length + insn.immediate.value; 724 + if (dest[-1] == 0xea && (dest[0] & 0xf0) == 0x70) { 725 + WARN_ON_ONCE(cfi_mode != CFI_FINEIBT); 726 + continue; 727 + } 937 728 break; 938 729 939 730 case 0x0f: /* escape */ ··· 967 746 968 747 #ifdef CONFIG_MITIGATION_RETHUNK 969 748 749 + bool cpu_wants_rethunk(void) 750 + { 751 + return cpu_feature_enabled(X86_FEATURE_RETHUNK); 752 + } 753 + 754 + bool cpu_wants_rethunk_at(void *addr) 755 + { 756 + if (!cpu_feature_enabled(X86_FEATURE_RETHUNK)) 757 + return false; 758 + if (x86_return_thunk != its_return_thunk) 759 + return true; 760 + 761 + return !((unsigned long)addr & 0x20); 762 + } 763 + 970 764 /* 971 765 * Rewrite the compiler generated return thunk tail-calls. 972 766 * ··· 998 762 int i = 0; 999 763 1000 764 /* Patch the custom return thunks... */ 1001 - if (cpu_feature_enabled(X86_FEATURE_RETHUNK)) { 765 + if (cpu_wants_rethunk_at(addr)) { 1002 766 i = JMP32_INSN_SIZE; 1003 767 __text_gen_insn(bytes, JMP32_INSN_OPCODE, addr, x86_return_thunk, i); 1004 768 } else { ··· 1015 779 { 1016 780 s32 *s; 1017 781 1018 - if (cpu_feature_enabled(X86_FEATURE_RETHUNK)) 782 + if (cpu_wants_rethunk()) 1019 783 static_call_force_reinit(); 1020 784 1021 785 for (s = start; s < end; s++) { ··· 1228 992 1229 993 static bool cfi_rand __ro_after_init = true; 1230 994 static u32 cfi_seed __ro_after_init; 1231 - 1232 - static bool cfi_paranoid __ro_after_init = false; 1233 995 1234 996 /* 1235 997 * Re-hash the CFI hash with a boot-time seed while making sure the result is ··· 1641 1407 return 0; 1642 1408 } 1643 1409 1410 + static int emit_paranoid_trampoline(void *addr, struct insn *insn, int reg, u8 *bytes) 1411 + { 1412 + u8 *thunk = (void *)__x86_indirect_its_thunk_array[reg] - 2; 1413 + 1414 + #ifdef CONFIG_MITIGATION_ITS 1415 + u8 *tmp = its_allocate_thunk(reg); 1416 + if (tmp) 1417 + thunk = tmp; 1418 + #endif 1419 + 1420 + return __emit_trampoline(addr, insn, bytes, thunk, thunk); 1421 + } 1422 + 1644 1423 static int cfi_rewrite_callers(s32 *start, s32 *end) 1645 1424 { 1646 1425 s32 *s; ··· 1695 1448 memcpy(bytes, fineibt_paranoid_start, fineibt_paranoid_size); 1696 1449 memcpy(bytes + fineibt_caller_hash, &hash, 4); 1697 1450 1698 - ret = emit_indirect(op, 11, bytes + fineibt_paranoid_ind); 1699 - if (WARN_ON_ONCE(ret != 3)) 1700 - continue; 1451 + if (cpu_wants_indirect_its_thunk_at((unsigned long)addr + fineibt_paranoid_ind, 11)) { 1452 + emit_paranoid_trampoline(addr + fineibt_caller_size, 1453 + &insn, 11, bytes + fineibt_caller_size); 1454 + } else { 1455 + ret = emit_indirect(op, 11, bytes + fineibt_paranoid_ind); 1456 + if (WARN_ON_ONCE(ret != 3)) 1457 + continue; 1458 + } 1701 1459 1702 1460 text_poke_early(addr, bytes, fineibt_paranoid_size); 1703 1461 } ··· 1929 1677 return false; 1930 1678 } 1931 1679 1680 + static bool is_paranoid_thunk(unsigned long addr) 1681 + { 1682 + u32 thunk; 1683 + 1684 + __get_kernel_nofault(&thunk, (u32 *)addr, u32, Efault); 1685 + return (thunk & 0x00FFFFFF) == 0xfd75ea; 1686 + 1687 + Efault: 1688 + return false; 1689 + } 1690 + 1932 1691 /* 1933 1692 * regs->ip points to a LOCK Jcc.d8 instruction from the fineibt_paranoid_start[] 1934 - * sequence. 1693 + * sequence, or to an invalid instruction (0xea) + Jcc.d8 for cfi_paranoid + ITS 1694 + * thunk. 1935 1695 */ 1936 1696 static bool decode_fineibt_paranoid(struct pt_regs *regs, unsigned long *target, u32 *type) 1937 1697 { 1938 1698 unsigned long addr = regs->ip - fineibt_paranoid_ud; 1939 - u32 hash; 1940 1699 1941 - if (!cfi_paranoid || !is_cfi_trap(addr + fineibt_caller_size - LEN_UD2)) 1700 + if (!cfi_paranoid) 1942 1701 return false; 1943 1702 1944 - __get_kernel_nofault(&hash, addr + fineibt_caller_hash, u32, Efault); 1945 - *target = regs->r11 + fineibt_preamble_size; 1946 - *type = regs->r10; 1703 + if (is_cfi_trap(addr + fineibt_caller_size - LEN_UD2)) { 1704 + *target = regs->r11 + fineibt_preamble_size; 1705 + *type = regs->r10; 1706 + 1707 + /* 1708 + * Since the trapping instruction is the exact, but LOCK prefixed, 1709 + * Jcc.d8 that got us here, the normal fixup will work. 1710 + */ 1711 + return true; 1712 + } 1947 1713 1948 1714 /* 1949 - * Since the trapping instruction is the exact, but LOCK prefixed, 1950 - * Jcc.d8 that got us here, the normal fixup will work. 1715 + * The cfi_paranoid + ITS thunk combination results in: 1716 + * 1717 + * 0: 41 ba 78 56 34 12 mov $0x12345678, %r10d 1718 + * 6: 45 3b 53 f7 cmp -0x9(%r11), %r10d 1719 + * a: 4d 8d 5b f0 lea -0x10(%r11), %r11 1720 + * e: 2e e8 XX XX XX XX cs call __x86_indirect_paranoid_thunk_r11 1721 + * 1722 + * Where the paranoid_thunk looks like: 1723 + * 1724 + * 1d: <ea> (bad) 1725 + * __x86_indirect_paranoid_thunk_r11: 1726 + * 1e: 75 fd jne 1d 1727 + * __x86_indirect_its_thunk_r11: 1728 + * 20: 41 ff eb jmp *%r11 1729 + * 23: cc int3 1730 + * 1951 1731 */ 1952 - return true; 1732 + if (is_paranoid_thunk(regs->ip)) { 1733 + *target = regs->r11 + fineibt_preamble_size; 1734 + *type = regs->r10; 1953 1735 1954 - Efault: 1736 + regs->ip = *target; 1737 + return true; 1738 + } 1739 + 1955 1740 return false; 1956 1741 } 1957 1742 ··· 2291 2002 2292 2003 void __init alternative_instructions(void) 2293 2004 { 2005 + u64 ibt; 2006 + 2294 2007 int3_selftest(); 2295 2008 2296 2009 /* ··· 2319 2028 */ 2320 2029 paravirt_set_cap(); 2321 2030 2031 + /* Keep CET-IBT disabled until caller/callee are patched */ 2032 + ibt = ibt_save(/*disable*/ true); 2033 + 2322 2034 __apply_fineibt(__retpoline_sites, __retpoline_sites_end, 2323 2035 __cfi_sites, __cfi_sites_end, true); 2324 2036 ··· 2344 2050 * Seal all functions that do not have their address taken. 2345 2051 */ 2346 2052 apply_seal_endbr(__ibt_endbr_seal, __ibt_endbr_seal_end); 2053 + 2054 + ibt_restore(ibt); 2347 2055 2348 2056 #ifdef CONFIG_SMP 2349 2057 /* Patch to UP if other cpus not imminent. */
+169 -7
arch/x86/kernel/cpu/bugs.c
··· 91 91 static void __init bhi_select_mitigation(void); 92 92 static void __init bhi_update_mitigation(void); 93 93 static void __init bhi_apply_mitigation(void); 94 + static void __init its_select_mitigation(void); 94 95 95 96 /* The base value of the SPEC_CTRL MSR without task-specific bits set */ 96 97 u64 x86_spec_ctrl_base; ··· 108 107 static DEFINE_MUTEX(spec_ctrl_mutex); 109 108 110 109 void (*x86_return_thunk)(void) __ro_after_init = __x86_return_thunk; 110 + 111 + static void __init set_return_thunk(void *thunk) 112 + { 113 + if (x86_return_thunk != __x86_return_thunk) 114 + pr_warn("x86/bugs: return thunk changed\n"); 115 + 116 + x86_return_thunk = thunk; 117 + } 111 118 112 119 /* Update SPEC_CTRL MSR and its cached copy unconditionally */ 113 120 static void update_spec_ctrl(u64 val) ··· 221 212 l1d_flush_select_mitigation(); 222 213 srso_select_mitigation(); 223 214 gds_select_mitigation(); 215 + its_select_mitigation(); 224 216 bhi_select_mitigation(); 225 217 226 218 /* ··· 1288 1278 setup_force_cpu_cap(X86_FEATURE_RETHUNK); 1289 1279 setup_force_cpu_cap(X86_FEATURE_UNRET); 1290 1280 1291 - x86_return_thunk = retbleed_return_thunk; 1281 + set_return_thunk(retbleed_return_thunk); 1292 1282 1293 1283 if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD && 1294 1284 boot_cpu_data.x86_vendor != X86_VENDOR_HYGON) ··· 1323 1313 setup_force_cpu_cap(X86_FEATURE_RETHUNK); 1324 1314 setup_force_cpu_cap(X86_FEATURE_CALL_DEPTH); 1325 1315 1326 - x86_return_thunk = call_depth_return_thunk; 1316 + set_return_thunk(call_depth_return_thunk); 1327 1317 break; 1328 1318 1329 1319 default: ··· 1333 1323 if (mitigate_smt && !boot_cpu_has(X86_FEATURE_STIBP) && 1334 1324 (retbleed_nosmt || cpu_mitigations_auto_nosmt())) 1335 1325 cpu_smt_disable(false); 1326 + } 1327 + 1328 + #undef pr_fmt 1329 + #define pr_fmt(fmt) "ITS: " fmt 1330 + 1331 + enum its_mitigation_cmd { 1332 + ITS_CMD_OFF, 1333 + ITS_CMD_ON, 1334 + ITS_CMD_VMEXIT, 1335 + ITS_CMD_RSB_STUFF, 1336 + }; 1337 + 1338 + enum its_mitigation { 1339 + ITS_MITIGATION_OFF, 1340 + ITS_MITIGATION_VMEXIT_ONLY, 1341 + ITS_MITIGATION_ALIGNED_THUNKS, 1342 + ITS_MITIGATION_RETPOLINE_STUFF, 1343 + }; 1344 + 1345 + static const char * const its_strings[] = { 1346 + [ITS_MITIGATION_OFF] = "Vulnerable", 1347 + [ITS_MITIGATION_VMEXIT_ONLY] = "Mitigation: Vulnerable, KVM: Not affected", 1348 + [ITS_MITIGATION_ALIGNED_THUNKS] = "Mitigation: Aligned branch/return thunks", 1349 + [ITS_MITIGATION_RETPOLINE_STUFF] = "Mitigation: Retpolines, Stuffing RSB", 1350 + }; 1351 + 1352 + static enum its_mitigation its_mitigation __ro_after_init = ITS_MITIGATION_ALIGNED_THUNKS; 1353 + 1354 + static enum its_mitigation_cmd its_cmd __ro_after_init = 1355 + IS_ENABLED(CONFIG_MITIGATION_ITS) ? ITS_CMD_ON : ITS_CMD_OFF; 1356 + 1357 + static int __init its_parse_cmdline(char *str) 1358 + { 1359 + if (!str) 1360 + return -EINVAL; 1361 + 1362 + if (!IS_ENABLED(CONFIG_MITIGATION_ITS)) { 1363 + pr_err("Mitigation disabled at compile time, ignoring option (%s)", str); 1364 + return 0; 1365 + } 1366 + 1367 + if (!strcmp(str, "off")) { 1368 + its_cmd = ITS_CMD_OFF; 1369 + } else if (!strcmp(str, "on")) { 1370 + its_cmd = ITS_CMD_ON; 1371 + } else if (!strcmp(str, "force")) { 1372 + its_cmd = ITS_CMD_ON; 1373 + setup_force_cpu_bug(X86_BUG_ITS); 1374 + } else if (!strcmp(str, "vmexit")) { 1375 + its_cmd = ITS_CMD_VMEXIT; 1376 + } else if (!strcmp(str, "stuff")) { 1377 + its_cmd = ITS_CMD_RSB_STUFF; 1378 + } else { 1379 + pr_err("Ignoring unknown indirect_target_selection option (%s).", str); 1380 + } 1381 + 1382 + return 0; 1383 + } 1384 + early_param("indirect_target_selection", its_parse_cmdline); 1385 + 1386 + static void __init its_select_mitigation(void) 1387 + { 1388 + enum its_mitigation_cmd cmd = its_cmd; 1389 + 1390 + if (!boot_cpu_has_bug(X86_BUG_ITS) || cpu_mitigations_off()) { 1391 + its_mitigation = ITS_MITIGATION_OFF; 1392 + return; 1393 + } 1394 + 1395 + /* Retpoline+CDT mitigates ITS, bail out */ 1396 + if (boot_cpu_has(X86_FEATURE_RETPOLINE) && 1397 + boot_cpu_has(X86_FEATURE_CALL_DEPTH)) { 1398 + its_mitigation = ITS_MITIGATION_RETPOLINE_STUFF; 1399 + goto out; 1400 + } 1401 + 1402 + /* Exit early to avoid irrelevant warnings */ 1403 + if (cmd == ITS_CMD_OFF) { 1404 + its_mitigation = ITS_MITIGATION_OFF; 1405 + goto out; 1406 + } 1407 + if (spectre_v2_enabled == SPECTRE_V2_NONE) { 1408 + pr_err("WARNING: Spectre-v2 mitigation is off, disabling ITS\n"); 1409 + its_mitigation = ITS_MITIGATION_OFF; 1410 + goto out; 1411 + } 1412 + if (!IS_ENABLED(CONFIG_MITIGATION_RETPOLINE) || 1413 + !IS_ENABLED(CONFIG_MITIGATION_RETHUNK)) { 1414 + pr_err("WARNING: ITS mitigation depends on retpoline and rethunk support\n"); 1415 + its_mitigation = ITS_MITIGATION_OFF; 1416 + goto out; 1417 + } 1418 + if (IS_ENABLED(CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_64B)) { 1419 + pr_err("WARNING: ITS mitigation is not compatible with CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_64B\n"); 1420 + its_mitigation = ITS_MITIGATION_OFF; 1421 + goto out; 1422 + } 1423 + if (boot_cpu_has(X86_FEATURE_RETPOLINE_LFENCE)) { 1424 + pr_err("WARNING: ITS mitigation is not compatible with lfence mitigation\n"); 1425 + its_mitigation = ITS_MITIGATION_OFF; 1426 + goto out; 1427 + } 1428 + 1429 + if (cmd == ITS_CMD_RSB_STUFF && 1430 + (!boot_cpu_has(X86_FEATURE_RETPOLINE) || !IS_ENABLED(CONFIG_MITIGATION_CALL_DEPTH_TRACKING))) { 1431 + pr_err("RSB stuff mitigation not supported, using default\n"); 1432 + cmd = ITS_CMD_ON; 1433 + } 1434 + 1435 + switch (cmd) { 1436 + case ITS_CMD_OFF: 1437 + its_mitigation = ITS_MITIGATION_OFF; 1438 + break; 1439 + case ITS_CMD_VMEXIT: 1440 + if (boot_cpu_has_bug(X86_BUG_ITS_NATIVE_ONLY)) { 1441 + its_mitigation = ITS_MITIGATION_VMEXIT_ONLY; 1442 + goto out; 1443 + } 1444 + fallthrough; 1445 + case ITS_CMD_ON: 1446 + its_mitigation = ITS_MITIGATION_ALIGNED_THUNKS; 1447 + if (!boot_cpu_has(X86_FEATURE_RETPOLINE)) 1448 + setup_force_cpu_cap(X86_FEATURE_INDIRECT_THUNK_ITS); 1449 + setup_force_cpu_cap(X86_FEATURE_RETHUNK); 1450 + set_return_thunk(its_return_thunk); 1451 + break; 1452 + case ITS_CMD_RSB_STUFF: 1453 + its_mitigation = ITS_MITIGATION_RETPOLINE_STUFF; 1454 + setup_force_cpu_cap(X86_FEATURE_RETHUNK); 1455 + setup_force_cpu_cap(X86_FEATURE_CALL_DEPTH); 1456 + set_return_thunk(call_depth_return_thunk); 1457 + if (retbleed_mitigation == RETBLEED_MITIGATION_NONE) { 1458 + retbleed_mitigation = RETBLEED_MITIGATION_STUFF; 1459 + pr_info("Retbleed mitigation updated to stuffing\n"); 1460 + } 1461 + break; 1462 + } 1463 + out: 1464 + pr_info("%s\n", its_strings[its_mitigation]); 1336 1465 } 1337 1466 1338 1467 #undef pr_fmt ··· 2019 1870 return; 2020 1871 } 2021 1872 2022 - /* Mitigate in hardware if supported */ 2023 - if (spec_ctrl_bhi_dis()) 1873 + if (!IS_ENABLED(CONFIG_X86_64)) 2024 1874 return; 2025 1875 2026 - if (!IS_ENABLED(CONFIG_X86_64)) 1876 + /* Mitigate in hardware if supported */ 1877 + if (spec_ctrl_bhi_dis()) 2027 1878 return; 2028 1879 2029 1880 if (bhi_mitigation == BHI_MITIGATION_VMEXIT_ONLY) { ··· 2973 2824 2974 2825 if (boot_cpu_data.x86 == 0x19) { 2975 2826 setup_force_cpu_cap(X86_FEATURE_SRSO_ALIAS); 2976 - x86_return_thunk = srso_alias_return_thunk; 2827 + set_return_thunk(srso_alias_return_thunk); 2977 2828 } else { 2978 2829 setup_force_cpu_cap(X86_FEATURE_SRSO); 2979 - x86_return_thunk = srso_return_thunk; 2830 + set_return_thunk(srso_return_thunk); 2980 2831 } 2981 2832 break; 2982 2833 case SRSO_MITIGATION_IBPB: ··· 3118 2969 return sysfs_emit(buf, "Unknown: running under hypervisor"); 3119 2970 3120 2971 return sysfs_emit(buf, "Vulnerable\n"); 2972 + } 2973 + 2974 + static ssize_t its_show_state(char *buf) 2975 + { 2976 + return sysfs_emit(buf, "%s\n", its_strings[its_mitigation]); 3121 2977 } 3122 2978 3123 2979 static char *stibp_state(void) ··· 3309 3155 case X86_BUG_OLD_MICROCODE: 3310 3156 return old_microcode_show_state(buf); 3311 3157 3158 + case X86_BUG_ITS: 3159 + return its_show_state(buf); 3160 + 3312 3161 default: 3313 3162 break; 3314 3163 } ··· 3392 3235 ssize_t cpu_show_old_microcode(struct device *dev, struct device_attribute *attr, char *buf) 3393 3236 { 3394 3237 return cpu_show_common(dev, attr, buf, X86_BUG_OLD_MICROCODE); 3238 + } 3239 + 3240 + ssize_t cpu_show_indirect_target_selection(struct device *dev, struct device_attribute *attr, char *buf) 3241 + { 3242 + return cpu_show_common(dev, attr, buf, X86_BUG_ITS); 3395 3243 } 3396 3244 #endif 3397 3245
+57 -15
arch/x86/kernel/cpu/common.c
··· 1229 1229 #define GDS BIT(6) 1230 1230 /* CPU is affected by Register File Data Sampling */ 1231 1231 #define RFDS BIT(7) 1232 + /* CPU is affected by Indirect Target Selection */ 1233 + #define ITS BIT(8) 1234 + /* CPU is affected by Indirect Target Selection, but guest-host isolation is not affected */ 1235 + #define ITS_NATIVE_ONLY BIT(9) 1232 1236 1233 1237 static const struct x86_cpu_id cpu_vuln_blacklist[] __initconst = { 1234 1238 VULNBL_INTEL_STEPS(INTEL_IVYBRIDGE, X86_STEP_MAX, SRBDS), ··· 1244 1240 VULNBL_INTEL_STEPS(INTEL_BROADWELL_G, X86_STEP_MAX, SRBDS), 1245 1241 VULNBL_INTEL_STEPS(INTEL_BROADWELL_X, X86_STEP_MAX, MMIO), 1246 1242 VULNBL_INTEL_STEPS(INTEL_BROADWELL, X86_STEP_MAX, SRBDS), 1247 - VULNBL_INTEL_STEPS(INTEL_SKYLAKE_X, X86_STEP_MAX, MMIO | RETBLEED | GDS), 1243 + VULNBL_INTEL_STEPS(INTEL_SKYLAKE_X, 0x5, MMIO | RETBLEED | GDS), 1244 + VULNBL_INTEL_STEPS(INTEL_SKYLAKE_X, X86_STEP_MAX, MMIO | RETBLEED | GDS | ITS), 1248 1245 VULNBL_INTEL_STEPS(INTEL_SKYLAKE_L, X86_STEP_MAX, MMIO | RETBLEED | GDS | SRBDS), 1249 1246 VULNBL_INTEL_STEPS(INTEL_SKYLAKE, X86_STEP_MAX, MMIO | RETBLEED | GDS | SRBDS), 1250 - VULNBL_INTEL_STEPS(INTEL_KABYLAKE_L, X86_STEP_MAX, MMIO | RETBLEED | GDS | SRBDS), 1251 - VULNBL_INTEL_STEPS(INTEL_KABYLAKE, X86_STEP_MAX, MMIO | RETBLEED | GDS | SRBDS), 1247 + VULNBL_INTEL_STEPS(INTEL_KABYLAKE_L, 0xb, MMIO | RETBLEED | GDS | SRBDS), 1248 + VULNBL_INTEL_STEPS(INTEL_KABYLAKE_L, X86_STEP_MAX, MMIO | RETBLEED | GDS | SRBDS | ITS), 1249 + VULNBL_INTEL_STEPS(INTEL_KABYLAKE, 0xc, MMIO | RETBLEED | GDS | SRBDS), 1250 + VULNBL_INTEL_STEPS(INTEL_KABYLAKE, X86_STEP_MAX, MMIO | RETBLEED | GDS | SRBDS | ITS), 1252 1251 VULNBL_INTEL_STEPS(INTEL_CANNONLAKE_L, X86_STEP_MAX, RETBLEED), 1253 - VULNBL_INTEL_STEPS(INTEL_ICELAKE_L, X86_STEP_MAX, MMIO | MMIO_SBDS | RETBLEED | GDS), 1254 - VULNBL_INTEL_STEPS(INTEL_ICELAKE_D, X86_STEP_MAX, MMIO | GDS), 1255 - VULNBL_INTEL_STEPS(INTEL_ICELAKE_X, X86_STEP_MAX, MMIO | GDS), 1256 - VULNBL_INTEL_STEPS(INTEL_COMETLAKE, X86_STEP_MAX, MMIO | MMIO_SBDS | RETBLEED | GDS), 1257 - VULNBL_INTEL_STEPS(INTEL_COMETLAKE_L, 0x0, MMIO | RETBLEED), 1258 - VULNBL_INTEL_STEPS(INTEL_COMETLAKE_L, X86_STEP_MAX, MMIO | MMIO_SBDS | RETBLEED | GDS), 1259 - VULNBL_INTEL_STEPS(INTEL_TIGERLAKE_L, X86_STEP_MAX, GDS), 1260 - VULNBL_INTEL_STEPS(INTEL_TIGERLAKE, X86_STEP_MAX, GDS), 1252 + VULNBL_INTEL_STEPS(INTEL_ICELAKE_L, X86_STEP_MAX, MMIO | MMIO_SBDS | RETBLEED | GDS | ITS | ITS_NATIVE_ONLY), 1253 + VULNBL_INTEL_STEPS(INTEL_ICELAKE_D, X86_STEP_MAX, MMIO | GDS | ITS | ITS_NATIVE_ONLY), 1254 + VULNBL_INTEL_STEPS(INTEL_ICELAKE_X, X86_STEP_MAX, MMIO | GDS | ITS | ITS_NATIVE_ONLY), 1255 + VULNBL_INTEL_STEPS(INTEL_COMETLAKE, X86_STEP_MAX, MMIO | MMIO_SBDS | RETBLEED | GDS | ITS), 1256 + VULNBL_INTEL_STEPS(INTEL_COMETLAKE_L, 0x0, MMIO | RETBLEED | ITS), 1257 + VULNBL_INTEL_STEPS(INTEL_COMETLAKE_L, X86_STEP_MAX, MMIO | MMIO_SBDS | RETBLEED | GDS | ITS), 1258 + VULNBL_INTEL_STEPS(INTEL_TIGERLAKE_L, X86_STEP_MAX, GDS | ITS | ITS_NATIVE_ONLY), 1259 + VULNBL_INTEL_STEPS(INTEL_TIGERLAKE, X86_STEP_MAX, GDS | ITS | ITS_NATIVE_ONLY), 1261 1260 VULNBL_INTEL_STEPS(INTEL_LAKEFIELD, X86_STEP_MAX, MMIO | MMIO_SBDS | RETBLEED), 1262 - VULNBL_INTEL_STEPS(INTEL_ROCKETLAKE, X86_STEP_MAX, MMIO | RETBLEED | GDS), 1261 + VULNBL_INTEL_STEPS(INTEL_ROCKETLAKE, X86_STEP_MAX, MMIO | RETBLEED | GDS | ITS | ITS_NATIVE_ONLY), 1263 1262 VULNBL_INTEL_TYPE(INTEL_ALDERLAKE, ATOM, RFDS), 1264 1263 VULNBL_INTEL_STEPS(INTEL_ALDERLAKE_L, X86_STEP_MAX, RFDS), 1265 1264 VULNBL_INTEL_TYPE(INTEL_RAPTORLAKE, ATOM, RFDS), ··· 1325 1318 1326 1319 /* Only consult the blacklist when there is no enumeration: */ 1327 1320 return cpu_matches(cpu_vuln_blacklist, RFDS); 1321 + } 1322 + 1323 + static bool __init vulnerable_to_its(u64 x86_arch_cap_msr) 1324 + { 1325 + /* The "immunity" bit trumps everything else: */ 1326 + if (x86_arch_cap_msr & ARCH_CAP_ITS_NO) 1327 + return false; 1328 + if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) 1329 + return false; 1330 + 1331 + /* None of the affected CPUs have BHI_CTRL */ 1332 + if (boot_cpu_has(X86_FEATURE_BHI_CTRL)) 1333 + return false; 1334 + 1335 + /* 1336 + * If a VMM did not expose ITS_NO, assume that a guest could 1337 + * be running on a vulnerable hardware or may migrate to such 1338 + * hardware. 1339 + */ 1340 + if (boot_cpu_has(X86_FEATURE_HYPERVISOR)) 1341 + return true; 1342 + 1343 + if (cpu_matches(cpu_vuln_blacklist, ITS)) 1344 + return true; 1345 + 1346 + return false; 1328 1347 } 1329 1348 1330 1349 static struct x86_cpu_id cpu_latest_microcode[] = { ··· 1511 1478 if (vulnerable_to_rfds(x86_arch_cap_msr)) 1512 1479 setup_force_cpu_bug(X86_BUG_RFDS); 1513 1480 1514 - /* When virtualized, eIBRS could be hidden, assume vulnerable */ 1515 - if (!(x86_arch_cap_msr & ARCH_CAP_BHI_NO) && 1516 - !cpu_matches(cpu_vuln_whitelist, NO_BHI) && 1481 + /* 1482 + * Intel parts with eIBRS are vulnerable to BHI attacks. Parts with 1483 + * BHI_NO still need to use the BHI mitigation to prevent Intra-mode 1484 + * attacks. When virtualized, eIBRS could be hidden, assume vulnerable. 1485 + */ 1486 + if (!cpu_matches(cpu_vuln_whitelist, NO_BHI) && 1517 1487 (boot_cpu_has(X86_FEATURE_IBRS_ENHANCED) || 1518 1488 boot_cpu_has(X86_FEATURE_HYPERVISOR))) 1519 1489 setup_force_cpu_bug(X86_BUG_BHI); 1520 1490 1521 1491 if (cpu_has(c, X86_FEATURE_AMD_IBPB) && !cpu_has(c, X86_FEATURE_AMD_IBPB_RET)) 1522 1492 setup_force_cpu_bug(X86_BUG_IBPB_NO_RET); 1493 + 1494 + if (vulnerable_to_its(x86_arch_cap_msr)) { 1495 + setup_force_cpu_bug(X86_BUG_ITS); 1496 + if (cpu_matches(cpu_vuln_blacklist, ITS_NATIVE_ONLY)) 1497 + setup_force_cpu_bug(X86_BUG_ITS_NATIVE_ONLY); 1498 + } 1523 1499 1524 1500 if (cpu_matches(cpu_vuln_whitelist, NO_MELTDOWN)) 1525 1501 return;
+1 -1
arch/x86/kernel/ftrace.c
··· 354 354 goto fail; 355 355 356 356 ip = trampoline + size; 357 - if (cpu_feature_enabled(X86_FEATURE_RETHUNK)) 357 + if (cpu_wants_rethunk_at(ip)) 358 358 __text_gen_insn(ip, JMP32_INSN_OPCODE, ip, x86_return_thunk, JMP32_INSN_SIZE); 359 359 else 360 360 memcpy(ip, retq, sizeof(retq));
+6
arch/x86/kernel/module.c
··· 266 266 ibt_endbr = s; 267 267 } 268 268 269 + its_init_mod(me); 270 + 269 271 if (retpolines || cfi) { 270 272 void *rseg = NULL, *cseg = NULL; 271 273 unsigned int rsize = 0, csize = 0; ··· 288 286 void *rseg = (void *)retpolines->sh_addr; 289 287 apply_retpolines(rseg, rseg + retpolines->sh_size); 290 288 } 289 + 290 + its_fini_mod(me); 291 + 291 292 if (returns) { 292 293 void *rseg = (void *)returns->sh_addr; 293 294 apply_returns(rseg, rseg + returns->sh_size); ··· 331 326 void module_arch_cleanup(struct module *mod) 332 327 { 333 328 alternatives_smp_module_del(mod); 329 + its_free_mod(mod); 334 330 }
+2 -2
arch/x86/kernel/static_call.c
··· 81 81 break; 82 82 83 83 case RET: 84 - if (cpu_feature_enabled(X86_FEATURE_RETHUNK)) 84 + if (cpu_wants_rethunk_at(insn)) 85 85 code = text_gen_insn(JMP32_INSN_OPCODE, insn, x86_return_thunk); 86 86 else 87 87 code = &retinsn; ··· 90 90 case JCC: 91 91 if (!func) { 92 92 func = __static_call_return; 93 - if (cpu_feature_enabled(X86_FEATURE_RETHUNK)) 93 + if (cpu_wants_rethunk()) 94 94 func = x86_return_thunk; 95 95 } 96 96
+10
arch/x86/kernel/vmlinux.lds.S
··· 509 509 "SRSO function pair won't alias"); 510 510 #endif 511 511 512 + #if defined(CONFIG_MITIGATION_ITS) && !defined(CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_64B) 513 + . = ASSERT(__x86_indirect_its_thunk_rax & 0x20, "__x86_indirect_thunk_rax not in second half of cacheline"); 514 + . = ASSERT(((__x86_indirect_its_thunk_rcx - __x86_indirect_its_thunk_rax) % 64) == 0, "Indirect thunks are not cacheline apart"); 515 + . = ASSERT(__x86_indirect_its_thunk_array == __x86_indirect_its_thunk_rax, "Gap in ITS thunk array"); 516 + #endif 517 + 518 + #if defined(CONFIG_MITIGATION_ITS) && !defined(CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_64B) 519 + . = ASSERT(its_return_thunk & 0x20, "its_return_thunk not in second half of cacheline"); 520 + #endif 521 + 512 522 #endif /* CONFIG_X86_64 */ 513 523 514 524 /*
+3 -1
arch/x86/kvm/x86.c
··· 1584 1584 ARCH_CAP_PSCHANGE_MC_NO | ARCH_CAP_TSX_CTRL_MSR | ARCH_CAP_TAA_NO | \ 1585 1585 ARCH_CAP_SBDR_SSDP_NO | ARCH_CAP_FBSDP_NO | ARCH_CAP_PSDP_NO | \ 1586 1586 ARCH_CAP_FB_CLEAR | ARCH_CAP_RRSBA | ARCH_CAP_PBRSB_NO | ARCH_CAP_GDS_NO | \ 1587 - ARCH_CAP_RFDS_NO | ARCH_CAP_RFDS_CLEAR | ARCH_CAP_BHI_NO) 1587 + ARCH_CAP_RFDS_NO | ARCH_CAP_RFDS_CLEAR | ARCH_CAP_BHI_NO | ARCH_CAP_ITS_NO) 1588 1588 1589 1589 static u64 kvm_get_arch_capabilities(void) 1590 1590 { ··· 1618 1618 data |= ARCH_CAP_MDS_NO; 1619 1619 if (!boot_cpu_has_bug(X86_BUG_RFDS)) 1620 1620 data |= ARCH_CAP_RFDS_NO; 1621 + if (!boot_cpu_has_bug(X86_BUG_ITS)) 1622 + data |= ARCH_CAP_ITS_NO; 1621 1623 1622 1624 if (!boot_cpu_has(X86_FEATURE_RTM)) { 1623 1625 /*
+48
arch/x86/lib/retpoline.S
··· 368 368 369 369 #endif /* CONFIG_MITIGATION_CALL_DEPTH_TRACKING */ 370 370 371 + #ifdef CONFIG_MITIGATION_ITS 372 + 373 + .macro ITS_THUNK reg 374 + 375 + /* 376 + * If CFI paranoid is used then the ITS thunk starts with opcodes (0xea; jne 1b) 377 + * that complete the fineibt_paranoid caller sequence. 378 + */ 379 + 1: .byte 0xea 380 + SYM_INNER_LABEL(__x86_indirect_paranoid_thunk_\reg, SYM_L_GLOBAL) 381 + UNWIND_HINT_UNDEFINED 382 + ANNOTATE_NOENDBR 383 + jne 1b 384 + SYM_INNER_LABEL(__x86_indirect_its_thunk_\reg, SYM_L_GLOBAL) 385 + UNWIND_HINT_UNDEFINED 386 + ANNOTATE_NOENDBR 387 + ANNOTATE_RETPOLINE_SAFE 388 + jmp *%\reg 389 + int3 390 + .align 32, 0xcc /* fill to the end of the line */ 391 + .skip 32 - (__x86_indirect_its_thunk_\reg - 1b), 0xcc /* skip to the next upper half */ 392 + .endm 393 + 394 + /* ITS mitigation requires thunks be aligned to upper half of cacheline */ 395 + .align 64, 0xcc 396 + .skip 29, 0xcc 397 + 398 + #define GEN(reg) ITS_THUNK reg 399 + #include <asm/GEN-for-each-reg.h> 400 + #undef GEN 401 + 402 + .align 64, 0xcc 403 + SYM_FUNC_ALIAS(__x86_indirect_its_thunk_array, __x86_indirect_its_thunk_rax) 404 + SYM_CODE_END(__x86_indirect_its_thunk_array) 405 + 406 + .align 64, 0xcc 407 + .skip 32, 0xcc 408 + SYM_CODE_START(its_return_thunk) 409 + UNWIND_HINT_FUNC 410 + ANNOTATE_NOENDBR 411 + ANNOTATE_UNRET_SAFE 412 + ret 413 + int3 414 + SYM_CODE_END(its_return_thunk) 415 + EXPORT_SYMBOL(its_return_thunk) 416 + 417 + #endif /* CONFIG_MITIGATION_ITS */ 418 + 371 419 /* 372 420 * This function name is magical and is used by -mfunction-return=thunk-extern 373 421 * for the compiler to generate JMPs to it.
+3
arch/x86/mm/init_32.c
··· 30 30 #include <linux/initrd.h> 31 31 #include <linux/cpumask.h> 32 32 #include <linux/gfp.h> 33 + #include <linux/execmem.h> 33 34 34 35 #include <asm/asm.h> 35 36 #include <asm/bios_ebda.h> ··· 748 747 set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT); 749 748 pr_info("Write protecting kernel text and read-only data: %luk\n", 750 749 size >> 10); 750 + 751 + execmem_cache_make_ro(); 751 752 752 753 kernel_set_to_readonly = 1; 753 754
+3
arch/x86/mm/init_64.c
··· 34 34 #include <linux/gfp.h> 35 35 #include <linux/kcore.h> 36 36 #include <linux/bootmem_info.h> 37 + #include <linux/execmem.h> 37 38 38 39 #include <asm/processor.h> 39 40 #include <asm/bios_ebda.h> ··· 1396 1395 printk(KERN_INFO "Write protecting the kernel read-only data: %luk\n", 1397 1396 (end - start) >> 10); 1398 1397 set_memory_ro(start, (end - start) >> PAGE_SHIFT); 1398 + 1399 + execmem_cache_make_ro(); 1399 1400 1400 1401 kernel_set_to_readonly = 1; 1401 1402
+56 -2
arch/x86/net/bpf_jit_comp.c
··· 41 41 #define EMIT2(b1, b2) EMIT((b1) + ((b2) << 8), 2) 42 42 #define EMIT3(b1, b2, b3) EMIT((b1) + ((b2) << 8) + ((b3) << 16), 3) 43 43 #define EMIT4(b1, b2, b3, b4) EMIT((b1) + ((b2) << 8) + ((b3) << 16) + ((b4) << 24), 4) 44 + #define EMIT5(b1, b2, b3, b4, b5) \ 45 + do { EMIT1(b1); EMIT4(b2, b3, b4, b5); } while (0) 44 46 45 47 #define EMIT1_off32(b1, off) \ 46 48 do { EMIT1(b1); EMIT(off, 4); } while (0) ··· 663 661 { 664 662 u8 *prog = *pprog; 665 663 666 - if (cpu_feature_enabled(X86_FEATURE_RETPOLINE_LFENCE)) { 664 + if (cpu_feature_enabled(X86_FEATURE_INDIRECT_THUNK_ITS)) { 665 + OPTIMIZER_HIDE_VAR(reg); 666 + emit_jump(&prog, its_static_thunk(reg), ip); 667 + } else if (cpu_feature_enabled(X86_FEATURE_RETPOLINE_LFENCE)) { 667 668 EMIT_LFENCE(); 668 669 EMIT2(0xFF, 0xE0 + reg); 669 670 } else if (cpu_feature_enabled(X86_FEATURE_RETPOLINE)) { ··· 688 683 { 689 684 u8 *prog = *pprog; 690 685 691 - if (cpu_feature_enabled(X86_FEATURE_RETHUNK)) { 686 + if (cpu_wants_rethunk()) { 692 687 emit_jump(&prog, x86_return_thunk, ip); 693 688 } else { 694 689 EMIT1(0xC3); /* ret */ ··· 1506 1501 /* Memory size/value to protect private stack overflow/underflow */ 1507 1502 #define PRIV_STACK_GUARD_SZ 8 1508 1503 #define PRIV_STACK_GUARD_VAL 0xEB9F12345678eb9fULL 1504 + 1505 + static int emit_spectre_bhb_barrier(u8 **pprog, u8 *ip, 1506 + struct bpf_prog *bpf_prog) 1507 + { 1508 + u8 *prog = *pprog; 1509 + u8 *func; 1510 + 1511 + if (cpu_feature_enabled(X86_FEATURE_CLEAR_BHB_LOOP)) { 1512 + /* The clearing sequence clobbers eax and ecx. */ 1513 + EMIT1(0x50); /* push rax */ 1514 + EMIT1(0x51); /* push rcx */ 1515 + ip += 2; 1516 + 1517 + func = (u8 *)clear_bhb_loop; 1518 + ip += x86_call_depth_emit_accounting(&prog, func, ip); 1519 + 1520 + if (emit_call(&prog, func, ip)) 1521 + return -EINVAL; 1522 + EMIT1(0x59); /* pop rcx */ 1523 + EMIT1(0x58); /* pop rax */ 1524 + } 1525 + /* Insert IBHF instruction */ 1526 + if ((cpu_feature_enabled(X86_FEATURE_CLEAR_BHB_LOOP) && 1527 + cpu_feature_enabled(X86_FEATURE_HYPERVISOR)) || 1528 + cpu_feature_enabled(X86_FEATURE_CLEAR_BHB_HW)) { 1529 + /* 1530 + * Add an Indirect Branch History Fence (IBHF). IBHF acts as a 1531 + * fence preventing branch history from before the fence from 1532 + * affecting indirect branches after the fence. This is 1533 + * specifically used in cBPF jitted code to prevent Intra-mode 1534 + * BHI attacks. The IBHF instruction is designed to be a NOP on 1535 + * hardware that doesn't need or support it. The REP and REX.W 1536 + * prefixes are required by the microcode, and they also ensure 1537 + * that the NOP is unlikely to be used in existing code. 1538 + * 1539 + * IBHF is not a valid instruction in 32-bit mode. 1540 + */ 1541 + EMIT5(0xF3, 0x48, 0x0F, 0x1E, 0xF8); /* ibhf */ 1542 + } 1543 + *pprog = prog; 1544 + return 0; 1545 + } 1509 1546 1510 1547 static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image, 1511 1548 int oldproglen, struct jit_context *ctx, bool jmp_padding) ··· 2591 2544 seen_exit = true; 2592 2545 /* Update cleanup_addr */ 2593 2546 ctx->cleanup_addr = proglen; 2547 + if (bpf_prog_was_classic(bpf_prog) && 2548 + !capable(CAP_SYS_ADMIN)) { 2549 + u8 *ip = image + addrs[i - 1]; 2550 + 2551 + if (emit_spectre_bhb_barrier(&prog, ip, bpf_prog)) 2552 + return -EINVAL; 2553 + } 2594 2554 if (bpf_prog->aux->exception_boundary) { 2595 2555 pop_callee_regs(&prog, all_callee_regs_used); 2596 2556 pop_r12(&prog);
+3
drivers/base/cpu.c
··· 601 601 CPU_SHOW_VULN_FALLBACK(reg_file_data_sampling); 602 602 CPU_SHOW_VULN_FALLBACK(ghostwrite); 603 603 CPU_SHOW_VULN_FALLBACK(old_microcode); 604 + CPU_SHOW_VULN_FALLBACK(indirect_target_selection); 604 605 605 606 static DEVICE_ATTR(meltdown, 0444, cpu_show_meltdown, NULL); 606 607 static DEVICE_ATTR(spectre_v1, 0444, cpu_show_spectre_v1, NULL); ··· 619 618 static DEVICE_ATTR(reg_file_data_sampling, 0444, cpu_show_reg_file_data_sampling, NULL); 620 619 static DEVICE_ATTR(ghostwrite, 0444, cpu_show_ghostwrite, NULL); 621 620 static DEVICE_ATTR(old_microcode, 0444, cpu_show_old_microcode, NULL); 621 + static DEVICE_ATTR(indirect_target_selection, 0444, cpu_show_indirect_target_selection, NULL); 622 622 623 623 static struct attribute *cpu_root_vulnerabilities_attrs[] = { 624 624 &dev_attr_meltdown.attr, ··· 638 636 &dev_attr_reg_file_data_sampling.attr, 639 637 &dev_attr_ghostwrite.attr, 640 638 &dev_attr_old_microcode.attr, 639 + &dev_attr_indirect_target_selection.attr, 641 640 NULL 642 641 }; 643 642
+2
include/linux/cpu.h
··· 80 80 extern ssize_t cpu_show_ghostwrite(struct device *dev, struct device_attribute *attr, char *buf); 81 81 extern ssize_t cpu_show_old_microcode(struct device *dev, 82 82 struct device_attribute *attr, char *buf); 83 + extern ssize_t cpu_show_indirect_target_selection(struct device *dev, 84 + struct device_attribute *attr, char *buf); 83 85 84 86 extern __printf(4, 5) 85 87 struct device *cpu_device_create(struct device *parent, void *drvdata,
+10 -1
include/linux/execmem.h
··· 4 4 5 5 #include <linux/types.h> 6 6 #include <linux/moduleloader.h> 7 + #include <linux/cleanup.h> 7 8 8 9 #if (defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)) && \ 9 10 !defined(CONFIG_KASAN_VMALLOC) ··· 54 53 EXECMEM_ROX_CACHE = (1 << 1), 55 54 }; 56 55 57 - #ifdef CONFIG_ARCH_HAS_EXECMEM_ROX 56 + #if defined(CONFIG_ARCH_HAS_EXECMEM_ROX) && defined(CONFIG_EXECMEM) 58 57 /** 59 58 * execmem_fill_trapping_insns - set memory to contain instructions that 60 59 * will trap ··· 94 93 * Return: 0 on success or negative error code on failure. 95 94 */ 96 95 int execmem_restore_rox(void *ptr, size_t size); 96 + 97 + /* 98 + * Called from mark_readonly(), where the system transitions to ROX. 99 + */ 100 + void execmem_cache_make_ro(void); 97 101 #else 98 102 static inline int execmem_make_temp_rw(void *ptr, size_t size) { return 0; } 99 103 static inline int execmem_restore_rox(void *ptr, size_t size) { return 0; } 104 + static inline void execmem_cache_make_ro(void) { } 100 105 #endif 101 106 102 107 /** ··· 176 169 * @ptr: pointer to the memory that should be freed 177 170 */ 178 171 void execmem_free(void *ptr); 172 + 173 + DEFINE_FREE(execmem, void *, if (_T) execmem_free(_T)); 179 174 180 175 #ifdef CONFIG_MMU 181 176 /**
+5
include/linux/module.h
··· 586 586 atomic_t refcnt; 587 587 #endif 588 588 589 + #ifdef CONFIG_MITIGATION_ITS 590 + int its_num_pages; 591 + void **its_page_array; 592 + #endif 593 + 589 594 #ifdef CONFIG_CONSTRUCTORS 590 595 /* Constructor functions. */ 591 596 ctor_fn_t *ctors;
+37 -3
mm/execmem.c
··· 254 254 return ptr; 255 255 } 256 256 257 + static bool execmem_cache_rox = false; 258 + 259 + void execmem_cache_make_ro(void) 260 + { 261 + struct maple_tree *free_areas = &execmem_cache.free_areas; 262 + struct maple_tree *busy_areas = &execmem_cache.busy_areas; 263 + MA_STATE(mas_free, free_areas, 0, ULONG_MAX); 264 + MA_STATE(mas_busy, busy_areas, 0, ULONG_MAX); 265 + struct mutex *mutex = &execmem_cache.mutex; 266 + void *area; 267 + 268 + execmem_cache_rox = true; 269 + 270 + mutex_lock(mutex); 271 + 272 + mas_for_each(&mas_free, area, ULONG_MAX) { 273 + unsigned long pages = mas_range_len(&mas_free) >> PAGE_SHIFT; 274 + set_memory_ro(mas_free.index, pages); 275 + } 276 + 277 + mas_for_each(&mas_busy, area, ULONG_MAX) { 278 + unsigned long pages = mas_range_len(&mas_busy) >> PAGE_SHIFT; 279 + set_memory_ro(mas_busy.index, pages); 280 + } 281 + 282 + mutex_unlock(mutex); 283 + } 284 + 257 285 static int execmem_cache_populate(struct execmem_range *range, size_t size) 258 286 { 259 287 unsigned long vm_flags = VM_ALLOW_HUGE_VMAP; ··· 302 274 /* fill memory with instructions that will trap */ 303 275 execmem_fill_trapping_insns(p, alloc_size, /* writable = */ true); 304 276 305 - err = set_memory_rox((unsigned long)p, vm->nr_pages); 306 - if (err) 307 - goto err_free_mem; 277 + if (execmem_cache_rox) { 278 + err = set_memory_rox((unsigned long)p, vm->nr_pages); 279 + if (err) 280 + goto err_free_mem; 281 + } else { 282 + err = set_memory_x((unsigned long)p, vm->nr_pages); 283 + if (err) 284 + goto err_free_mem; 285 + } 308 286 309 287 err = execmem_cache_add(p, alloc_size); 310 288 if (err)
+9
tools/objtool/arch/x86/decode.c
··· 189 189 op2 = ins.opcode.bytes[1]; 190 190 op3 = ins.opcode.bytes[2]; 191 191 192 + /* 193 + * XXX hack, decoder is buggered and thinks 0xea is 7 bytes long. 194 + */ 195 + if (op1 == 0xea) { 196 + insn->len = 1; 197 + insn->type = INSN_BUG; 198 + return 0; 199 + } 200 + 192 201 if (ins.rex_prefix.nbytes) { 193 202 rex = ins.rex_prefix.bytes[0]; 194 203 rex_w = X86_REX_W(rex) >> 3;
+1
tools/testing/selftests/Makefile
··· 121 121 TARGETS += vDSO 122 122 TARGETS += mm 123 123 TARGETS += x86 124 + TARGETS += x86/bugs 124 125 TARGETS += zram 125 126 #Please keep the TARGETS list alphabetically sorted 126 127 # Run "make quicktest=1 run_tests" or
+3
tools/testing/selftests/x86/bugs/Makefile
··· 1 + TEST_PROGS := its_sysfs.py its_permutations.py its_indirect_alignment.py its_ret_alignment.py 2 + TEST_FILES := common.py 3 + include ../../lib.mk
+164
tools/testing/selftests/x86/bugs/common.py
··· 1 + #!/usr/bin/env python3 2 + # SPDX-License-Identifier: GPL-2.0 3 + # 4 + # Copyright (c) 2025 Intel Corporation 5 + # 6 + # This contains kselftest framework adapted common functions for testing 7 + # mitigation for x86 bugs. 8 + 9 + import os, sys, re, shutil 10 + 11 + sys.path.insert(0, '../../kselftest') 12 + import ksft 13 + 14 + def read_file(path): 15 + if not os.path.exists(path): 16 + return None 17 + with open(path, 'r') as file: 18 + return file.read().strip() 19 + 20 + def cpuinfo_has(arg): 21 + cpuinfo = read_file('/proc/cpuinfo') 22 + if arg in cpuinfo: 23 + return True 24 + return False 25 + 26 + def cmdline_has(arg): 27 + cmdline = read_file('/proc/cmdline') 28 + if arg in cmdline: 29 + return True 30 + return False 31 + 32 + def cmdline_has_either(args): 33 + cmdline = read_file('/proc/cmdline') 34 + for arg in args: 35 + if arg in cmdline: 36 + return True 37 + return False 38 + 39 + def cmdline_has_none(args): 40 + return not cmdline_has_either(args) 41 + 42 + def cmdline_has_all(args): 43 + cmdline = read_file('/proc/cmdline') 44 + for arg in args: 45 + if arg not in cmdline: 46 + return False 47 + return True 48 + 49 + def get_sysfs(bug): 50 + return read_file("/sys/devices/system/cpu/vulnerabilities/" + bug) 51 + 52 + def sysfs_has(bug, mitigation): 53 + status = get_sysfs(bug) 54 + if mitigation in status: 55 + return True 56 + return False 57 + 58 + def sysfs_has_either(bugs, mitigations): 59 + for bug in bugs: 60 + for mitigation in mitigations: 61 + if sysfs_has(bug, mitigation): 62 + return True 63 + return False 64 + 65 + def sysfs_has_none(bugs, mitigations): 66 + return not sysfs_has_either(bugs, mitigations) 67 + 68 + def sysfs_has_all(bugs, mitigations): 69 + for bug in bugs: 70 + for mitigation in mitigations: 71 + if not sysfs_has(bug, mitigation): 72 + return False 73 + return True 74 + 75 + def bug_check_pass(bug, found): 76 + ksft.print_msg(f"\nFound: {found}") 77 + # ksft.print_msg(f"\ncmdline: {read_file('/proc/cmdline')}") 78 + ksft.test_result_pass(f'{bug}: {found}') 79 + 80 + def bug_check_fail(bug, found, expected): 81 + ksft.print_msg(f'\nFound:\t {found}') 82 + ksft.print_msg(f'Expected:\t {expected}') 83 + ksft.print_msg(f"\ncmdline: {read_file('/proc/cmdline')}") 84 + ksft.test_result_fail(f'{bug}: {found}') 85 + 86 + def bug_status_unknown(bug, found): 87 + ksft.print_msg(f'\nUnknown status: {found}') 88 + ksft.print_msg(f"\ncmdline: {read_file('/proc/cmdline')}") 89 + ksft.test_result_fail(f'{bug}: {found}') 90 + 91 + def basic_checks_sufficient(bug, mitigation): 92 + if not mitigation: 93 + bug_status_unknown(bug, "None") 94 + return True 95 + elif mitigation == "Not affected": 96 + ksft.test_result_pass(bug) 97 + return True 98 + elif mitigation == "Vulnerable": 99 + if cmdline_has_either([f'{bug}=off', 'mitigations=off']): 100 + bug_check_pass(bug, mitigation) 101 + return True 102 + return False 103 + 104 + def get_section_info(vmlinux, section_name): 105 + from elftools.elf.elffile import ELFFile 106 + with open(vmlinux, 'rb') as f: 107 + elffile = ELFFile(f) 108 + section = elffile.get_section_by_name(section_name) 109 + if section is None: 110 + ksft.print_msg("Available sections in vmlinux:") 111 + for sec in elffile.iter_sections(): 112 + ksft.print_msg(sec.name) 113 + raise ValueError(f"Section {section_name} not found in {vmlinux}") 114 + return section['sh_addr'], section['sh_offset'], section['sh_size'] 115 + 116 + def get_patch_sites(vmlinux, offset, size): 117 + import struct 118 + output = [] 119 + with open(vmlinux, 'rb') as f: 120 + f.seek(offset) 121 + i = 0 122 + while i < size: 123 + data = f.read(4) # s32 124 + if not data: 125 + break 126 + sym_offset = struct.unpack('<i', data)[0] + i 127 + i += 4 128 + output.append(sym_offset) 129 + return output 130 + 131 + def get_instruction_from_vmlinux(elffile, section, virtual_address, target_address): 132 + from capstone import Cs, CS_ARCH_X86, CS_MODE_64 133 + section_start = section['sh_addr'] 134 + section_end = section_start + section['sh_size'] 135 + 136 + if not (section_start <= target_address < section_end): 137 + return None 138 + 139 + offset = target_address - section_start 140 + code = section.data()[offset:offset + 16] 141 + 142 + cap = init_capstone() 143 + for instruction in cap.disasm(code, target_address): 144 + if instruction.address == target_address: 145 + return instruction 146 + return None 147 + 148 + def init_capstone(): 149 + from capstone import Cs, CS_ARCH_X86, CS_MODE_64, CS_OPT_SYNTAX_ATT 150 + cap = Cs(CS_ARCH_X86, CS_MODE_64) 151 + cap.syntax = CS_OPT_SYNTAX_ATT 152 + return cap 153 + 154 + def get_runtime_kernel(): 155 + import drgn 156 + return drgn.program_from_kernel() 157 + 158 + def check_dependencies_or_skip(modules, script_name="unknown test"): 159 + for mod in modules: 160 + try: 161 + __import__(mod) 162 + except ImportError: 163 + ksft.test_result_skip(f"Skipping {script_name}: missing module '{mod}'") 164 + ksft.finished()
+150
tools/testing/selftests/x86/bugs/its_indirect_alignment.py
··· 1 + #!/usr/bin/env python3 2 + # SPDX-License-Identifier: GPL-2.0 3 + # 4 + # Copyright (c) 2025 Intel Corporation 5 + # 6 + # Test for indirect target selection (ITS) mitigation. 7 + # 8 + # Test if indirect CALL/JMP are correctly patched by evaluating 9 + # the vmlinux .retpoline_sites in /proc/kcore. 10 + 11 + # Install dependencies 12 + # add-apt-repository ppa:michel-slm/kernel-utils 13 + # apt update 14 + # apt install -y python3-drgn python3-pyelftools python3-capstone 15 + # 16 + # Best to copy the vmlinux at a standard location: 17 + # mkdir -p /usr/lib/debug/lib/modules/$(uname -r) 18 + # cp $VMLINUX /usr/lib/debug/lib/modules/$(uname -r)/vmlinux 19 + # 20 + # Usage: ./its_indirect_alignment.py [vmlinux] 21 + 22 + import os, sys, argparse 23 + from pathlib import Path 24 + 25 + this_dir = os.path.dirname(os.path.realpath(__file__)) 26 + sys.path.insert(0, this_dir + '/../../kselftest') 27 + import ksft 28 + import common as c 29 + 30 + bug = "indirect_target_selection" 31 + 32 + mitigation = c.get_sysfs(bug) 33 + if not mitigation or "Aligned branch/return thunks" not in mitigation: 34 + ksft.test_result_skip("Skipping its_indirect_alignment.py: Aligned branch/return thunks not enabled") 35 + ksft.finished() 36 + 37 + if c.sysfs_has("spectre_v2", "Retpolines"): 38 + ksft.test_result_skip("Skipping its_indirect_alignment.py: Retpolines deployed") 39 + ksft.finished() 40 + 41 + c.check_dependencies_or_skip(['drgn', 'elftools', 'capstone'], script_name="its_indirect_alignment.py") 42 + 43 + from elftools.elf.elffile import ELFFile 44 + from drgn.helpers.common.memory import identify_address 45 + 46 + cap = c.init_capstone() 47 + 48 + if len(os.sys.argv) > 1: 49 + arg_vmlinux = os.sys.argv[1] 50 + if not os.path.exists(arg_vmlinux): 51 + ksft.test_result_fail(f"its_indirect_alignment.py: vmlinux not found at argument path: {arg_vmlinux}") 52 + ksft.exit_fail() 53 + os.makedirs(f"/usr/lib/debug/lib/modules/{os.uname().release}", exist_ok=True) 54 + os.system(f'cp {arg_vmlinux} /usr/lib/debug/lib/modules/$(uname -r)/vmlinux') 55 + 56 + vmlinux = f"/usr/lib/debug/lib/modules/{os.uname().release}/vmlinux" 57 + if not os.path.exists(vmlinux): 58 + ksft.test_result_fail(f"its_indirect_alignment.py: vmlinux not found at {vmlinux}") 59 + ksft.exit_fail() 60 + 61 + ksft.print_msg(f"Using vmlinux: {vmlinux}") 62 + 63 + retpolines_start_vmlinux, retpolines_sec_offset, size = c.get_section_info(vmlinux, '.retpoline_sites') 64 + ksft.print_msg(f"vmlinux: Section .retpoline_sites (0x{retpolines_start_vmlinux:x}) found at 0x{retpolines_sec_offset:x} with size 0x{size:x}") 65 + 66 + sites_offset = c.get_patch_sites(vmlinux, retpolines_sec_offset, size) 67 + total_retpoline_tests = len(sites_offset) 68 + ksft.print_msg(f"Found {total_retpoline_tests} retpoline sites") 69 + 70 + prog = c.get_runtime_kernel() 71 + retpolines_start_kcore = prog.symbol('__retpoline_sites').address 72 + ksft.print_msg(f'kcore: __retpoline_sites: 0x{retpolines_start_kcore:x}') 73 + 74 + x86_indirect_its_thunk_r15 = prog.symbol('__x86_indirect_its_thunk_r15').address 75 + ksft.print_msg(f'kcore: __x86_indirect_its_thunk_r15: 0x{x86_indirect_its_thunk_r15:x}') 76 + 77 + tests_passed = 0 78 + tests_failed = 0 79 + tests_unknown = 0 80 + 81 + with open(vmlinux, 'rb') as f: 82 + elffile = ELFFile(f) 83 + text_section = elffile.get_section_by_name('.text') 84 + 85 + for i in range(0, len(sites_offset)): 86 + site = retpolines_start_kcore + sites_offset[i] 87 + vmlinux_site = retpolines_start_vmlinux + sites_offset[i] 88 + passed = unknown = failed = False 89 + try: 90 + vmlinux_insn = c.get_instruction_from_vmlinux(elffile, text_section, text_section['sh_addr'], vmlinux_site) 91 + kcore_insn = list(cap.disasm(prog.read(site, 16), site))[0] 92 + operand = kcore_insn.op_str 93 + insn_end = site + kcore_insn.size - 1 # TODO handle Jcc.32 __x86_indirect_thunk_\reg 94 + safe_site = insn_end & 0x20 95 + site_status = "" if safe_site else "(unsafe)" 96 + 97 + ksft.print_msg(f"\nSite {i}: {identify_address(prog, site)} <0x{site:x}> {site_status}") 98 + ksft.print_msg(f"\tvmlinux: 0x{vmlinux_insn.address:x}:\t{vmlinux_insn.mnemonic}\t{vmlinux_insn.op_str}") 99 + ksft.print_msg(f"\tkcore: 0x{kcore_insn.address:x}:\t{kcore_insn.mnemonic}\t{kcore_insn.op_str}") 100 + 101 + if (site & 0x20) ^ (insn_end & 0x20): 102 + ksft.print_msg(f"\tSite at safe/unsafe boundary: {str(kcore_insn.bytes)} {kcore_insn.mnemonic} {operand}") 103 + if safe_site: 104 + tests_passed += 1 105 + passed = True 106 + ksft.print_msg(f"\tPASSED: At safe address") 107 + continue 108 + 109 + if operand.startswith('0xffffffff'): 110 + thunk = int(operand, 16) 111 + if thunk > x86_indirect_its_thunk_r15: 112 + insn_at_thunk = list(cap.disasm(prog.read(thunk, 16), thunk))[0] 113 + operand += ' -> ' + insn_at_thunk.mnemonic + ' ' + insn_at_thunk.op_str + ' <dynamic-thunk?>' 114 + if 'jmp' in insn_at_thunk.mnemonic and thunk & 0x20: 115 + ksft.print_msg(f"\tPASSED: Found {operand} at safe address") 116 + passed = True 117 + if not passed: 118 + if kcore_insn.operands[0].type == capstone.CS_OP_IMM: 119 + operand += ' <' + prog.symbol(int(operand, 16)) + '>' 120 + if '__x86_indirect_its_thunk_' in operand: 121 + ksft.print_msg(f"\tPASSED: Found {operand}") 122 + else: 123 + ksft.print_msg(f"\tPASSED: Found direct branch: {kcore_insn}, ITS thunk not required.") 124 + passed = True 125 + else: 126 + unknown = True 127 + if passed: 128 + tests_passed += 1 129 + elif unknown: 130 + ksft.print_msg(f"UNKNOWN: unexpected operand: {kcore_insn}") 131 + tests_unknown += 1 132 + else: 133 + ksft.print_msg(f'\t************* FAILED *************') 134 + ksft.print_msg(f"\tFound {kcore_insn.bytes} {kcore_insn.mnemonic} {operand}") 135 + ksft.print_msg(f'\t**********************************') 136 + tests_failed += 1 137 + except Exception as e: 138 + ksft.print_msg(f"UNKNOWN: An unexpected error occurred: {e}") 139 + tests_unknown += 1 140 + 141 + ksft.print_msg(f"\n\nSummary:") 142 + ksft.print_msg(f"PASS: \t{tests_passed} \t/ {total_retpoline_tests}") 143 + ksft.print_msg(f"FAIL: \t{tests_failed} \t/ {total_retpoline_tests}") 144 + ksft.print_msg(f"UNKNOWN: \t{tests_unknown} \t/ {total_retpoline_tests}") 145 + 146 + if tests_failed == 0: 147 + ksft.test_result_pass("All ITS return thunk sites passed") 148 + else: 149 + ksft.test_result_fail(f"{tests_failed} ITS return thunk sites failed") 150 + ksft.finished()
+109
tools/testing/selftests/x86/bugs/its_permutations.py
··· 1 + #!/usr/bin/env python3 2 + # SPDX-License-Identifier: GPL-2.0 3 + # 4 + # Copyright (c) 2025 Intel Corporation 5 + # 6 + # Test for indirect target selection (ITS) cmdline permutations with other bugs 7 + # like spectre_v2 and retbleed. 8 + 9 + import os, sys, subprocess, itertools, re, shutil 10 + 11 + test_dir = os.path.dirname(os.path.realpath(__file__)) 12 + sys.path.insert(0, test_dir + '/../../kselftest') 13 + import ksft 14 + import common as c 15 + 16 + bug = "indirect_target_selection" 17 + mitigation = c.get_sysfs(bug) 18 + 19 + if not mitigation or "Not affected" in mitigation: 20 + ksft.test_result_skip("Skipping its_permutations.py: not applicable") 21 + ksft.finished() 22 + 23 + if shutil.which('vng') is None: 24 + ksft.test_result_skip("Skipping its_permutations.py: virtme-ng ('vng') not found in PATH.") 25 + ksft.finished() 26 + 27 + TEST = f"{test_dir}/its_sysfs.py" 28 + default_kparam = ['clearcpuid=hypervisor', 'panic=5', 'panic_on_warn=1', 'oops=panic', 'nmi_watchdog=1', 'hung_task_panic=1'] 29 + 30 + DEBUG = " -v " 31 + 32 + # Install dependencies 33 + # https://github.com/arighi/virtme-ng 34 + # apt install virtme-ng 35 + BOOT_CMD = f"vng --run {test_dir}/../../../../../arch/x86/boot/bzImage " 36 + #BOOT_CMD += DEBUG 37 + 38 + bug = "indirect_target_selection" 39 + 40 + input_options = { 41 + 'indirect_target_selection' : ['off', 'on', 'stuff', 'vmexit'], 42 + 'retbleed' : ['off', 'stuff', 'auto'], 43 + 'spectre_v2' : ['off', 'on', 'eibrs', 'retpoline', 'ibrs', 'eibrs,retpoline'], 44 + } 45 + 46 + def pretty_print(output): 47 + OKBLUE = '\033[94m' 48 + OKGREEN = '\033[92m' 49 + WARNING = '\033[93m' 50 + FAIL = '\033[91m' 51 + ENDC = '\033[0m' 52 + BOLD = '\033[1m' 53 + 54 + # Define patterns and their corresponding colors 55 + patterns = { 56 + r"^ok \d+": OKGREEN, 57 + r"^not ok \d+": FAIL, 58 + r"^# Testing .*": OKBLUE, 59 + r"^# Found: .*": WARNING, 60 + r"^# Totals: .*": BOLD, 61 + r"pass:([1-9]\d*)": OKGREEN, 62 + r"fail:([1-9]\d*)": FAIL, 63 + r"skip:([1-9]\d*)": WARNING, 64 + } 65 + 66 + # Apply colors based on patterns 67 + for pattern, color in patterns.items(): 68 + output = re.sub(pattern, lambda match: f"{color}{match.group(0)}{ENDC}", output, flags=re.MULTILINE) 69 + 70 + print(output) 71 + 72 + combinations = list(itertools.product(*input_options.values())) 73 + ksft.print_header() 74 + ksft.set_plan(len(combinations)) 75 + 76 + logs = "" 77 + 78 + for combination in combinations: 79 + append = "" 80 + log = "" 81 + for p in default_kparam: 82 + append += f' --append={p}' 83 + command = BOOT_CMD + append 84 + test_params = "" 85 + for i, key in enumerate(input_options.keys()): 86 + param = f'{key}={combination[i]}' 87 + test_params += f' {param}' 88 + command += f" --append={param}" 89 + command += f" -- {TEST}" 90 + test_name = f"{bug} {test_params}" 91 + pretty_print(f'# Testing {test_name}') 92 + t = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 93 + t.wait() 94 + output, _ = t.communicate() 95 + if t.returncode == 0: 96 + ksft.test_result_pass(test_name) 97 + else: 98 + ksft.test_result_fail(test_name) 99 + output = output.decode() 100 + log += f" {output}" 101 + pretty_print(log) 102 + logs += output + "\n" 103 + 104 + # Optionally use tappy to parse the output 105 + # apt install python3-tappy 106 + with open("logs.txt", "w") as f: 107 + f.write(logs) 108 + 109 + ksft.finished()
+139
tools/testing/selftests/x86/bugs/its_ret_alignment.py
··· 1 + #!/usr/bin/env python3 2 + # SPDX-License-Identifier: GPL-2.0 3 + # 4 + # Copyright (c) 2025 Intel Corporation 5 + # 6 + # Test for indirect target selection (ITS) mitigation. 7 + # 8 + # Tests if the RETs are correctly patched by evaluating the 9 + # vmlinux .return_sites in /proc/kcore. 10 + # 11 + # Install dependencies 12 + # add-apt-repository ppa:michel-slm/kernel-utils 13 + # apt update 14 + # apt install -y python3-drgn python3-pyelftools python3-capstone 15 + # 16 + # Run on target machine 17 + # mkdir -p /usr/lib/debug/lib/modules/$(uname -r) 18 + # cp $VMLINUX /usr/lib/debug/lib/modules/$(uname -r)/vmlinux 19 + # 20 + # Usage: ./its_ret_alignment.py 21 + 22 + import os, sys, argparse 23 + from pathlib import Path 24 + 25 + this_dir = os.path.dirname(os.path.realpath(__file__)) 26 + sys.path.insert(0, this_dir + '/../../kselftest') 27 + import ksft 28 + import common as c 29 + 30 + bug = "indirect_target_selection" 31 + mitigation = c.get_sysfs(bug) 32 + if not mitigation or "Aligned branch/return thunks" not in mitigation: 33 + ksft.test_result_skip("Skipping its_ret_alignment.py: Aligned branch/return thunks not enabled") 34 + ksft.finished() 35 + 36 + c.check_dependencies_or_skip(['drgn', 'elftools', 'capstone'], script_name="its_ret_alignment.py") 37 + 38 + from elftools.elf.elffile import ELFFile 39 + from drgn.helpers.common.memory import identify_address 40 + 41 + cap = c.init_capstone() 42 + 43 + if len(os.sys.argv) > 1: 44 + arg_vmlinux = os.sys.argv[1] 45 + if not os.path.exists(arg_vmlinux): 46 + ksft.test_result_fail(f"its_ret_alignment.py: vmlinux not found at user-supplied path: {arg_vmlinux}") 47 + ksft.exit_fail() 48 + os.makedirs(f"/usr/lib/debug/lib/modules/{os.uname().release}", exist_ok=True) 49 + os.system(f'cp {arg_vmlinux} /usr/lib/debug/lib/modules/$(uname -r)/vmlinux') 50 + 51 + vmlinux = f"/usr/lib/debug/lib/modules/{os.uname().release}/vmlinux" 52 + if not os.path.exists(vmlinux): 53 + ksft.test_result_fail(f"its_ret_alignment.py: vmlinux not found at {vmlinux}") 54 + ksft.exit_fail() 55 + 56 + ksft.print_msg(f"Using vmlinux: {vmlinux}") 57 + 58 + rethunks_start_vmlinux, rethunks_sec_offset, size = c.get_section_info(vmlinux, '.return_sites') 59 + ksft.print_msg(f"vmlinux: Section .return_sites (0x{rethunks_start_vmlinux:x}) found at 0x{rethunks_sec_offset:x} with size 0x{size:x}") 60 + 61 + sites_offset = c.get_patch_sites(vmlinux, rethunks_sec_offset, size) 62 + total_rethunk_tests = len(sites_offset) 63 + ksft.print_msg(f"Found {total_rethunk_tests} rethunk sites") 64 + 65 + prog = c.get_runtime_kernel() 66 + rethunks_start_kcore = prog.symbol('__return_sites').address 67 + ksft.print_msg(f'kcore: __rethunk_sites: 0x{rethunks_start_kcore:x}') 68 + 69 + its_return_thunk = prog.symbol('its_return_thunk').address 70 + ksft.print_msg(f'kcore: its_return_thunk: 0x{its_return_thunk:x}') 71 + 72 + tests_passed = 0 73 + tests_failed = 0 74 + tests_unknown = 0 75 + tests_skipped = 0 76 + 77 + with open(vmlinux, 'rb') as f: 78 + elffile = ELFFile(f) 79 + text_section = elffile.get_section_by_name('.text') 80 + 81 + for i in range(len(sites_offset)): 82 + site = rethunks_start_kcore + sites_offset[i] 83 + vmlinux_site = rethunks_start_vmlinux + sites_offset[i] 84 + try: 85 + passed = unknown = failed = skipped = False 86 + 87 + symbol = identify_address(prog, site) 88 + vmlinux_insn = c.get_instruction_from_vmlinux(elffile, text_section, text_section['sh_addr'], vmlinux_site) 89 + kcore_insn = list(cap.disasm(prog.read(site, 16), site))[0] 90 + 91 + insn_end = site + kcore_insn.size - 1 92 + 93 + safe_site = insn_end & 0x20 94 + site_status = "" if safe_site else "(unsafe)" 95 + 96 + ksft.print_msg(f"\nSite {i}: {symbol} <0x{site:x}> {site_status}") 97 + ksft.print_msg(f"\tvmlinux: 0x{vmlinux_insn.address:x}:\t{vmlinux_insn.mnemonic}\t{vmlinux_insn.op_str}") 98 + ksft.print_msg(f"\tkcore: 0x{kcore_insn.address:x}:\t{kcore_insn.mnemonic}\t{kcore_insn.op_str}") 99 + 100 + if safe_site: 101 + tests_passed += 1 102 + passed = True 103 + ksft.print_msg(f"\tPASSED: At safe address") 104 + continue 105 + 106 + if "jmp" in kcore_insn.mnemonic: 107 + passed = True 108 + elif "ret" not in kcore_insn.mnemonic: 109 + skipped = True 110 + 111 + if passed: 112 + ksft.print_msg(f"\tPASSED: Found {kcore_insn.mnemonic} {kcore_insn.op_str}") 113 + tests_passed += 1 114 + elif skipped: 115 + ksft.print_msg(f"\tSKIPPED: Found '{kcore_insn.mnemonic}'") 116 + tests_skipped += 1 117 + elif unknown: 118 + ksft.print_msg(f"UNKNOWN: An unknown instruction: {kcore_insn}") 119 + tests_unknown += 1 120 + else: 121 + ksft.print_msg(f'\t************* FAILED *************') 122 + ksft.print_msg(f"\tFound {kcore_insn.mnemonic} {kcore_insn.op_str}") 123 + ksft.print_msg(f'\t**********************************') 124 + tests_failed += 1 125 + except Exception as e: 126 + ksft.print_msg(f"UNKNOWN: An unexpected error occurred: {e}") 127 + tests_unknown += 1 128 + 129 + ksft.print_msg(f"\n\nSummary:") 130 + ksft.print_msg(f"PASSED: \t{tests_passed} \t/ {total_rethunk_tests}") 131 + ksft.print_msg(f"FAILED: \t{tests_failed} \t/ {total_rethunk_tests}") 132 + ksft.print_msg(f"SKIPPED: \t{tests_skipped} \t/ {total_rethunk_tests}") 133 + ksft.print_msg(f"UNKNOWN: \t{tests_unknown} \t/ {total_rethunk_tests}") 134 + 135 + if tests_failed == 0: 136 + ksft.test_result_pass("All ITS return thunk sites passed.") 137 + else: 138 + ksft.test_result_fail(f"{tests_failed} failed sites need ITS return thunks.") 139 + ksft.finished()
+65
tools/testing/selftests/x86/bugs/its_sysfs.py
··· 1 + #!/usr/bin/env python3 2 + # SPDX-License-Identifier: GPL-2.0 3 + # 4 + # Copyright (c) 2025 Intel Corporation 5 + # 6 + # Test for Indirect Target Selection(ITS) mitigation sysfs status. 7 + 8 + import sys, os, re 9 + this_dir = os.path.dirname(os.path.realpath(__file__)) 10 + sys.path.insert(0, this_dir + '/../../kselftest') 11 + import ksft 12 + 13 + from common import * 14 + 15 + bug = "indirect_target_selection" 16 + mitigation = get_sysfs(bug) 17 + 18 + ITS_MITIGATION_ALIGNED_THUNKS = "Mitigation: Aligned branch/return thunks" 19 + ITS_MITIGATION_RETPOLINE_STUFF = "Mitigation: Retpolines, Stuffing RSB" 20 + ITS_MITIGATION_VMEXIT_ONLY = "Mitigation: Vulnerable, KVM: Not affected" 21 + ITS_MITIGATION_VULNERABLE = "Vulnerable" 22 + 23 + def check_mitigation(): 24 + if mitigation == ITS_MITIGATION_ALIGNED_THUNKS: 25 + if cmdline_has(f'{bug}=stuff') and sysfs_has("spectre_v2", "Retpolines"): 26 + bug_check_fail(bug, ITS_MITIGATION_ALIGNED_THUNKS, ITS_MITIGATION_RETPOLINE_STUFF) 27 + return 28 + if cmdline_has(f'{bug}=vmexit') and cpuinfo_has('its_native_only'): 29 + bug_check_fail(bug, ITS_MITIGATION_ALIGNED_THUNKS, ITS_MITIGATION_VMEXIT_ONLY) 30 + return 31 + bug_check_pass(bug, ITS_MITIGATION_ALIGNED_THUNKS) 32 + return 33 + 34 + if mitigation == ITS_MITIGATION_RETPOLINE_STUFF: 35 + if cmdline_has(f'{bug}=stuff') and sysfs_has("spectre_v2", "Retpolines"): 36 + bug_check_pass(bug, ITS_MITIGATION_RETPOLINE_STUFF) 37 + return 38 + if sysfs_has('retbleed', 'Stuffing'): 39 + bug_check_pass(bug, ITS_MITIGATION_RETPOLINE_STUFF) 40 + return 41 + bug_check_fail(bug, ITS_MITIGATION_RETPOLINE_STUFF, ITS_MITIGATION_ALIGNED_THUNKS) 42 + 43 + if mitigation == ITS_MITIGATION_VMEXIT_ONLY: 44 + if cmdline_has(f'{bug}=vmexit') and cpuinfo_has('its_native_only'): 45 + bug_check_pass(bug, ITS_MITIGATION_VMEXIT_ONLY) 46 + return 47 + bug_check_fail(bug, ITS_MITIGATION_VMEXIT_ONLY, ITS_MITIGATION_ALIGNED_THUNKS) 48 + 49 + if mitigation == ITS_MITIGATION_VULNERABLE: 50 + if sysfs_has("spectre_v2", "Vulnerable"): 51 + bug_check_pass(bug, ITS_MITIGATION_VULNERABLE) 52 + else: 53 + bug_check_fail(bug, "Mitigation", ITS_MITIGATION_VULNERABLE) 54 + 55 + bug_status_unknown(bug, mitigation) 56 + return 57 + 58 + ksft.print_header() 59 + ksft.set_plan(1) 60 + ksft.print_msg(f'{bug}: {mitigation} ...') 61 + 62 + if not basic_checks_sufficient(bug, mitigation): 63 + check_mitigation() 64 + 65 + ksft.finished()