mirror of
https://github.com/torvalds/linux.git
synced 2024-11-21 19:41:42 +00:00
x86/cfi: Add boot time hash randomization
In order to avoid known hashes (from knowing the boot image), randomize the CFI hashes with a per-boot random seed. Suggested-by: Kees Cook <keescook@chromium.org> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Reviewed-by: Kees Cook <keescook@chromium.org> Link: https://lore.kernel.org/r/20221027092842.765195516@infradead.org
This commit is contained in:
parent
082c4c8152
commit
0c3e806ec0
@ -711,6 +711,24 @@ enum cfi_mode {
|
||||
};
|
||||
|
||||
static enum cfi_mode cfi_mode __ro_after_init = CFI_DEFAULT;
|
||||
static bool cfi_rand __ro_after_init = true;
|
||||
static u32 cfi_seed __ro_after_init;
|
||||
|
||||
/*
|
||||
* Re-hash the CFI hash with a boot-time seed while making sure the result is
|
||||
* not a valid ENDBR instruction.
|
||||
*/
|
||||
static u32 cfi_rehash(u32 hash)
|
||||
{
|
||||
hash ^= cfi_seed;
|
||||
while (unlikely(is_endbr(hash) || is_endbr(-hash))) {
|
||||
bool lsb = hash & 1;
|
||||
hash >>= 1;
|
||||
if (lsb)
|
||||
hash ^= 0x80200003;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
static __init int cfi_parse_cmdline(char *str)
|
||||
{
|
||||
@ -728,10 +746,13 @@ static __init int cfi_parse_cmdline(char *str)
|
||||
cfi_mode = CFI_DEFAULT;
|
||||
} else if (!strcmp(str, "off")) {
|
||||
cfi_mode = CFI_OFF;
|
||||
cfi_rand = false;
|
||||
} else if (!strcmp(str, "kcfi")) {
|
||||
cfi_mode = CFI_KCFI;
|
||||
} else if (!strcmp(str, "fineibt")) {
|
||||
cfi_mode = CFI_FINEIBT;
|
||||
} else if (!strcmp(str, "norand")) {
|
||||
cfi_rand = false;
|
||||
} else {
|
||||
pr_err("Ignoring unknown cfi option (%s).", str);
|
||||
}
|
||||
@ -856,7 +877,50 @@ static int cfi_disable_callers(s32 *start, s32 *end)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cfi_enable_callers(s32 *start, s32 *end)
|
||||
{
|
||||
/*
|
||||
* Re-enable kCFI, undo what cfi_disable_callers() did.
|
||||
*/
|
||||
const u8 mov[] = { 0x41, 0xba };
|
||||
s32 *s;
|
||||
|
||||
for (s = start; s < end; s++) {
|
||||
void *addr = (void *)s + *s;
|
||||
u32 hash;
|
||||
|
||||
addr -= fineibt_caller_size;
|
||||
hash = decode_caller_hash(addr);
|
||||
if (!hash) /* nocfi callers */
|
||||
continue;
|
||||
|
||||
text_poke_early(addr, mov, 2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* .cfi_sites */
|
||||
static int cfi_rand_preamble(s32 *start, s32 *end)
|
||||
{
|
||||
s32 *s;
|
||||
|
||||
for (s = start; s < end; s++) {
|
||||
void *addr = (void *)s + *s;
|
||||
u32 hash;
|
||||
|
||||
hash = decode_preamble_hash(addr);
|
||||
if (WARN(!hash, "no CFI hash found at: %pS %px %*ph\n",
|
||||
addr, addr, 5, addr))
|
||||
return -EINVAL;
|
||||
|
||||
hash = cfi_rehash(hash);
|
||||
text_poke_early(addr + 1, &hash, 4);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cfi_rewrite_preamble(s32 *start, s32 *end)
|
||||
{
|
||||
s32 *s;
|
||||
@ -879,6 +943,25 @@ static int cfi_rewrite_preamble(s32 *start, s32 *end)
|
||||
}
|
||||
|
||||
/* .retpoline_sites */
|
||||
static int cfi_rand_callers(s32 *start, s32 *end)
|
||||
{
|
||||
s32 *s;
|
||||
|
||||
for (s = start; s < end; s++) {
|
||||
void *addr = (void *)s + *s;
|
||||
u32 hash;
|
||||
|
||||
addr -= fineibt_caller_size;
|
||||
hash = decode_caller_hash(addr);
|
||||
if (hash) {
|
||||
hash = -cfi_rehash(hash);
|
||||
text_poke_early(addr + 2, &hash, 4);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cfi_rewrite_callers(s32 *start, s32 *end)
|
||||
{
|
||||
s32 *s;
|
||||
@ -915,31 +998,44 @@ static void __apply_fineibt(s32 *start_retpoline, s32 *end_retpoline,
|
||||
cfi_mode = CFI_FINEIBT;
|
||||
}
|
||||
|
||||
switch (cfi_mode) {
|
||||
case CFI_OFF:
|
||||
ret = cfi_disable_callers(start_retpoline, end_retpoline);
|
||||
/*
|
||||
* Rewrite the callers to not use the __cfi_ stubs, such that we might
|
||||
* rewrite them. This disables all CFI. If this succeeds but any of the
|
||||
* later stages fails, we're without CFI.
|
||||
*/
|
||||
ret = cfi_disable_callers(start_retpoline, end_retpoline);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (cfi_rand) {
|
||||
if (builtin)
|
||||
cfi_seed = get_random_u32();
|
||||
|
||||
ret = cfi_rand_preamble(start_cfi, end_cfi);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = cfi_rand_callers(start_retpoline, end_retpoline);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
switch (cfi_mode) {
|
||||
case CFI_OFF:
|
||||
if (builtin)
|
||||
pr_info("Disabling CFI\n");
|
||||
return;
|
||||
|
||||
case CFI_KCFI:
|
||||
ret = cfi_enable_callers(start_retpoline, end_retpoline);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (builtin)
|
||||
pr_info("Using kCFI\n");
|
||||
return;
|
||||
|
||||
case CFI_FINEIBT:
|
||||
/*
|
||||
* Rewrite the callers to not use the __cfi_ stubs, such that we might
|
||||
* rewrite them. This disables all CFI. If this succeeds but any of the
|
||||
* later stages fails, we're without CFI.
|
||||
*/
|
||||
ret = cfi_disable_callers(start_retpoline, end_retpoline);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = cfi_rewrite_preamble(start_cfi, end_cfi);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
Loading…
Reference in New Issue
Block a user