mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
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>
This commit is contained in:
committed by
Dave Hansen
parent
872df34d7c
commit
e52c1dc745
@@ -127,6 +127,10 @@ const unsigned char * const x86_nops[ASM_NOP_MAX+1] =
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef CONFIG_FINEIBT
|
||||
static bool cfi_paranoid __ro_after_init;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MITIGATION_ITS
|
||||
|
||||
static struct module *its_mod;
|
||||
@@ -137,8 +141,25 @@ static unsigned int its_offset;
|
||||
static void *its_init_thunk(void *thunk, int reg)
|
||||
{
|
||||
u8 *bytes = thunk;
|
||||
int offset = 0;
|
||||
int i = 0;
|
||||
|
||||
#ifdef CONFIG_FINEIBT
|
||||
if (cfi_paranoid) {
|
||||
/*
|
||||
* When ITS uses indirect branch thunk the fineibt_paranoid
|
||||
* caller sequence doesn't fit in the caller site. So put the
|
||||
* remaining part of the sequence (<ea> + JNE) into the ITS
|
||||
* thunk.
|
||||
*/
|
||||
bytes[i++] = 0xea; /* invalid instruction */
|
||||
bytes[i++] = 0x75; /* JNE */
|
||||
bytes[i++] = 0xfd;
|
||||
|
||||
offset = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (reg >= 8) {
|
||||
bytes[i++] = 0x41; /* REX.B prefix */
|
||||
reg -= 8;
|
||||
@@ -147,7 +168,7 @@ static void *its_init_thunk(void *thunk, int reg)
|
||||
bytes[i++] = 0xe0 + reg; /* jmp *reg */
|
||||
bytes[i++] = 0xcc;
|
||||
|
||||
return thunk;
|
||||
return thunk + offset;
|
||||
}
|
||||
|
||||
void its_init_mod(struct module *mod)
|
||||
@@ -217,6 +238,17 @@ static void *its_allocate_thunk(int reg)
|
||||
int size = 3 + (reg / 8);
|
||||
void *thunk;
|
||||
|
||||
#ifdef CONFIG_FINEIBT
|
||||
/*
|
||||
* The ITS thunk contains an indirect jump and an int3 instruction so
|
||||
* its size is 3 or 4 bytes depending on the register used. If CFI
|
||||
* paranoid is used then 3 extra bytes are added in the ITS thunk to
|
||||
* complete the fineibt_paranoid caller sequence.
|
||||
*/
|
||||
if (cfi_paranoid)
|
||||
size += 3;
|
||||
#endif
|
||||
|
||||
if (!its_page || (its_offset + size - 1) >= PAGE_SIZE) {
|
||||
its_page = its_alloc();
|
||||
if (!its_page) {
|
||||
@@ -240,6 +272,18 @@ static void *its_allocate_thunk(int reg)
|
||||
return its_init_thunk(thunk, reg);
|
||||
}
|
||||
|
||||
u8 *its_static_thunk(int reg)
|
||||
{
|
||||
u8 *thunk = __x86_indirect_its_thunk_array[reg];
|
||||
|
||||
#ifdef CONFIG_FINEIBT
|
||||
/* Paranoid thunk starts 2 bytes before */
|
||||
if (cfi_paranoid)
|
||||
return thunk - 2;
|
||||
#endif
|
||||
return thunk;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -775,8 +819,17 @@ static bool cpu_wants_indirect_its_thunk_at(unsigned long addr, int reg)
|
||||
/* Lower-half of the cacheline? */
|
||||
return !(addr & 0x20);
|
||||
}
|
||||
#else /* CONFIG_MITIGATION_ITS */
|
||||
|
||||
#ifdef CONFIG_FINEIBT
|
||||
static bool cpu_wants_indirect_its_thunk_at(unsigned long addr, int reg)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_MITIGATION_ITS */
|
||||
|
||||
/*
|
||||
* Rewrite the compiler generated retpoline thunk calls.
|
||||
*
|
||||
@@ -893,6 +946,7 @@ void __init_or_module noinline apply_retpolines(s32 *start, s32 *end)
|
||||
int len, ret;
|
||||
u8 bytes[16];
|
||||
u8 op1, op2;
|
||||
u8 *dest;
|
||||
|
||||
ret = insn_decode_kernel(&insn, addr);
|
||||
if (WARN_ON_ONCE(ret < 0))
|
||||
@@ -909,6 +963,12 @@ void __init_or_module noinline apply_retpolines(s32 *start, s32 *end)
|
||||
|
||||
case CALL_INSN_OPCODE:
|
||||
case JMP32_INSN_OPCODE:
|
||||
/* Check for cfi_paranoid + ITS */
|
||||
dest = addr + insn.length + insn.immediate.value;
|
||||
if (dest[-1] == 0xea && (dest[0] & 0xf0) == 0x70) {
|
||||
WARN_ON_ONCE(cfi_mode != CFI_FINEIBT);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x0f: /* escape */
|
||||
@@ -1198,8 +1258,6 @@ int cfi_get_func_arity(void *func)
|
||||
static bool cfi_rand __ro_after_init = true;
|
||||
static u32 cfi_seed __ro_after_init;
|
||||
|
||||
static bool cfi_paranoid __ro_after_init = false;
|
||||
|
||||
/*
|
||||
* Re-hash the CFI hash with a boot-time seed while making sure the result is
|
||||
* not a valid ENDBR instruction.
|
||||
@@ -1612,6 +1670,19 @@ static int cfi_rand_callers(s32 *start, s32 *end)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emit_paranoid_trampoline(void *addr, struct insn *insn, int reg, u8 *bytes)
|
||||
{
|
||||
u8 *thunk = (void *)__x86_indirect_its_thunk_array[reg] - 2;
|
||||
|
||||
#ifdef CONFIG_MITIGATION_ITS
|
||||
u8 *tmp = its_allocate_thunk(reg);
|
||||
if (tmp)
|
||||
thunk = tmp;
|
||||
#endif
|
||||
|
||||
return __emit_trampoline(addr, insn, bytes, thunk, thunk);
|
||||
}
|
||||
|
||||
static int cfi_rewrite_callers(s32 *start, s32 *end)
|
||||
{
|
||||
s32 *s;
|
||||
@@ -1653,9 +1724,14 @@ static int cfi_rewrite_callers(s32 *start, s32 *end)
|
||||
memcpy(bytes, fineibt_paranoid_start, fineibt_paranoid_size);
|
||||
memcpy(bytes + fineibt_caller_hash, &hash, 4);
|
||||
|
||||
ret = emit_indirect(op, 11, bytes + fineibt_paranoid_ind);
|
||||
if (WARN_ON_ONCE(ret != 3))
|
||||
continue;
|
||||
if (cpu_wants_indirect_its_thunk_at((unsigned long)addr + fineibt_paranoid_ind, 11)) {
|
||||
emit_paranoid_trampoline(addr + fineibt_caller_size,
|
||||
&insn, 11, bytes + fineibt_caller_size);
|
||||
} else {
|
||||
ret = emit_indirect(op, 11, bytes + fineibt_paranoid_ind);
|
||||
if (WARN_ON_ONCE(ret != 3))
|
||||
continue;
|
||||
}
|
||||
|
||||
text_poke_early(addr, bytes, fineibt_paranoid_size);
|
||||
}
|
||||
@@ -1882,29 +1958,66 @@ Efault:
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool is_paranoid_thunk(unsigned long addr)
|
||||
{
|
||||
u32 thunk;
|
||||
|
||||
__get_kernel_nofault(&thunk, (u32 *)addr, u32, Efault);
|
||||
return (thunk & 0x00FFFFFF) == 0xfd75ea;
|
||||
|
||||
Efault:
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* regs->ip points to a LOCK Jcc.d8 instruction from the fineibt_paranoid_start[]
|
||||
* sequence.
|
||||
* sequence, or to an invalid instruction (0xea) + Jcc.d8 for cfi_paranoid + ITS
|
||||
* thunk.
|
||||
*/
|
||||
static bool decode_fineibt_paranoid(struct pt_regs *regs, unsigned long *target, u32 *type)
|
||||
{
|
||||
unsigned long addr = regs->ip - fineibt_paranoid_ud;
|
||||
u32 hash;
|
||||
|
||||
if (!cfi_paranoid || !is_cfi_trap(addr + fineibt_caller_size - LEN_UD2))
|
||||
if (!cfi_paranoid)
|
||||
return false;
|
||||
|
||||
__get_kernel_nofault(&hash, addr + fineibt_caller_hash, u32, Efault);
|
||||
*target = regs->r11 + fineibt_preamble_size;
|
||||
*type = regs->r10;
|
||||
if (is_cfi_trap(addr + fineibt_caller_size - LEN_UD2)) {
|
||||
*target = regs->r11 + fineibt_preamble_size;
|
||||
*type = regs->r10;
|
||||
|
||||
/*
|
||||
* Since the trapping instruction is the exact, but LOCK prefixed,
|
||||
* Jcc.d8 that got us here, the normal fixup will work.
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Since the trapping instruction is the exact, but LOCK prefixed,
|
||||
* Jcc.d8 that got us here, the normal fixup will work.
|
||||
* The cfi_paranoid + ITS thunk combination results in:
|
||||
*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
return true;
|
||||
if (is_paranoid_thunk(regs->ip)) {
|
||||
*target = regs->r11 + fineibt_preamble_size;
|
||||
*type = regs->r10;
|
||||
|
||||
regs->ip = *target;
|
||||
return true;
|
||||
}
|
||||
|
||||
Efault:
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user