mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02: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
					
				
					 1 changed files with 108 additions and 12 deletions
				
			
		| 
						 | 
					@ -711,6 +711,24 @@ enum cfi_mode {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static enum cfi_mode cfi_mode __ro_after_init = CFI_DEFAULT;
 | 
					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)
 | 
					static __init int cfi_parse_cmdline(char *str)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -728,10 +746,13 @@ static __init int cfi_parse_cmdline(char *str)
 | 
				
			||||||
			cfi_mode = CFI_DEFAULT;
 | 
								cfi_mode = CFI_DEFAULT;
 | 
				
			||||||
		} else if (!strcmp(str, "off")) {
 | 
							} else if (!strcmp(str, "off")) {
 | 
				
			||||||
			cfi_mode = CFI_OFF;
 | 
								cfi_mode = CFI_OFF;
 | 
				
			||||||
 | 
								cfi_rand = false;
 | 
				
			||||||
		} else if (!strcmp(str, "kcfi")) {
 | 
							} else if (!strcmp(str, "kcfi")) {
 | 
				
			||||||
			cfi_mode = CFI_KCFI;
 | 
								cfi_mode = CFI_KCFI;
 | 
				
			||||||
		} else if (!strcmp(str, "fineibt")) {
 | 
							} else if (!strcmp(str, "fineibt")) {
 | 
				
			||||||
			cfi_mode = CFI_FINEIBT;
 | 
								cfi_mode = CFI_FINEIBT;
 | 
				
			||||||
 | 
							} else if (!strcmp(str, "norand")) {
 | 
				
			||||||
 | 
								cfi_rand = false;
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			pr_err("Ignoring unknown cfi option (%s).", str);
 | 
								pr_err("Ignoring unknown cfi option (%s).", str);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -856,7 +877,50 @@ static int cfi_disable_callers(s32 *start, s32 *end)
 | 
				
			||||||
	return 0;
 | 
						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 */
 | 
					/* .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)
 | 
					static int cfi_rewrite_preamble(s32 *start, s32 *end)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	s32 *s;
 | 
						s32 *s;
 | 
				
			||||||
| 
						 | 
					@ -879,6 +943,25 @@ static int cfi_rewrite_preamble(s32 *start, s32 *end)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* .retpoline_sites */
 | 
					/* .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)
 | 
					static int cfi_rewrite_callers(s32 *start, s32 *end)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	s32 *s;
 | 
						s32 *s;
 | 
				
			||||||
| 
						 | 
					@ -915,22 +998,6 @@ static void __apply_fineibt(s32 *start_retpoline, s32 *end_retpoline,
 | 
				
			||||||
			cfi_mode = CFI_FINEIBT;
 | 
								cfi_mode = CFI_FINEIBT;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (cfi_mode) {
 | 
					 | 
				
			||||||
	case CFI_OFF:
 | 
					 | 
				
			||||||
		ret = cfi_disable_callers(start_retpoline, end_retpoline);
 | 
					 | 
				
			||||||
		if (ret)
 | 
					 | 
				
			||||||
			goto err;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (builtin)
 | 
					 | 
				
			||||||
			pr_info("Disabling CFI\n");
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	case CFI_KCFI:
 | 
					 | 
				
			||||||
		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 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
 | 
						 * rewrite them. This disables all CFI. If this succeeds but any of the
 | 
				
			||||||
| 
						 | 
					@ -940,6 +1007,35 @@ static void __apply_fineibt(s32 *start_retpoline, s32 *end_retpoline,
 | 
				
			||||||
	if (ret)
 | 
						if (ret)
 | 
				
			||||||
		goto err;
 | 
							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:
 | 
				
			||||||
		ret = cfi_rewrite_preamble(start_cfi, end_cfi);
 | 
							ret = cfi_rewrite_preamble(start_cfi, end_cfi);
 | 
				
			||||||
		if (ret)
 | 
							if (ret)
 | 
				
			||||||
			goto err;
 | 
								goto err;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue