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

x86/its: FineIBT-paranoid vs ITS

FineIBT-paranoid was using the retpoline bytes for the paranoid check,
disabling retpolines, because all parts that have IBT also have eIBRS
and thus don't need no stinking retpolines.

Except... ITS needs the retpolines for indirect calls must not be in
the first half of a cacheline :-/

So what was the paranoid call sequence:

<fineibt_paranoid_start>:
0: 41 ba 78 56 34 12 mov $0x12345678, %r10d
6: 45 3b 53 f7 cmp -0x9(%r11), %r10d
a: 4d 8d 5b <f0> lea -0x10(%r11), %r11
e: 75 fd jne d <fineibt_paranoid_start+0xd>
10: 41 ff d3 call *%r11
13: 90 nop

Now becomes:

<fineibt_paranoid_start>:
0: 41 ba 78 56 34 12 mov $0x12345678, %r10d
6: 45 3b 53 f7 cmp -0x9(%r11), %r10d
a: 4d 8d 5b f0 lea -0x10(%r11), %r11
e: 2e e8 XX XX XX XX cs call __x86_indirect_paranoid_thunk_r11

Where the paranoid_thunk looks like:

1d: <ea> (bad)
__x86_indirect_paranoid_thunk_r11:
1e: 75 fd jne 1d
__x86_indirect_its_thunk_r11:
20: 41 ff eb jmp *%r11
23: cc int3

[ dhansen: remove initialization to false ]

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Pawan Gupta <pawan.kumar.gupta@linux.intel.com>
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Reviewed-by: Alexandre Chartre <alexandre.chartre@oracle.com>

authored by

Peter Zijlstra and committed by
Dave Hansen
e52c1dc7 872df34d

+159 -20
+8
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 ··· 129 128 extern void its_init_mod(struct module *mod); 130 129 extern void its_fini_mod(struct module *mod); 131 130 extern void its_free_mod(struct module *mod); 131 + extern u8 *its_static_thunk(int reg); 132 132 #else /* CONFIG_MITIGATION_ITS */ 133 133 static inline void its_init_mod(struct module *mod) { } 134 134 static inline void its_fini_mod(struct module *mod) { } 135 135 static inline void its_free_mod(struct module *mod) { } 136 + static inline u8 *its_static_thunk(int reg) 137 + { 138 + WARN_ONCE(1, "ITS not compiled in"); 139 + 140 + return NULL; 141 + } 136 142 #endif 137 143 138 144 #if defined(CONFIG_MITIGATION_RETHUNK) && defined(CONFIG_OBJTOOL)
+129 -16
arch/x86/kernel/alternative.c
··· 127 127 #endif 128 128 }; 129 129 130 + #ifdef CONFIG_FINEIBT 131 + static bool cfi_paranoid __ro_after_init; 132 + #endif 133 + 130 134 #ifdef CONFIG_MITIGATION_ITS 131 135 132 136 static struct module *its_mod; ··· 141 137 static void *its_init_thunk(void *thunk, int reg) 142 138 { 143 139 u8 *bytes = thunk; 140 + int offset = 0; 144 141 int i = 0; 142 + 143 + #ifdef CONFIG_FINEIBT 144 + if (cfi_paranoid) { 145 + /* 146 + * When ITS uses indirect branch thunk the fineibt_paranoid 147 + * caller sequence doesn't fit in the caller site. So put the 148 + * remaining part of the sequence (<ea> + JNE) into the ITS 149 + * thunk. 150 + */ 151 + bytes[i++] = 0xea; /* invalid instruction */ 152 + bytes[i++] = 0x75; /* JNE */ 153 + bytes[i++] = 0xfd; 154 + 155 + offset = 1; 156 + } 157 + #endif 145 158 146 159 if (reg >= 8) { 147 160 bytes[i++] = 0x41; /* REX.B prefix */ ··· 168 147 bytes[i++] = 0xe0 + reg; /* jmp *reg */ 169 148 bytes[i++] = 0xcc; 170 149 171 - return thunk; 150 + return thunk + offset; 172 151 } 173 152 174 153 void its_init_mod(struct module *mod) ··· 238 217 int size = 3 + (reg / 8); 239 218 void *thunk; 240 219 220 + #ifdef CONFIG_FINEIBT 221 + /* 222 + * The ITS thunk contains an indirect jump and an int3 instruction so 223 + * its size is 3 or 4 bytes depending on the register used. If CFI 224 + * paranoid is used then 3 extra bytes are added in the ITS thunk to 225 + * complete the fineibt_paranoid caller sequence. 226 + */ 227 + if (cfi_paranoid) 228 + size += 3; 229 + #endif 230 + 241 231 if (!its_page || (its_offset + size - 1) >= PAGE_SIZE) { 242 232 its_page = its_alloc(); 243 233 if (!its_page) { ··· 270 238 its_offset += size; 271 239 272 240 return its_init_thunk(thunk, reg); 241 + } 242 + 243 + u8 *its_static_thunk(int reg) 244 + { 245 + u8 *thunk = __x86_indirect_its_thunk_array[reg]; 246 + 247 + #ifdef CONFIG_FINEIBT 248 + /* Paranoid thunk starts 2 bytes before */ 249 + if (cfi_paranoid) 250 + return thunk - 2; 251 + #endif 252 + return thunk; 273 253 } 274 254 275 255 #endif ··· 819 775 /* Lower-half of the cacheline? */ 820 776 return !(addr & 0x20); 821 777 } 778 + #else /* CONFIG_MITIGATION_ITS */ 779 + 780 + #ifdef CONFIG_FINEIBT 781 + static bool cpu_wants_indirect_its_thunk_at(unsigned long addr, int reg) 782 + { 783 + return false; 784 + } 822 785 #endif 786 + 787 + #endif /* CONFIG_MITIGATION_ITS */ 823 788 824 789 /* 825 790 * Rewrite the compiler generated retpoline thunk calls. ··· 946 893 int len, ret; 947 894 u8 bytes[16]; 948 895 u8 op1, op2; 896 + u8 *dest; 949 897 950 898 ret = insn_decode_kernel(&insn, addr); 951 899 if (WARN_ON_ONCE(ret < 0)) ··· 963 909 964 910 case CALL_INSN_OPCODE: 965 911 case JMP32_INSN_OPCODE: 912 + /* Check for cfi_paranoid + ITS */ 913 + dest = addr + insn.length + insn.immediate.value; 914 + if (dest[-1] == 0xea && (dest[0] & 0xf0) == 0x70) { 915 + WARN_ON_ONCE(cfi_mode != CFI_FINEIBT); 916 + continue; 917 + } 966 918 break; 967 919 968 920 case 0x0f: /* escape */ ··· 1257 1197 1258 1198 static bool cfi_rand __ro_after_init = true; 1259 1199 static u32 cfi_seed __ro_after_init; 1260 - 1261 - static bool cfi_paranoid __ro_after_init = false; 1262 1200 1263 1201 /* 1264 1202 * Re-hash the CFI hash with a boot-time seed while making sure the result is ··· 1670 1612 return 0; 1671 1613 } 1672 1614 1615 + static int emit_paranoid_trampoline(void *addr, struct insn *insn, int reg, u8 *bytes) 1616 + { 1617 + u8 *thunk = (void *)__x86_indirect_its_thunk_array[reg] - 2; 1618 + 1619 + #ifdef CONFIG_MITIGATION_ITS 1620 + u8 *tmp = its_allocate_thunk(reg); 1621 + if (tmp) 1622 + thunk = tmp; 1623 + #endif 1624 + 1625 + return __emit_trampoline(addr, insn, bytes, thunk, thunk); 1626 + } 1627 + 1673 1628 static int cfi_rewrite_callers(s32 *start, s32 *end) 1674 1629 { 1675 1630 s32 *s; ··· 1724 1653 memcpy(bytes, fineibt_paranoid_start, fineibt_paranoid_size); 1725 1654 memcpy(bytes + fineibt_caller_hash, &hash, 4); 1726 1655 1727 - ret = emit_indirect(op, 11, bytes + fineibt_paranoid_ind); 1728 - if (WARN_ON_ONCE(ret != 3)) 1729 - continue; 1656 + if (cpu_wants_indirect_its_thunk_at((unsigned long)addr + fineibt_paranoid_ind, 11)) { 1657 + emit_paranoid_trampoline(addr + fineibt_caller_size, 1658 + &insn, 11, bytes + fineibt_caller_size); 1659 + } else { 1660 + ret = emit_indirect(op, 11, bytes + fineibt_paranoid_ind); 1661 + if (WARN_ON_ONCE(ret != 3)) 1662 + continue; 1663 + } 1730 1664 1731 1665 text_poke_early(addr, bytes, fineibt_paranoid_size); 1732 1666 } ··· 1958 1882 return false; 1959 1883 } 1960 1884 1885 + static bool is_paranoid_thunk(unsigned long addr) 1886 + { 1887 + u32 thunk; 1888 + 1889 + __get_kernel_nofault(&thunk, (u32 *)addr, u32, Efault); 1890 + return (thunk & 0x00FFFFFF) == 0xfd75ea; 1891 + 1892 + Efault: 1893 + return false; 1894 + } 1895 + 1961 1896 /* 1962 1897 * regs->ip points to a LOCK Jcc.d8 instruction from the fineibt_paranoid_start[] 1963 - * sequence. 1898 + * sequence, or to an invalid instruction (0xea) + Jcc.d8 for cfi_paranoid + ITS 1899 + * thunk. 1964 1900 */ 1965 1901 static bool decode_fineibt_paranoid(struct pt_regs *regs, unsigned long *target, u32 *type) 1966 1902 { 1967 1903 unsigned long addr = regs->ip - fineibt_paranoid_ud; 1968 - u32 hash; 1969 1904 1970 - if (!cfi_paranoid || !is_cfi_trap(addr + fineibt_caller_size - LEN_UD2)) 1905 + if (!cfi_paranoid) 1971 1906 return false; 1972 1907 1973 - __get_kernel_nofault(&hash, addr + fineibt_caller_hash, u32, Efault); 1974 - *target = regs->r11 + fineibt_preamble_size; 1975 - *type = regs->r10; 1908 + if (is_cfi_trap(addr + fineibt_caller_size - LEN_UD2)) { 1909 + *target = regs->r11 + fineibt_preamble_size; 1910 + *type = regs->r10; 1911 + 1912 + /* 1913 + * Since the trapping instruction is the exact, but LOCK prefixed, 1914 + * Jcc.d8 that got us here, the normal fixup will work. 1915 + */ 1916 + return true; 1917 + } 1976 1918 1977 1919 /* 1978 - * Since the trapping instruction is the exact, but LOCK prefixed, 1979 - * Jcc.d8 that got us here, the normal fixup will work. 1920 + * The cfi_paranoid + ITS thunk combination results in: 1921 + * 1922 + * 0: 41 ba 78 56 34 12 mov $0x12345678, %r10d 1923 + * 6: 45 3b 53 f7 cmp -0x9(%r11), %r10d 1924 + * a: 4d 8d 5b f0 lea -0x10(%r11), %r11 1925 + * e: 2e e8 XX XX XX XX cs call __x86_indirect_paranoid_thunk_r11 1926 + * 1927 + * Where the paranoid_thunk looks like: 1928 + * 1929 + * 1d: <ea> (bad) 1930 + * __x86_indirect_paranoid_thunk_r11: 1931 + * 1e: 75 fd jne 1d 1932 + * __x86_indirect_its_thunk_r11: 1933 + * 20: 41 ff eb jmp *%r11 1934 + * 23: cc int3 1935 + * 1980 1936 */ 1981 - return true; 1937 + if (is_paranoid_thunk(regs->ip)) { 1938 + *target = regs->r11 + fineibt_preamble_size; 1939 + *type = regs->r10; 1982 1940 1983 - Efault: 1941 + regs->ip = *target; 1942 + return true; 1943 + } 1944 + 1984 1945 return false; 1985 1946 } 1986 1947
+12 -3
arch/x86/lib/retpoline.S
··· 371 371 372 372 .macro ITS_THUNK reg 373 373 374 + /* 375 + * If CFI paranoid is used then the ITS thunk starts with opcodes (0xea; jne 1b) 376 + * that complete the fineibt_paranoid caller sequence. 377 + */ 378 + 1: .byte 0xea 379 + SYM_INNER_LABEL(__x86_indirect_paranoid_thunk_\reg, SYM_L_GLOBAL) 380 + UNWIND_HINT_UNDEFINED 381 + ANNOTATE_NOENDBR 382 + jne 1b 374 383 SYM_INNER_LABEL(__x86_indirect_its_thunk_\reg, SYM_L_GLOBAL) 375 384 UNWIND_HINT_UNDEFINED 376 385 ANNOTATE_NOENDBR ··· 387 378 jmp *%\reg 388 379 int3 389 380 .align 32, 0xcc /* fill to the end of the line */ 390 - .skip 32, 0xcc /* skip to the next upper half */ 381 + .skip 32 - (__x86_indirect_its_thunk_\reg - 1b), 0xcc /* skip to the next upper half */ 391 382 .endm 392 383 393 384 /* ITS mitigation requires thunks be aligned to upper half of cacheline */ 394 385 .align 64, 0xcc 395 - .skip 32, 0xcc 396 - SYM_CODE_START(__x86_indirect_its_thunk_array) 386 + .skip 29, 0xcc 397 387 398 388 #define GEN(reg) ITS_THUNK reg 399 389 #include <asm/GEN-for-each-reg.h> 400 390 #undef GEN 401 391 402 392 .align 64, 0xcc 393 + SYM_FUNC_ALIAS(__x86_indirect_its_thunk_array, __x86_indirect_its_thunk_rax) 403 394 SYM_CODE_END(__x86_indirect_its_thunk_array) 404 395 405 396 .align 64, 0xcc
+1 -1
arch/x86/net/bpf_jit_comp.c
··· 663 663 664 664 if (cpu_feature_enabled(X86_FEATURE_INDIRECT_THUNK_ITS)) { 665 665 OPTIMIZER_HIDE_VAR(reg); 666 - emit_jump(&prog, &__x86_indirect_its_thunk_array[reg], ip); 666 + emit_jump(&prog, its_static_thunk(reg), ip); 667 667 } else if (cpu_feature_enabled(X86_FEATURE_RETPOLINE_LFENCE)) { 668 668 EMIT_LFENCE(); 669 669 EMIT2(0xFF, 0xE0 + reg);
+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;