| /* SPDX-License-Identifier: GPL-2.0 */ |
| #ifndef _ASM_X86_CFI_H |
| #define _ASM_X86_CFI_H |
| |
| /* |
| * Clang Control Flow Integrity (CFI) support. |
| * |
| * Copyright (C) 2022 Google LLC |
| */ |
| #include <linux/bug.h> |
| #include <asm/ibt.h> |
| |
| /* |
| * An overview of the various calling conventions... |
| * |
| * Traditional: |
| * |
| * foo: |
| * ... code here ... |
| * ret |
| * |
| * direct caller: |
| * call foo |
| * |
| * indirect caller: |
| * lea foo(%rip), %r11 |
| * ... |
| * call *%r11 |
| * |
| * |
| * IBT: |
| * |
| * foo: |
| * endbr64 |
| * ... code here ... |
| * ret |
| * |
| * direct caller: |
| * call foo / call foo+4 |
| * |
| * indirect caller: |
| * lea foo(%rip), %r11 |
| * ... |
| * call *%r11 |
| * |
| * |
| * kCFI: |
| * |
| * __cfi_foo: |
| * movl $0x12345678, %eax |
| * # 11 nops when CONFIG_CALL_PADDING |
| * foo: |
| * endbr64 # when IBT |
| * ... code here ... |
| * ret |
| * |
| * direct call: |
| * call foo # / call foo+4 when IBT |
| * |
| * indirect call: |
| * lea foo(%rip), %r11 |
| * ... |
| * movl $(-0x12345678), %r10d |
| * addl -4(%r11), %r10d # -15 when CONFIG_CALL_PADDING |
| * jz 1f |
| * ud2 |
| * 1:call *%r11 |
| * |
| * |
| * FineIBT (builds as kCFI + CALL_PADDING + IBT + RETPOLINE and runtime patches into): |
| * |
| * __cfi_foo: |
| * endbr64 |
| * subl 0x12345678, %r10d |
| * jz foo |
| * ud2 |
| * nop |
| * foo: |
| * osp nop3 # was endbr64 |
| * ... code here ... |
| * ret |
| * |
| * direct caller: |
| * call foo / call foo+4 |
| * |
| * indirect caller: |
| * lea foo(%rip), %r11 |
| * ... |
| * movl $0x12345678, %r10d |
| * subl $16, %r11 |
| * nop4 |
| * call *%r11 |
| * |
| */ |
| enum cfi_mode { |
| CFI_DEFAULT, /* FineIBT if hardware has IBT, otherwise kCFI */ |
| CFI_OFF, /* Taditional / IBT depending on .config */ |
| CFI_KCFI, /* Optionally CALL_PADDING, IBT, RETPOLINE */ |
| CFI_FINEIBT, /* see arch/x86/kernel/alternative.c */ |
| }; |
| |
| extern enum cfi_mode cfi_mode; |
| |
| struct pt_regs; |
| |
| #ifdef CONFIG_CFI_CLANG |
| enum bug_trap_type handle_cfi_failure(struct pt_regs *regs); |
| #define __bpfcall |
| extern u32 cfi_bpf_hash; |
| extern u32 cfi_bpf_subprog_hash; |
| |
| static inline int cfi_get_offset(void) |
| { |
| switch (cfi_mode) { |
| case CFI_FINEIBT: |
| return 16; |
| case CFI_KCFI: |
| if (IS_ENABLED(CONFIG_CALL_PADDING)) |
| return 16; |
| return 5; |
| default: |
| return 0; |
| } |
| } |
| #define cfi_get_offset cfi_get_offset |
| |
| extern u32 cfi_get_func_hash(void *func); |
| |
| #else |
| static inline enum bug_trap_type handle_cfi_failure(struct pt_regs *regs) |
| { |
| return BUG_TRAP_TYPE_NONE; |
| } |
| #define cfi_bpf_hash 0U |
| #define cfi_bpf_subprog_hash 0U |
| static inline u32 cfi_get_func_hash(void *func) |
| { |
| return 0; |
| } |
| #endif /* CONFIG_CFI_CLANG */ |
| |
| #if HAS_KERNEL_IBT == 1 |
| #define CFI_NOSEAL(x) asm(IBT_NOSEAL(__stringify(x))) |
| #endif |
| |
| #endif /* _ASM_X86_CFI_H */ |