Skip to content

Commit

Permalink
CFI: Add support for gcc CFI in aarch64
Browse files Browse the repository at this point in the history
Based on Sami's patch[1], this patch makes the corresponding kernel
configuration of CFI available when compiling the kernel with the gcc[2].

The code after enabling cfi is as follows:

int (*p)(void);
int func (int)
{
	p();
}

__cfi_func:
        .4byte 0x439d3502
func:
        ......
        adrp    x0, p
        add     x0, x0, :lo12:p
        mov     w1, 23592
        movk    w1, 0x4601, lsl 16
        cmp     w0, w1
        beq     .L2
        ......
        bl      cfi_check_failed
.L2:
        blr     x19
        ret

In the compiler part[4], there are some differences from Sami's
implementation[3], mainly including:

1. When a typeid mismatch is detected, the cfi_check_failed function
   will be called instead of the brk instruction. This function needs
   to be implemented by the compiler user.
   If there are user mode programs or other systems that want to use
   this feature, it may be more convenient to use a callback (so this
   compilation option is set to -fsanitize=cfi instead of kcfi).

2. A reserved typeid (such as 0x0U on the aarch64 platform) is always
   inserted in front of functions that should not be called indirectly.
   Functions that can be called indirectly will not use this hash value,
   which prevents instructions/data before the function from being used
   as a typeid by an attacker.

3. Some bits are ignored in the typeid to avoid conflicts between the
   typeid and the instruction set of a specific platform, thereby
   preventing an attacker from bypassing the CFI check by using the
   instruction as a typeid, such as on the aarch64 platform:
   * If the following instruction sequence exists:
	  400620:       a9be7bfd        stp     x29, x30, [sp, #-32]!
	  400624:       910003fd        mov     x29, sp
	  400628:       f9000bf3        str     x19, [sp, torvalds#16]
   * If the expected typeid of the indirect call is exactly 0x910003fd,
     the attacker can jump to the next instruction position of any
     "mov x29,sp" instruction (such as 0x400628 here).

4. Insert a symbol __cfi_<function> before each function's typeid,
   which may be helpful for fine-grained KASLR implementations (or not?).

5. The current implementation of gcc only supports the aarch64 platform.

This produces the following oops on CFI failure (generated using lkdtm):

/kselftest_install/lkdtm # ./CFI_FORWARD_PROTO.sh
[   74.856516] lkdtm: Performing direct entry CFI_FORWARD_PROTO
[   74.856878] lkdtm: Calling matched prototype ...
[   74.857011] lkdtm: Calling mismatched prototype ...
[   74.857133] CFI failure at lkdtm_indirect_call+0x30/0x50 (target: lkdtm_increment_int+0x0/0x1c; expected type: 0xc59c68f1)
[   74.858185] Kernel panic - not syncing: Oops - CFI
[   74.859240] CPU: 0 PID: 129 Comm: cat Not tainted 6.0.0-rc4-00024-g32bf7f14f497-dirty torvalds#150
[   74.859481] Hardware name: linux,dummy-virt (DT)
[   74.859795] Call trace:
[   74.859959]  dump_backtrace.part.0+0xcc/0xe0
[   74.860212]  show_stack+0x18/0x5c
[   74.860327]  dump_stack_lvl+0x64/0x84
[   74.860398]  dump_stack+0x18/0x38
[   74.860443]  panic+0x170/0x36c
[   74.860496]  cfi_check_failed+0x38/0x44
[   74.860564]  lkdtm_indirect_call+0x30/0x50
[   74.860614]  lkdtm_CFI_FORWARD_PROTO+0x3c/0x6c
[   74.860701]  lkdtm_do_action+0x44/0x58
[   74.860764]  direct_entry+0x148/0x160
[   74.860814]  full_proxy_write+0x74/0xe0
[   74.860874]  vfs_write+0xd8/0x2d0
[   74.860942]  ksys_write+0x70/0x110
[   74.861000]  __arm64_sys_write+0x1c/0x30
[   74.861067]  invoke_syscall+0x5c/0x140
[   74.861117]  el0_svc_common.constprop.0+0x44/0xf0
[   74.861190]  do_el0_svc+0x2c/0xc0
[   74.861233]  el0_svc+0x20/0x60
[   74.861287]  el0t_64_sync_handler+0xf4/0x124
[   74.861340]  el0t_64_sync+0x160/0x164
[   74.861782] SMP: stopping secondary CPUs
[   74.862336] Kernel Offset: disabled
[   74.862439] CPU features: 0x0000,00075024,699418af
[   74.862799] Memory Limit: none
[   74.863373] ---[ end Kernel panic - not syncing: Oops - CFI ]---

The gcc-related patches[4] are based on tag: releases/gcc-12.2.0.

Any suggestion please let me know :).

Thanks, Dan.

[1] https://lore.kernel.org/all/20220908215504.3686827-1-samitolvanen@google.com/
[2] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=107048
[3] https://reviews.llvm.org/D119296
[4] https://lore.kernel.org/linux-hardening/20221219055431.22596-1-ashimida.1990@gmail.com/

Signed-off-by: Dan Li <ashimida.1990@gmail.com>
  • Loading branch information
ashimida123 authored and intel-lab-lkp committed Dec 19, 2022
1 parent 731c4ea commit 934502d
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 6 deletions.
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -1016,6 +1016,12 @@ KBUILD_CFLAGS += $(CC_FLAGS_CFI)
export CC_FLAGS_CFI
endif

ifdef CONFIG_CFI_GCC
CC_FLAGS_CFI := -fsanitize=cfi
KBUILD_CFLAGS += $(CC_FLAGS_CFI)
export CC_FLAGS_CFI
endif

ifdef CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_64B
KBUILD_CFLAGS += -falign-functions=64
endif
Expand Down
24 changes: 23 additions & 1 deletion arch/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -762,9 +762,31 @@ config CFI_CLANG

https://clang.llvm.org/docs/ControlFlowIntegrity.html

config ARCH_SUPPORTS_CFI_GCC
bool
help
An architecture should select this option if it can support GCC's
Control-Flow Integrity (CFI) checking.

config CFI_GCC
bool "Use GCC's Control Flow Integrity (CFI)"
depends on ARCH_SUPPORTS_CFI_GCC
depends on $(cc-option,-fsanitize=cfi)
help
This option enables GCC’s forward-edge Control Flow Integrity
(CFI) checking, where the compiler injects a runtime check to each
indirect function call to ensure the target is a valid function with
the correct static type. This restricts possible call targets and
makes it more difficult for an attacker to exploit bugs that allow
the modification of stored function pointers. More information can be
found from the compiler's documentation:

- Clang: https://clang.llvm.org/docs/ControlFlowIntegrity.html
- GCC: https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html#Instrumentation-Options

config CFI_PERMISSIVE
bool "Use CFI in permissive mode"
depends on CFI_CLANG
depends on CFI_CLANG || CFI_GCC
help
When selected, Control Flow Integrity (CFI) violations result in a
warning instead of a kernel panic. This option should only be used
Expand Down
1 change: 1 addition & 0 deletions arch/arm64/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ config ARM64
select ARCH_SUPPORTS_LTO_CLANG if CPU_LITTLE_ENDIAN
select ARCH_SUPPORTS_LTO_CLANG_THIN
select ARCH_SUPPORTS_CFI_CLANG
select ARCH_SUPPORTS_CFI_GCC
select ARCH_SUPPORTS_ATOMIC_RMW
select ARCH_SUPPORTS_INT128 if CC_HAS_INT128
select ARCH_SUPPORTS_NUMA_BALANCING
Expand Down
15 changes: 11 additions & 4 deletions include/linux/cfi_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,25 @@
#ifdef __ASSEMBLY__
#include <linux/linkage.h>

#ifdef CONFIG_CFI_CLANG
#if defined(CONFIG_CFI_CLANG) || defined(CONFIG_CFI_GCC)
/*
* Use the __kcfi_typeid_<function> type identifier symbol to
* Use the __[k]cfi_typeid_<function> type identifier symbol to
* annotate indirectly called assembly functions. The compiler emits
* these symbols for all address-taken function declarations in C
* code.
*/
#ifndef __CFI_TYPE

#ifdef CONFIG_CFI_GCC
#define __CFI_TYPE(name) \
.4byte __cfi_typeid_##name
#else
#define __CFI_TYPE(name) \
.4byte __kcfi_typeid_##name
#endif

#endif

#define SYM_TYPED_ENTRY(name, linkage, align...) \
linkage(name) ASM_NL \
align ASM_NL \
Expand All @@ -29,12 +36,12 @@
#define SYM_TYPED_START(name, linkage, align...) \
SYM_TYPED_ENTRY(name, linkage, align)

#else /* CONFIG_CFI_CLANG */
#else /* defined(CONFIG_CFI_CLANG) || defined(CONFIG_CFI_GCC) */

#define SYM_TYPED_START(name, linkage, align...) \
SYM_START(name, linkage, align)

#endif /* CONFIG_CFI_CLANG */
#endif /* defined(CONFIG_CFI_CLANG) || defined(CONFIG_CFI_GCC) */

#ifndef SYM_TYPED_FUNC_START
#define SYM_TYPED_FUNC_START(name) \
Expand Down
4 changes: 4 additions & 0 deletions include/linux/compiler-gcc.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@
#define __noscs __attribute__((__no_sanitize__("shadow-call-stack")))
#endif

#ifdef CONFIG_CFI_GCC
#define __nocfi __attribute__((no_sanitize("cfi")))
#endif

#if __has_attribute(__no_sanitize_address__)
#define __no_sanitize_address __attribute__((no_sanitize_address))
#else
Expand Down
1 change: 1 addition & 0 deletions kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ obj-$(CONFIG_SHADOW_CALL_STACK) += scs.o
obj-$(CONFIG_HAVE_STATIC_CALL) += static_call.o
obj-$(CONFIG_HAVE_STATIC_CALL_INLINE) += static_call_inline.o
obj-$(CONFIG_CFI_CLANG) += cfi.o
obj-$(CONFIG_CFI_GCC) += cfi.o

obj-$(CONFIG_PERF_EVENTS) += events/

Expand Down
23 changes: 23 additions & 0 deletions kernel/cfi.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ enum bug_trap_type report_cfi_failure(struct pt_regs *regs, unsigned long addr,
return BUG_TRAP_TYPE_BUG;
}

#ifdef CONFIG_CFI_CLANG
#ifdef CONFIG_ARCH_USES_CFI_TRAPS
static inline unsigned long trap_address(s32 *p)
{
Expand Down Expand Up @@ -99,3 +100,25 @@ bool is_cfi_trap(unsigned long addr)
return is_module_cfi_trap(addr);
}
#endif /* CONFIG_ARCH_USES_CFI_TRAPS */
#endif /* CONFIG_CFI_CLANG */


#ifdef CONFIG_CFI_GCC
void cfi_check_failed(u32 caller_hash, u32 callee_hash, void *callee_addr)
{
unsigned long pc, target;

pc = (unsigned long)__builtin_return_address(0);
target = (unsigned long)callee_addr;

switch (report_cfi_failure(NULL, pc, &target, caller_hash)) {
case BUG_TRAP_TYPE_WARN:
break;

default:
panic("Oops - CFI");
}
}
EXPORT_SYMBOL(cfi_check_failed);

#endif /* CONFIG_CFI_GCC */
4 changes: 3 additions & 1 deletion scripts/kallsyms.c
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,9 @@ static bool is_ignored_symbol(const char *name, char type)
"__ThumbV7PILongThunk_",
"__LA25Thunk_", /* mips lld */
"__microLA25Thunk_",
"__kcfi_typeid_", /* CFI type identifiers */
"__kcfi_typeid_", /* CFI type identifiers in Clang */
"__cfi_", /* CFI type identifiers in GCC */
"__pi___cfi", /* CFI type identifiers in GCC */
NULL
};

Expand Down

0 comments on commit 934502d

Please sign in to comment.