Commit 89245600 authored by Sami Tolvanen's avatar Sami Tolvanen Committed by Kees Cook
Browse files

cfi: Switch to -fsanitize=kcfi



Switch from Clang's original forward-edge control-flow integrity
implementation to -fsanitize=kcfi, which is better suited for the
kernel, as it doesn't require LTO, doesn't use a jump table that
requires altering function references, and won't break cross-module
function address equality.

Signed-off-by: default avatarSami Tolvanen <samitolvanen@google.com>
Reviewed-by: default avatarKees Cook <keescook@chromium.org>
Tested-by: default avatarKees Cook <keescook@chromium.org>
Tested-by: default avatarNathan Chancellor <nathan@kernel.org>
Acked-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Tested-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: default avatarKees Cook <keescook@chromium.org>
Link: https://lore.kernel.org/r/20220908215504.3686827-6-samitolvanen@google.com
parent 92efda8e
Loading
Loading
Loading
Loading
+1 −12
Original line number Diff line number Diff line
@@ -921,18 +921,7 @@ export CC_FLAGS_LTO
endif

ifdef CONFIG_CFI_CLANG
CC_FLAGS_CFI	:= -fsanitize=cfi \
		   -fsanitize-cfi-cross-dso \
		   -fno-sanitize-cfi-canonical-jump-tables \
		   -fno-sanitize-trap=cfi \
		   -fno-sanitize-blacklist

ifdef CONFIG_CFI_PERMISSIVE
CC_FLAGS_CFI	+= -fsanitize-recover=cfi
endif

# If LTO flags are filtered out, we must also filter out CFI.
CC_FLAGS_LTO	+= $(CC_FLAGS_CFI)
CC_FLAGS_CFI	:= -fsanitize=kcfi
KBUILD_CFLAGS	+= $(CC_FLAGS_CFI)
export CC_FLAGS_CFI
endif
+5 −3
Original line number Diff line number Diff line
@@ -738,11 +738,13 @@ config ARCH_SUPPORTS_CFI_CLANG
	  An architecture should select this option if it can support Clang's
	  Control-Flow Integrity (CFI) checking.

config ARCH_USES_CFI_TRAPS
	bool

config CFI_CLANG
	bool "Use Clang's Control Flow Integrity (CFI)"
	depends on LTO_CLANG && ARCH_SUPPORTS_CFI_CLANG
	depends on CLANG_VERSION >= 140000
	select KALLSYMS
	depends on ARCH_SUPPORTS_CFI_CLANG
	depends on $(cc-option,-fsanitize=kcfi)
	help
	  This option enables Clang’s forward-edge Control Flow Integrity
	  (CFI) checking, where the compiler injects a runtime check to each
+19 −18
Original line number Diff line number Diff line
@@ -421,6 +421,22 @@
	__end_ro_after_init = .;
#endif

/*
 * .kcfi_traps contains a list KCFI trap locations.
 */
#ifndef KCFI_TRAPS
#ifdef CONFIG_ARCH_USES_CFI_TRAPS
#define KCFI_TRAPS							\
	__kcfi_traps : AT(ADDR(__kcfi_traps) - LOAD_OFFSET) {		\
		__start___kcfi_traps = .;				\
		KEEP(*(.kcfi_traps))					\
		__stop___kcfi_traps = .;				\
	}
#else
#define KCFI_TRAPS
#endif
#endif

/*
 * Read only Data
 */
@@ -529,6 +545,8 @@
		__stop___modver = .;					\
	}								\
									\
	KCFI_TRAPS							\
									\
	RO_EXCEPTION_TABLE						\
	NOTES								\
	BTF								\
@@ -537,21 +555,6 @@
	__end_rodata = .;


/*
 * .text..L.cfi.jumptable.* contain Control-Flow Integrity (CFI)
 * jump table entries.
 */
#ifdef CONFIG_CFI_CLANG
#define TEXT_CFI_JT							\
		. = ALIGN(PMD_SIZE);					\
		__cfi_jt_start = .;					\
		*(.text..L.cfi.jumptable .text..L.cfi.jumptable.*)	\
		. = ALIGN(PMD_SIZE);					\
		__cfi_jt_end = .;
#else
#define TEXT_CFI_JT
#endif

/*
 * Non-instrumentable text section
 */
@@ -579,7 +582,6 @@
		*(.text..refcount)					\
		*(.ref.text)						\
		*(.text.asan.* .text.tsan.*)				\
		TEXT_CFI_JT						\
	MEM_KEEP(init.text*)						\
	MEM_KEEP(exit.text*)						\

@@ -1008,8 +1010,7 @@
 * keep any .init_array.* sections.
 * https://bugs.llvm.org/show_bug.cgi?id=46478
 */
#if defined(CONFIG_GCOV_KERNEL) || defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KCSAN) || \
	defined(CONFIG_CFI_CLANG)
#if defined(CONFIG_GCOV_KERNEL) || defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KCSAN)
# ifdef CONFIG_CONSTRUCTORS
#  define SANITIZER_DISCARDS						\
	*(.eh_frame)
+25 −4
Original line number Diff line number Diff line
@@ -2,17 +2,38 @@
/*
 * Clang Control Flow Integrity (CFI) support.
 *
 * Copyright (C) 2021 Google LLC
 * Copyright (C) 2022 Google LLC
 */
#ifndef _LINUX_CFI_H
#define _LINUX_CFI_H

#include <linux/bug.h>
#include <linux/module.h>

#ifdef CONFIG_CFI_CLANG
typedef void (*cfi_check_fn)(uint64_t id, void *ptr, void *diag);
enum bug_trap_type report_cfi_failure(struct pt_regs *regs, unsigned long addr,
				      unsigned long *target, u32 type);

/* Compiler-generated function in each module, and the kernel */
extern void __cfi_check(uint64_t id, void *ptr, void *diag);
static inline enum bug_trap_type report_cfi_failure_noaddr(struct pt_regs *regs,
							   unsigned long addr)
{
	return report_cfi_failure(regs, addr, NULL, 0);
}

#ifdef CONFIG_ARCH_USES_CFI_TRAPS
bool is_cfi_trap(unsigned long addr);
#endif
#endif /* CONFIG_CFI_CLANG */

#ifdef CONFIG_MODULES
#ifdef CONFIG_ARCH_USES_CFI_TRAPS
void module_cfi_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
			 struct module *mod);
#else
static inline void module_cfi_finalize(const Elf_Ehdr *hdr,
				       const Elf_Shdr *sechdrs,
				       struct module *mod) {}
#endif /* CONFIG_ARCH_USES_CFI_TRAPS */
#endif /* CONFIG_MODULES */

#endif /* _LINUX_CFI_H */
+3 −11
Original line number Diff line number Diff line
@@ -66,17 +66,9 @@
# define __noscs	__attribute__((__no_sanitize__("shadow-call-stack")))
#endif

#define __nocfi		__attribute__((__no_sanitize__("cfi")))
#define __cficanonical	__attribute__((__cfi_canonical_jump_table__))

#if defined(CONFIG_CFI_CLANG)
/*
 * With CONFIG_CFI_CLANG, the compiler replaces function address
 * references with the address of the function's CFI jump table
 * entry. The function_nocfi macro always returns the address of the
 * actual function instead.
 */
#define function_nocfi(x)	__builtin_function_start(x)
#if __has_feature(kcfi)
/* Disable CFI checking inside a function. */
#define __nocfi		__attribute__((__no_sanitize__("kcfi")))
#endif

/*
Loading