Skip to content

Commit 0c3e806

Browse files
author
Peter Zijlstra
committed
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 <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Reviewed-by: Kees Cook <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 082c4c8 commit 0c3e806

File tree

1 file changed

+108
-12
lines changed

1 file changed

+108
-12
lines changed

arch/x86/kernel/alternative.c

Lines changed: 108 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,24 @@ enum cfi_mode {
711711
};
712712

713713
static enum cfi_mode cfi_mode __ro_after_init = CFI_DEFAULT;
714+
static bool cfi_rand __ro_after_init = true;
715+
static u32 cfi_seed __ro_after_init;
716+
717+
/*
718+
* Re-hash the CFI hash with a boot-time seed while making sure the result is
719+
* not a valid ENDBR instruction.
720+
*/
721+
static u32 cfi_rehash(u32 hash)
722+
{
723+
hash ^= cfi_seed;
724+
while (unlikely(is_endbr(hash) || is_endbr(-hash))) {
725+
bool lsb = hash & 1;
726+
hash >>= 1;
727+
if (lsb)
728+
hash ^= 0x80200003;
729+
}
730+
return hash;
731+
}
714732

715733
static __init int cfi_parse_cmdline(char *str)
716734
{
@@ -728,10 +746,13 @@ static __init int cfi_parse_cmdline(char *str)
728746
cfi_mode = CFI_DEFAULT;
729747
} else if (!strcmp(str, "off")) {
730748
cfi_mode = CFI_OFF;
749+
cfi_rand = false;
731750
} else if (!strcmp(str, "kcfi")) {
732751
cfi_mode = CFI_KCFI;
733752
} else if (!strcmp(str, "fineibt")) {
734753
cfi_mode = CFI_FINEIBT;
754+
} else if (!strcmp(str, "norand")) {
755+
cfi_rand = false;
735756
} else {
736757
pr_err("Ignoring unknown cfi option (%s).", str);
737758
}
@@ -856,7 +877,50 @@ static int cfi_disable_callers(s32 *start, s32 *end)
856877
return 0;
857878
}
858879

880+
static int cfi_enable_callers(s32 *start, s32 *end)
881+
{
882+
/*
883+
* Re-enable kCFI, undo what cfi_disable_callers() did.
884+
*/
885+
const u8 mov[] = { 0x41, 0xba };
886+
s32 *s;
887+
888+
for (s = start; s < end; s++) {
889+
void *addr = (void *)s + *s;
890+
u32 hash;
891+
892+
addr -= fineibt_caller_size;
893+
hash = decode_caller_hash(addr);
894+
if (!hash) /* nocfi callers */
895+
continue;
896+
897+
text_poke_early(addr, mov, 2);
898+
}
899+
900+
return 0;
901+
}
902+
859903
/* .cfi_sites */
904+
static int cfi_rand_preamble(s32 *start, s32 *end)
905+
{
906+
s32 *s;
907+
908+
for (s = start; s < end; s++) {
909+
void *addr = (void *)s + *s;
910+
u32 hash;
911+
912+
hash = decode_preamble_hash(addr);
913+
if (WARN(!hash, "no CFI hash found at: %pS %px %*ph\n",
914+
addr, addr, 5, addr))
915+
return -EINVAL;
916+
917+
hash = cfi_rehash(hash);
918+
text_poke_early(addr + 1, &hash, 4);
919+
}
920+
921+
return 0;
922+
}
923+
860924
static int cfi_rewrite_preamble(s32 *start, s32 *end)
861925
{
862926
s32 *s;
@@ -879,6 +943,25 @@ static int cfi_rewrite_preamble(s32 *start, s32 *end)
879943
}
880944

881945
/* .retpoline_sites */
946+
static int cfi_rand_callers(s32 *start, s32 *end)
947+
{
948+
s32 *s;
949+
950+
for (s = start; s < end; s++) {
951+
void *addr = (void *)s + *s;
952+
u32 hash;
953+
954+
addr -= fineibt_caller_size;
955+
hash = decode_caller_hash(addr);
956+
if (hash) {
957+
hash = -cfi_rehash(hash);
958+
text_poke_early(addr + 2, &hash, 4);
959+
}
960+
}
961+
962+
return 0;
963+
}
964+
882965
static int cfi_rewrite_callers(s32 *start, s32 *end)
883966
{
884967
s32 *s;
@@ -915,31 +998,44 @@ static void __apply_fineibt(s32 *start_retpoline, s32 *end_retpoline,
915998
cfi_mode = CFI_FINEIBT;
916999
}
9171000

918-
switch (cfi_mode) {
919-
case CFI_OFF:
920-
ret = cfi_disable_callers(start_retpoline, end_retpoline);
1001+
/*
1002+
* Rewrite the callers to not use the __cfi_ stubs, such that we might
1003+
* rewrite them. This disables all CFI. If this succeeds but any of the
1004+
* later stages fails, we're without CFI.
1005+
*/
1006+
ret = cfi_disable_callers(start_retpoline, end_retpoline);
1007+
if (ret)
1008+
goto err;
1009+
1010+
if (cfi_rand) {
1011+
if (builtin)
1012+
cfi_seed = get_random_u32();
1013+
1014+
ret = cfi_rand_preamble(start_cfi, end_cfi);
9211015
if (ret)
9221016
goto err;
9231017

1018+
ret = cfi_rand_callers(start_retpoline, end_retpoline);
1019+
if (ret)
1020+
goto err;
1021+
}
1022+
1023+
switch (cfi_mode) {
1024+
case CFI_OFF:
9241025
if (builtin)
9251026
pr_info("Disabling CFI\n");
9261027
return;
9271028

9281029
case CFI_KCFI:
1030+
ret = cfi_enable_callers(start_retpoline, end_retpoline);
1031+
if (ret)
1032+
goto err;
1033+
9291034
if (builtin)
9301035
pr_info("Using kCFI\n");
9311036
return;
9321037

9331038
case CFI_FINEIBT:
934-
/*
935-
* Rewrite the callers to not use the __cfi_ stubs, such that we might
936-
* rewrite them. This disables all CFI. If this succeeds but any of the
937-
* later stages fails, we're without CFI.
938-
*/
939-
ret = cfi_disable_callers(start_retpoline, end_retpoline);
940-
if (ret)
941-
goto err;
942-
9431039
ret = cfi_rewrite_preamble(start_cfi, end_cfi);
9441040
if (ret)
9451041
goto err;

0 commit comments

Comments
 (0)