Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Sami Tolvanen <samitolvanen@google.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
Tested-by: Kees Cook <keescook@chromium.org>
Tested-by: Nathan Chancellor <nathan@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Tested-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Kees Cook <keescook@chromium.org>
Link: https://lore.kernel.org/r/20220908215504.3686827-6-samitolvanen@google.com

authored by

Sami Tolvanen and committed by
Kees Cook
89245600 92efda8e

+139 -182
+1 -12
Makefile
··· 921 921 endif 922 922 923 923 ifdef CONFIG_CFI_CLANG 924 - CC_FLAGS_CFI := -fsanitize=cfi \ 925 - -fsanitize-cfi-cross-dso \ 926 - -fno-sanitize-cfi-canonical-jump-tables \ 927 - -fno-sanitize-trap=cfi \ 928 - -fno-sanitize-blacklist 929 - 930 - ifdef CONFIG_CFI_PERMISSIVE 931 - CC_FLAGS_CFI += -fsanitize-recover=cfi 932 - endif 933 - 934 - # If LTO flags are filtered out, we must also filter out CFI. 935 - CC_FLAGS_LTO += $(CC_FLAGS_CFI) 924 + CC_FLAGS_CFI := -fsanitize=kcfi 936 925 KBUILD_CFLAGS += $(CC_FLAGS_CFI) 937 926 export CC_FLAGS_CFI 938 927 endif
+5 -3
arch/Kconfig
··· 738 738 An architecture should select this option if it can support Clang's 739 739 Control-Flow Integrity (CFI) checking. 740 740 741 + config ARCH_USES_CFI_TRAPS 742 + bool 743 + 741 744 config CFI_CLANG 742 745 bool "Use Clang's Control Flow Integrity (CFI)" 743 - depends on LTO_CLANG && ARCH_SUPPORTS_CFI_CLANG 744 - depends on CLANG_VERSION >= 140000 745 - select KALLSYMS 746 + depends on ARCH_SUPPORTS_CFI_CLANG 747 + depends on $(cc-option,-fsanitize=kcfi) 746 748 help 747 749 This option enables Clang’s forward-edge Control Flow Integrity 748 750 (CFI) checking, where the compiler injects a runtime check to each
+19 -18
include/asm-generic/vmlinux.lds.h
··· 422 422 #endif 423 423 424 424 /* 425 + * .kcfi_traps contains a list KCFI trap locations. 426 + */ 427 + #ifndef KCFI_TRAPS 428 + #ifdef CONFIG_ARCH_USES_CFI_TRAPS 429 + #define KCFI_TRAPS \ 430 + __kcfi_traps : AT(ADDR(__kcfi_traps) - LOAD_OFFSET) { \ 431 + __start___kcfi_traps = .; \ 432 + KEEP(*(.kcfi_traps)) \ 433 + __stop___kcfi_traps = .; \ 434 + } 435 + #else 436 + #define KCFI_TRAPS 437 + #endif 438 + #endif 439 + 440 + /* 425 441 * Read only Data 426 442 */ 427 443 #define RO_DATA(align) \ ··· 545 529 __stop___modver = .; \ 546 530 } \ 547 531 \ 532 + KCFI_TRAPS \ 533 + \ 548 534 RO_EXCEPTION_TABLE \ 549 535 NOTES \ 550 536 BTF \ ··· 554 536 . = ALIGN((align)); \ 555 537 __end_rodata = .; 556 538 557 - 558 - /* 559 - * .text..L.cfi.jumptable.* contain Control-Flow Integrity (CFI) 560 - * jump table entries. 561 - */ 562 - #ifdef CONFIG_CFI_CLANG 563 - #define TEXT_CFI_JT \ 564 - . = ALIGN(PMD_SIZE); \ 565 - __cfi_jt_start = .; \ 566 - *(.text..L.cfi.jumptable .text..L.cfi.jumptable.*) \ 567 - . = ALIGN(PMD_SIZE); \ 568 - __cfi_jt_end = .; 569 - #else 570 - #define TEXT_CFI_JT 571 - #endif 572 539 573 540 /* 574 541 * Non-instrumentable text section ··· 582 579 *(.text..refcount) \ 583 580 *(.ref.text) \ 584 581 *(.text.asan.* .text.tsan.*) \ 585 - TEXT_CFI_JT \ 586 582 MEM_KEEP(init.text*) \ 587 583 MEM_KEEP(exit.text*) \ 588 584 ··· 1010 1008 * keep any .init_array.* sections. 1011 1009 * https://bugs.llvm.org/show_bug.cgi?id=46478 1012 1010 */ 1013 - #if defined(CONFIG_GCOV_KERNEL) || defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KCSAN) || \ 1014 - defined(CONFIG_CFI_CLANG) 1011 + #if defined(CONFIG_GCOV_KERNEL) || defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KCSAN) 1015 1012 # ifdef CONFIG_CONSTRUCTORS 1016 1013 # define SANITIZER_DISCARDS \ 1017 1014 *(.eh_frame)
+25 -4
include/linux/cfi.h
··· 2 2 /* 3 3 * Clang Control Flow Integrity (CFI) support. 4 4 * 5 - * Copyright (C) 2021 Google LLC 5 + * Copyright (C) 2022 Google LLC 6 6 */ 7 7 #ifndef _LINUX_CFI_H 8 8 #define _LINUX_CFI_H 9 9 10 + #include <linux/bug.h> 11 + #include <linux/module.h> 12 + 10 13 #ifdef CONFIG_CFI_CLANG 11 - typedef void (*cfi_check_fn)(uint64_t id, void *ptr, void *diag); 14 + enum bug_trap_type report_cfi_failure(struct pt_regs *regs, unsigned long addr, 15 + unsigned long *target, u32 type); 12 16 13 - /* Compiler-generated function in each module, and the kernel */ 14 - extern void __cfi_check(uint64_t id, void *ptr, void *diag); 17 + static inline enum bug_trap_type report_cfi_failure_noaddr(struct pt_regs *regs, 18 + unsigned long addr) 19 + { 20 + return report_cfi_failure(regs, addr, NULL, 0); 21 + } 15 22 23 + #ifdef CONFIG_ARCH_USES_CFI_TRAPS 24 + bool is_cfi_trap(unsigned long addr); 25 + #endif 16 26 #endif /* CONFIG_CFI_CLANG */ 27 + 28 + #ifdef CONFIG_MODULES 29 + #ifdef CONFIG_ARCH_USES_CFI_TRAPS 30 + void module_cfi_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, 31 + struct module *mod); 32 + #else 33 + static inline void module_cfi_finalize(const Elf_Ehdr *hdr, 34 + const Elf_Shdr *sechdrs, 35 + struct module *mod) {} 36 + #endif /* CONFIG_ARCH_USES_CFI_TRAPS */ 37 + #endif /* CONFIG_MODULES */ 17 38 18 39 #endif /* _LINUX_CFI_H */
+3 -11
include/linux/compiler-clang.h
··· 66 66 # define __noscs __attribute__((__no_sanitize__("shadow-call-stack"))) 67 67 #endif 68 68 69 - #define __nocfi __attribute__((__no_sanitize__("cfi"))) 70 - #define __cficanonical __attribute__((__cfi_canonical_jump_table__)) 71 - 72 - #if defined(CONFIG_CFI_CLANG) 73 - /* 74 - * With CONFIG_CFI_CLANG, the compiler replaces function address 75 - * references with the address of the function's CFI jump table 76 - * entry. The function_nocfi macro always returns the address of the 77 - * actual function instead. 78 - */ 79 - #define function_nocfi(x) __builtin_function_start(x) 69 + #if __has_feature(kcfi) 70 + /* Disable CFI checking inside a function. */ 71 + #define __nocfi __attribute__((__no_sanitize__("kcfi"))) 80 72 #endif 81 73 82 74 /*
+3 -3
include/linux/module.h
··· 27 27 #include <linux/tracepoint-defs.h> 28 28 #include <linux/srcu.h> 29 29 #include <linux/static_call_types.h> 30 - #include <linux/cfi.h> 31 30 32 31 #include <linux/percpu.h> 33 32 #include <asm/module.h> ··· 386 387 const s32 *crcs; 387 388 unsigned int num_syms; 388 389 389 - #ifdef CONFIG_CFI_CLANG 390 - cfi_check_fn cfi_check; 390 + #ifdef CONFIG_ARCH_USES_CFI_TRAPS 391 + s32 *kcfi_traps; 392 + s32 *kcfi_traps_end; 391 393 #endif 392 394 393 395 /* Kernel parameters. */
+76 -80
kernel/cfi.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 /* 3 - * Clang Control Flow Integrity (CFI) error and slowpath handling. 3 + * Clang Control Flow Integrity (CFI) error handling. 4 4 * 5 - * Copyright (C) 2021 Google LLC 5 + * Copyright (C) 2022 Google LLC 6 6 */ 7 7 8 - #include <linux/hardirq.h> 9 - #include <linux/kallsyms.h> 10 - #include <linux/module.h> 11 - #include <linux/mutex.h> 12 - #include <linux/printk.h> 13 - #include <linux/ratelimit.h> 14 - #include <linux/rcupdate.h> 15 - #include <linux/vmalloc.h> 16 - #include <asm/cacheflush.h> 17 - #include <asm/set_memory.h> 8 + #include <linux/cfi.h> 18 9 19 - /* Compiler-defined handler names */ 20 - #ifdef CONFIG_CFI_PERMISSIVE 21 - #define cfi_failure_handler __ubsan_handle_cfi_check_fail 22 - #else 23 - #define cfi_failure_handler __ubsan_handle_cfi_check_fail_abort 24 - #endif 25 - 26 - static inline void handle_cfi_failure(void *ptr) 10 + enum bug_trap_type report_cfi_failure(struct pt_regs *regs, unsigned long addr, 11 + unsigned long *target, u32 type) 27 12 { 28 - if (IS_ENABLED(CONFIG_CFI_PERMISSIVE)) 29 - WARN_RATELIMIT(1, "CFI failure (target: %pS):\n", ptr); 13 + if (target) 14 + pr_err("CFI failure at %pS (target: %pS; expected type: 0x%08x)\n", 15 + (void *)addr, (void *)*target, type); 30 16 else 31 - panic("CFI failure (target: %pS)\n", ptr); 17 + pr_err("CFI failure at %pS (no target information)\n", 18 + (void *)addr); 19 + 20 + if (IS_ENABLED(CONFIG_CFI_PERMISSIVE)) { 21 + __warn(NULL, 0, (void *)addr, 0, regs, NULL); 22 + return BUG_TRAP_TYPE_WARN; 23 + } 24 + 25 + return BUG_TRAP_TYPE_BUG; 26 + } 27 + 28 + #ifdef CONFIG_ARCH_USES_CFI_TRAPS 29 + static inline unsigned long trap_address(s32 *p) 30 + { 31 + return (unsigned long)((long)p + (long)*p); 32 + } 33 + 34 + static bool is_trap(unsigned long addr, s32 *start, s32 *end) 35 + { 36 + s32 *p; 37 + 38 + for (p = start; p < end; ++p) { 39 + if (trap_address(p) == addr) 40 + return true; 41 + } 42 + 43 + return false; 32 44 } 33 45 34 46 #ifdef CONFIG_MODULES 35 - 36 - static inline cfi_check_fn find_module_check_fn(unsigned long ptr) 47 + /* Populates `kcfi_trap(_end)?` fields in `struct module`. */ 48 + void module_cfi_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, 49 + struct module *mod) 37 50 { 38 - cfi_check_fn fn = NULL; 51 + char *secstrings; 52 + unsigned int i; 53 + 54 + mod->kcfi_traps = NULL; 55 + mod->kcfi_traps_end = NULL; 56 + 57 + secstrings = (char *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; 58 + 59 + for (i = 1; i < hdr->e_shnum; i++) { 60 + if (strcmp(secstrings + sechdrs[i].sh_name, "__kcfi_traps")) 61 + continue; 62 + 63 + mod->kcfi_traps = (s32 *)sechdrs[i].sh_addr; 64 + mod->kcfi_traps_end = (s32 *)(sechdrs[i].sh_addr + sechdrs[i].sh_size); 65 + break; 66 + } 67 + } 68 + 69 + static bool is_module_cfi_trap(unsigned long addr) 70 + { 39 71 struct module *mod; 72 + bool found = false; 40 73 41 74 rcu_read_lock_sched_notrace(); 42 - mod = __module_address(ptr); 75 + 76 + mod = __module_address(addr); 43 77 if (mod) 44 - fn = mod->cfi_check; 78 + found = is_trap(addr, mod->kcfi_traps, mod->kcfi_traps_end); 79 + 45 80 rcu_read_unlock_sched_notrace(); 46 81 47 - return fn; 82 + return found; 48 83 } 49 - 50 - static inline cfi_check_fn find_check_fn(unsigned long ptr) 84 + #else /* CONFIG_MODULES */ 85 + static inline bool is_module_cfi_trap(unsigned long addr) 51 86 { 52 - cfi_check_fn fn = NULL; 53 - unsigned long flags; 54 - bool rcu_idle; 55 - 56 - if (is_kernel_text(ptr)) 57 - return __cfi_check; 58 - 59 - /* 60 - * Indirect call checks can happen when RCU is not watching. Both 61 - * the shadow and __module_address use RCU, so we need to wake it 62 - * up if necessary. 63 - */ 64 - rcu_idle = !rcu_is_watching(); 65 - if (rcu_idle) { 66 - local_irq_save(flags); 67 - ct_irq_enter(); 68 - } 69 - 70 - fn = find_module_check_fn(ptr); 71 - 72 - if (rcu_idle) { 73 - ct_irq_exit(); 74 - local_irq_restore(flags); 75 - } 76 - 77 - return fn; 87 + return false; 78 88 } 79 - 80 - void __cfi_slowpath_diag(uint64_t id, void *ptr, void *diag) 81 - { 82 - cfi_check_fn fn = find_check_fn((unsigned long)ptr); 83 - 84 - if (likely(fn)) 85 - fn(id, ptr, diag); 86 - else /* Don't allow unchecked modules */ 87 - handle_cfi_failure(ptr); 88 - } 89 - EXPORT_SYMBOL(__cfi_slowpath_diag); 90 - 91 - #else /* !CONFIG_MODULES */ 92 - 93 - void __cfi_slowpath_diag(uint64_t id, void *ptr, void *diag) 94 - { 95 - handle_cfi_failure(ptr); /* No modules */ 96 - } 97 - EXPORT_SYMBOL(__cfi_slowpath_diag); 98 - 99 89 #endif /* CONFIG_MODULES */ 100 90 101 - void cfi_failure_handler(void *data, void *ptr, void *vtable) 91 + extern s32 __start___kcfi_traps[]; 92 + extern s32 __stop___kcfi_traps[]; 93 + 94 + bool is_cfi_trap(unsigned long addr) 102 95 { 103 - handle_cfi_failure(ptr); 96 + if (is_trap(addr, __start___kcfi_traps, __stop___kcfi_traps)) 97 + return true; 98 + 99 + return is_module_cfi_trap(addr); 104 100 } 105 - EXPORT_SYMBOL(cfi_failure_handler); 101 + #endif /* CONFIG_ARCH_USES_CFI_TRAPS */
+3 -32
kernel/module/main.c
··· 53 53 #include <linux/bsearch.h> 54 54 #include <linux/dynamic_debug.h> 55 55 #include <linux/audit.h> 56 + #include <linux/cfi.h> 56 57 #include <uapi/linux/module.h> 57 58 #include "internal.h" 58 59 ··· 2598 2597 if (err < 0) 2599 2598 goto out; 2600 2599 2601 - /* This relies on module_mutex for list integrity. */ 2600 + /* These rely on module_mutex for list integrity. */ 2602 2601 module_bug_finalize(info->hdr, info->sechdrs, mod); 2602 + module_cfi_finalize(info->hdr, info->sechdrs, mod); 2603 2603 2604 2604 if (module_check_misalignment(mod)) 2605 2605 goto out_misaligned; ··· 2661 2659 pr_warn("%s: unknown parameter '%s' ignored\n", modname, param); 2662 2660 return 0; 2663 2661 } 2664 - 2665 - static void cfi_init(struct module *mod); 2666 2662 2667 2663 /* 2668 2664 * Allocate and load the module: note that size of section 0 is always ··· 2790 2790 goto free_modinfo; 2791 2791 2792 2792 flush_module_icache(mod); 2793 - 2794 - /* Setup CFI for the module. */ 2795 - cfi_init(mod); 2796 2793 2797 2794 /* Now copy in args */ 2798 2795 mod->args = strndup_user(uargs, ~0UL >> 1); ··· 2950 2953 static inline int within(unsigned long addr, void *start, unsigned long size) 2951 2954 { 2952 2955 return ((void *)addr >= start && (void *)addr < start + size); 2953 - } 2954 - 2955 - static void cfi_init(struct module *mod) 2956 - { 2957 - #ifdef CONFIG_CFI_CLANG 2958 - initcall_t *init; 2959 - #ifdef CONFIG_MODULE_UNLOAD 2960 - exitcall_t *exit; 2961 - #endif 2962 - 2963 - rcu_read_lock_sched(); 2964 - mod->cfi_check = (cfi_check_fn) 2965 - find_kallsyms_symbol_value(mod, "__cfi_check"); 2966 - init = (initcall_t *) 2967 - find_kallsyms_symbol_value(mod, "__cfi_jt_init_module"); 2968 - /* Fix init/exit functions to point to the CFI jump table */ 2969 - if (init) 2970 - mod->init = *init; 2971 - #ifdef CONFIG_MODULE_UNLOAD 2972 - exit = (exitcall_t *) 2973 - find_kallsyms_symbol_value(mod, "__cfi_jt_cleanup_module"); 2974 - if (exit) 2975 - mod->exit = *exit; 2976 - #endif 2977 - rcu_read_unlock_sched(); 2978 - #endif 2979 2956 } 2980 2957 2981 2958 /* Keep in sync with MODULE_FLAGS_BUF_SIZE !!! */
+4 -19
scripts/module.lds.S
··· 3 3 * Archs are free to supply their own linker scripts. ld will 4 4 * combine them automatically. 5 5 */ 6 - #ifdef CONFIG_CFI_CLANG 7 - # include <asm/page.h> 8 - # define ALIGN_CFI ALIGN(PAGE_SIZE) 9 - # define SANITIZER_DISCARDS *(.eh_frame) 10 - #else 11 - # define ALIGN_CFI 12 - # define SANITIZER_DISCARDS 13 - #endif 14 - 15 6 SECTIONS { 16 7 /DISCARD/ : { 17 8 *(.discard) 18 9 *(.discard.*) 19 - SANITIZER_DISCARDS 20 10 } 21 11 22 12 __ksymtab 0 : { *(SORT(___ksymtab+*)) } ··· 22 32 __jump_table 0 : ALIGN(8) { KEEP(*(__jump_table)) } 23 33 24 34 __patchable_function_entries : { *(__patchable_function_entries) } 35 + 36 + #ifdef CONFIG_ARCH_USES_CFI_TRAPS 37 + __kcfi_traps : { KEEP(*(.kcfi_traps)) } 38 + #endif 25 39 26 40 #ifdef CONFIG_LTO_CLANG 27 41 /* ··· 46 52 .rodata : { 47 53 *(.rodata .rodata.[0-9a-zA-Z_]*) 48 54 *(.rodata..L*) 49 - } 50 - 51 - /* 52 - * With CONFIG_CFI_CLANG, we assume __cfi_check is at the beginning 53 - * of the .text section, and is aligned to PAGE_SIZE. 54 - */ 55 - .text : ALIGN_CFI { 56 - *(.text.__cfi_check) 57 - *(.text .text.[0-9a-zA-Z_]* .text..L.cfi*) 58 55 } 59 56 #endif 60 57 }