* [PATCH bpf-next v12 0/3] Support kCFI + BPF on arm64
@ 2025-07-21 20:20 Sami Tolvanen
2025-07-21 20:20 ` [PATCH bpf-next v12 1/3] cfi: add C CFI type macro Sami Tolvanen
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Sami Tolvanen @ 2025-07-21 20:20 UTC (permalink / raw)
To: bpf, Puranjay Mohan, Alexei Starovoitov, Daniel Borkmann
Cc: Catalin Marinas, Will Deacon, Andrii Nakryiko, Mark Rutland,
linux-arm-kernel, linux-kernel, Maxwell Bland, Sami Tolvanen
Hi folks,
These patches add KCFI types to arm64 BPF JIT output. Puranjay and
Maxwell have been working on this for some time now, but I haven't
seen any progress since June 2024, so I decided to pick up the latest
version[1] posted by Maxwell and fix the few remaining issues I
noticed. I confirmed that with these patches applied, I no longer see
CFI failures in jitted code when running BPF self-tests on arm64.
[1] https://lore.kernel.org/linux-arm-kernel/ptrugmna4xb5o5lo4xislf4rlz7avdmd4pfho5fjwtjj7v422u@iqrwfrbwuxrq/
Sami
---
v12:
- Fixed sparse warnings and 32-bit ARM build errors.
v11: https://lore.kernel.org/bpf/20250718223345.1075521-5-samitolvanen@google.com/
- Moved cfi_get_func_hash to a static inline with an ifdef guard.
- Picked by Will's Acked-by tags.
v10: https://lore.kernel.org/bpf/20250715225733.3921432-5-samitolvanen@google.com/
- Rebased to bpf-next/master again.
- Added a patch to moved duplicate type hash variables and helper
functions to common CFI code.
v9: https://lore.kernel.org/bpf/20250505223437.3722164-4-samitolvanen@google.com/
- Rebased to bpf-next/master to fix merge x86 merge conflicts.
- Fixed checkpatch warnings about Co-developed-by tags and including
<asm/cfi.h>.
- Picked up Tested-by tags.
v8: https://lore.kernel.org/bpf/20250310222942.1988975-4-samitolvanen@google.com/
- Changed DEFINE_CFI_TYPE to use .4byte to match __CFI_TYPE.
- Changed cfi_get_func_hash() to again use get_kernel_nofault().
- Fixed a panic in bpf_jit_free() by resetting prog->bpf_func before
calling bpf_jit_binary_pack_hdr().
---
Mark Rutland (1):
cfi: add C CFI type macro
Puranjay Mohan (1):
arm64/cfi,bpf: Support kCFI + BPF on arm64
Sami Tolvanen (1):
cfi: Move BPF CFI types and helpers to generic code
arch/arm64/include/asm/cfi.h | 7 +++++
arch/arm64/net/bpf_jit_comp.c | 22 +++++++++++++--
arch/riscv/include/asm/cfi.h | 16 -----------
arch/riscv/kernel/cfi.c | 53 -----------------------------------
arch/x86/include/asm/cfi.h | 10 ++-----
arch/x86/kernel/alternative.c | 37 ------------------------
include/linux/cfi.h | 47 +++++++++++++++++++++++++------
include/linux/cfi_types.h | 23 +++++++++++++++
kernel/cfi.c | 15 ++++++++++
9 files changed, 105 insertions(+), 125 deletions(-)
create mode 100644 arch/arm64/include/asm/cfi.h
base-commit: 42be23e8f2dcb100cb9944b2b54b6bf41aff943d
--
2.50.0.727.gbf7dc18ff4-goog
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH bpf-next v12 1/3] cfi: add C CFI type macro
2025-07-21 20:20 [PATCH bpf-next v12 0/3] Support kCFI + BPF on arm64 Sami Tolvanen
@ 2025-07-21 20:20 ` Sami Tolvanen
2025-07-21 20:20 ` [PATCH bpf-next v12 2/3] cfi: Move BPF CFI types and helpers to generic code Sami Tolvanen
2025-07-21 20:20 ` [PATCH bpf-next v12 3/3] arm64/cfi,bpf: Support kCFI + BPF on arm64 Sami Tolvanen
2 siblings, 0 replies; 7+ messages in thread
From: Sami Tolvanen @ 2025-07-21 20:20 UTC (permalink / raw)
To: bpf, Puranjay Mohan, Alexei Starovoitov, Daniel Borkmann
Cc: Catalin Marinas, Will Deacon, Andrii Nakryiko, Mark Rutland,
linux-arm-kernel, linux-kernel, Maxwell Bland, Sami Tolvanen,
Dao Huang
From: Mark Rutland <mark.rutland@arm.com>
Currently x86 and riscv open-code 4 instances of the same logic to
define a u32 variable with the KCFI typeid of a given function.
Replace the duplicate logic with a common macro.
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Co-developed-by: Maxwell Bland <mbland@motorola.com>
Signed-off-by: Maxwell Bland <mbland@motorola.com>
Co-developed-by: Sami Tolvanen <samitolvanen@google.com>
Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Tested-by: Dao Huang <huangdao1@oppo.com>
Acked-by: Will Deacon <will@kernel.org>
---
arch/riscv/kernel/cfi.c | 35 +++--------------------------------
arch/x86/kernel/alternative.c | 31 +++----------------------------
include/linux/cfi_types.h | 23 +++++++++++++++++++++++
3 files changed, 29 insertions(+), 60 deletions(-)
diff --git a/arch/riscv/kernel/cfi.c b/arch/riscv/kernel/cfi.c
index 64bdd3e1ab8c..e7aec5f36dd5 100644
--- a/arch/riscv/kernel/cfi.c
+++ b/arch/riscv/kernel/cfi.c
@@ -4,6 +4,7 @@
*
* Copyright (C) 2023 Google LLC
*/
+#include <linux/cfi_types.h>
#include <linux/cfi.h>
#include <asm/insn.h>
@@ -82,41 +83,11 @@ struct bpf_insn;
/* Must match bpf_func_t / DEFINE_BPF_PROG_RUN() */
extern unsigned int __bpf_prog_runX(const void *ctx,
const struct bpf_insn *insn);
-
-/*
- * Force a reference to the external symbol so the compiler generates
- * __kcfi_typid.
- */
-__ADDRESSABLE(__bpf_prog_runX);
-
-/* u32 __ro_after_init cfi_bpf_hash = __kcfi_typeid___bpf_prog_runX; */
-asm (
-" .pushsection .data..ro_after_init,\"aw\",@progbits \n"
-" .type cfi_bpf_hash,@object \n"
-" .globl cfi_bpf_hash \n"
-" .p2align 2, 0x0 \n"
-"cfi_bpf_hash: \n"
-" .word __kcfi_typeid___bpf_prog_runX \n"
-" .size cfi_bpf_hash, 4 \n"
-" .popsection \n"
-);
+DEFINE_CFI_TYPE(cfi_bpf_hash, __bpf_prog_runX);
/* Must match bpf_callback_t */
extern u64 __bpf_callback_fn(u64, u64, u64, u64, u64);
-
-__ADDRESSABLE(__bpf_callback_fn);
-
-/* u32 __ro_after_init cfi_bpf_subprog_hash = __kcfi_typeid___bpf_callback_fn; */
-asm (
-" .pushsection .data..ro_after_init,\"aw\",@progbits \n"
-" .type cfi_bpf_subprog_hash,@object \n"
-" .globl cfi_bpf_subprog_hash \n"
-" .p2align 2, 0x0 \n"
-"cfi_bpf_subprog_hash: \n"
-" .word __kcfi_typeid___bpf_callback_fn \n"
-" .size cfi_bpf_subprog_hash, 4 \n"
-" .popsection \n"
-);
+DEFINE_CFI_TYPE(cfi_bpf_subprog_hash, __bpf_callback_fn);
u32 cfi_get_func_hash(void *func)
{
diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c
index ea1d984166cd..a555665b4d9c 100644
--- a/arch/x86/kernel/alternative.c
+++ b/arch/x86/kernel/alternative.c
@@ -2,6 +2,7 @@
#define pr_fmt(fmt) "SMP alternatives: " fmt
#include <linux/mmu_context.h>
+#include <linux/cfi_types.h>
#include <linux/perf_event.h>
#include <linux/vmalloc.h>
#include <linux/memory.h>
@@ -1189,37 +1190,11 @@ struct bpf_insn;
/* Must match bpf_func_t / DEFINE_BPF_PROG_RUN() */
extern unsigned int __bpf_prog_runX(const void *ctx,
const struct bpf_insn *insn);
-
-KCFI_REFERENCE(__bpf_prog_runX);
-
-/* u32 __ro_after_init cfi_bpf_hash = __kcfi_typeid___bpf_prog_runX; */
-asm (
-" .pushsection .data..ro_after_init,\"aw\",@progbits \n"
-" .type cfi_bpf_hash,@object \n"
-" .globl cfi_bpf_hash \n"
-" .p2align 2, 0x0 \n"
-"cfi_bpf_hash: \n"
-" .long __kcfi_typeid___bpf_prog_runX \n"
-" .size cfi_bpf_hash, 4 \n"
-" .popsection \n"
-);
+DEFINE_CFI_TYPE(cfi_bpf_hash, __bpf_prog_runX);
/* Must match bpf_callback_t */
extern u64 __bpf_callback_fn(u64, u64, u64, u64, u64);
-
-KCFI_REFERENCE(__bpf_callback_fn);
-
-/* u32 __ro_after_init cfi_bpf_subprog_hash = __kcfi_typeid___bpf_callback_fn; */
-asm (
-" .pushsection .data..ro_after_init,\"aw\",@progbits \n"
-" .type cfi_bpf_subprog_hash,@object \n"
-" .globl cfi_bpf_subprog_hash \n"
-" .p2align 2, 0x0 \n"
-"cfi_bpf_subprog_hash: \n"
-" .long __kcfi_typeid___bpf_callback_fn \n"
-" .size cfi_bpf_subprog_hash, 4 \n"
-" .popsection \n"
-);
+DEFINE_CFI_TYPE(cfi_bpf_subprog_hash, __bpf_callback_fn);
u32 cfi_get_func_hash(void *func)
{
diff --git a/include/linux/cfi_types.h b/include/linux/cfi_types.h
index 6b8713675765..685f7181780f 100644
--- a/include/linux/cfi_types.h
+++ b/include/linux/cfi_types.h
@@ -41,5 +41,28 @@
SYM_TYPED_START(name, SYM_L_GLOBAL, SYM_A_ALIGN)
#endif
+#else /* __ASSEMBLY__ */
+
+#ifdef CONFIG_CFI_CLANG
+#define DEFINE_CFI_TYPE(name, func) \
+ /* \
+ * Force a reference to the function so the compiler generates \
+ * __kcfi_typeid_<func>. \
+ */ \
+ __ADDRESSABLE(func); \
+ /* u32 name __ro_after_init = __kcfi_typeid_<func> */ \
+ extern u32 name; \
+ asm ( \
+ " .pushsection .data..ro_after_init,\"aw\",\%progbits \n" \
+ " .type " #name ",\%object \n" \
+ " .globl " #name " \n" \
+ " .p2align 2, 0x0 \n" \
+ #name ": \n" \
+ " .4byte __kcfi_typeid_" #func " \n" \
+ " .size " #name ", 4 \n" \
+ " .popsection \n" \
+ );
+#endif
+
#endif /* __ASSEMBLY__ */
#endif /* _LINUX_CFI_TYPES_H */
--
2.50.0.727.gbf7dc18ff4-goog
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH bpf-next v12 2/3] cfi: Move BPF CFI types and helpers to generic code
2025-07-21 20:20 [PATCH bpf-next v12 0/3] Support kCFI + BPF on arm64 Sami Tolvanen
2025-07-21 20:20 ` [PATCH bpf-next v12 1/3] cfi: add C CFI type macro Sami Tolvanen
@ 2025-07-21 20:20 ` Sami Tolvanen
2025-07-21 20:20 ` [PATCH bpf-next v12 3/3] arm64/cfi,bpf: Support kCFI + BPF on arm64 Sami Tolvanen
2 siblings, 0 replies; 7+ messages in thread
From: Sami Tolvanen @ 2025-07-21 20:20 UTC (permalink / raw)
To: bpf, Puranjay Mohan, Alexei Starovoitov, Daniel Borkmann
Cc: Catalin Marinas, Will Deacon, Andrii Nakryiko, Mark Rutland,
linux-arm-kernel, linux-kernel, Maxwell Bland, Sami Tolvanen
Instead of duplicating the same code for each architecture, move
the CFI type hash variables for BPF function types and related
helper functions to generic CFI code, and allow architectures to
override the function definitions if needed.
Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
---
arch/riscv/include/asm/cfi.h | 16 ------------
arch/riscv/kernel/cfi.c | 24 ------------------
arch/x86/include/asm/cfi.h | 10 ++------
arch/x86/kernel/alternative.c | 12 ---------
include/linux/cfi.h | 47 +++++++++++++++++++++++++++++------
kernel/cfi.c | 15 +++++++++++
6 files changed, 56 insertions(+), 68 deletions(-)
diff --git a/arch/riscv/include/asm/cfi.h b/arch/riscv/include/asm/cfi.h
index fb9696d7a3f2..4508aaa7a2fd 100644
--- a/arch/riscv/include/asm/cfi.h
+++ b/arch/riscv/include/asm/cfi.h
@@ -14,27 +14,11 @@ struct pt_regs;
#ifdef CONFIG_CFI_CLANG
enum bug_trap_type handle_cfi_failure(struct pt_regs *regs);
#define __bpfcall
-static inline int cfi_get_offset(void)
-{
- return 4;
-}
-
-#define cfi_get_offset cfi_get_offset
-extern u32 cfi_bpf_hash;
-extern u32 cfi_bpf_subprog_hash;
-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 */
#endif /* _ASM_RISCV_CFI_H */
diff --git a/arch/riscv/kernel/cfi.c b/arch/riscv/kernel/cfi.c
index e7aec5f36dd5..6ec9dbd7292e 100644
--- a/arch/riscv/kernel/cfi.c
+++ b/arch/riscv/kernel/cfi.c
@@ -4,7 +4,6 @@
*
* Copyright (C) 2023 Google LLC
*/
-#include <linux/cfi_types.h>
#include <linux/cfi.h>
#include <asm/insn.h>
@@ -76,26 +75,3 @@ enum bug_trap_type handle_cfi_failure(struct pt_regs *regs)
return report_cfi_failure(regs, regs->epc, &target, type);
}
-
-#ifdef CONFIG_CFI_CLANG
-struct bpf_insn;
-
-/* Must match bpf_func_t / DEFINE_BPF_PROG_RUN() */
-extern unsigned int __bpf_prog_runX(const void *ctx,
- const struct bpf_insn *insn);
-DEFINE_CFI_TYPE(cfi_bpf_hash, __bpf_prog_runX);
-
-/* Must match bpf_callback_t */
-extern u64 __bpf_callback_fn(u64, u64, u64, u64, u64);
-DEFINE_CFI_TYPE(cfi_bpf_subprog_hash, __bpf_callback_fn);
-
-u32 cfi_get_func_hash(void *func)
-{
- u32 hash;
-
- if (get_kernel_nofault(hash, func - cfi_get_offset()))
- return 0;
-
- return hash;
-}
-#endif
diff --git a/arch/x86/include/asm/cfi.h b/arch/x86/include/asm/cfi.h
index 3e51ba459154..1751f1eb95ef 100644
--- a/arch/x86/include/asm/cfi.h
+++ b/arch/x86/include/asm/cfi.h
@@ -116,8 +116,6 @@ 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)
{
@@ -135,6 +133,8 @@ static inline int cfi_get_offset(void)
#define cfi_get_offset cfi_get_offset
extern u32 cfi_get_func_hash(void *func);
+#define cfi_get_func_hash cfi_get_func_hash
+
extern int cfi_get_func_arity(void *func);
#ifdef CONFIG_FINEIBT
@@ -153,12 +153,6 @@ 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;
-}
static inline int cfi_get_func_arity(void *func)
{
return 0;
diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c
index a555665b4d9c..9f6b7dab2d9a 100644
--- a/arch/x86/kernel/alternative.c
+++ b/arch/x86/kernel/alternative.c
@@ -2,7 +2,6 @@
#define pr_fmt(fmt) "SMP alternatives: " fmt
#include <linux/mmu_context.h>
-#include <linux/cfi_types.h>
#include <linux/perf_event.h>
#include <linux/vmalloc.h>
#include <linux/memory.h>
@@ -1185,17 +1184,6 @@ bool cfi_bhi __ro_after_init = false;
#endif
#ifdef CONFIG_CFI_CLANG
-struct bpf_insn;
-
-/* Must match bpf_func_t / DEFINE_BPF_PROG_RUN() */
-extern unsigned int __bpf_prog_runX(const void *ctx,
- const struct bpf_insn *insn);
-DEFINE_CFI_TYPE(cfi_bpf_hash, __bpf_prog_runX);
-
-/* Must match bpf_callback_t */
-extern u64 __bpf_callback_fn(u64, u64, u64, u64, u64);
-DEFINE_CFI_TYPE(cfi_bpf_subprog_hash, __bpf_callback_fn);
-
u32 cfi_get_func_hash(void *func)
{
u32 hash;
diff --git a/include/linux/cfi.h b/include/linux/cfi.h
index 1db17ecbb86c..52a98886a455 100644
--- a/include/linux/cfi.h
+++ b/include/linux/cfi.h
@@ -11,16 +11,9 @@
#include <linux/module.h>
#include <asm/cfi.h>
+#ifdef CONFIG_CFI_CLANG
extern bool cfi_warn;
-#ifndef cfi_get_offset
-static inline int cfi_get_offset(void)
-{
- return 0;
-}
-#endif
-
-#ifdef CONFIG_CFI_CLANG
enum bug_trap_type report_cfi_failure(struct pt_regs *regs, unsigned long addr,
unsigned long *target, u32 type);
@@ -29,6 +22,44 @@ static inline enum bug_trap_type report_cfi_failure_noaddr(struct pt_regs *regs,
{
return report_cfi_failure(regs, addr, NULL, 0);
}
+
+#ifndef cfi_get_offset
+/*
+ * Returns the CFI prefix offset. By default, the compiler emits only
+ * a 4-byte CFI type hash before the function. If an architecture
+ * uses -fpatchable-function-entry=N,M where M>0 to change the prefix
+ * offset, they must override this function.
+ */
+static inline int cfi_get_offset(void)
+{
+ return 4;
+}
+#endif
+
+#ifndef cfi_get_func_hash
+static inline u32 cfi_get_func_hash(void *func)
+{
+ u32 hash;
+
+ if (get_kernel_nofault(hash, func - cfi_get_offset()))
+ return 0;
+
+ return hash;
+}
+#endif
+
+/* CFI type hashes for BPF function types */
+extern u32 cfi_bpf_hash;
+extern u32 cfi_bpf_subprog_hash;
+
+#else /* CONFIG_CFI_CLANG */
+
+static inline int cfi_get_offset(void) { return 0; }
+static inline u32 cfi_get_func_hash(void *func) { return 0; }
+
+#define cfi_bpf_hash 0U
+#define cfi_bpf_subprog_hash 0U
+
#endif /* CONFIG_CFI_CLANG */
#ifdef CONFIG_ARCH_USES_CFI_TRAPS
diff --git a/kernel/cfi.c b/kernel/cfi.c
index 422fa4f958ae..4dad04ead06c 100644
--- a/kernel/cfi.c
+++ b/kernel/cfi.c
@@ -5,6 +5,8 @@
* Copyright (C) 2022 Google LLC
*/
+#include <linux/bpf.h>
+#include <linux/cfi_types.h>
#include <linux/cfi.h>
bool cfi_warn __ro_after_init = IS_ENABLED(CONFIG_CFI_PERMISSIVE);
@@ -27,6 +29,19 @@ enum bug_trap_type report_cfi_failure(struct pt_regs *regs, unsigned long addr,
return BUG_TRAP_TYPE_BUG;
}
+/*
+ * Declare two non-existent functions with types that match bpf_func_t and
+ * bpf_callback_t pointers, and use DEFINE_CFI_TYPE to define type hash
+ * variables for each function type. The cfi_bpf_* variables are used by
+ * arch-specific BPF JIT implementations to ensure indirectly callable JIT
+ * code has matching CFI type hashes.
+ */
+extern typeof(*(bpf_func_t)0) __bpf_prog_runX;
+DEFINE_CFI_TYPE(cfi_bpf_hash, __bpf_prog_runX);
+
+extern typeof(*(bpf_callback_t)0) __bpf_callback_fn;
+DEFINE_CFI_TYPE(cfi_bpf_subprog_hash, __bpf_callback_fn);
+
#ifdef CONFIG_ARCH_USES_CFI_TRAPS
static inline unsigned long trap_address(s32 *p)
{
--
2.50.0.727.gbf7dc18ff4-goog
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH bpf-next v12 3/3] arm64/cfi,bpf: Support kCFI + BPF on arm64
2025-07-21 20:20 [PATCH bpf-next v12 0/3] Support kCFI + BPF on arm64 Sami Tolvanen
2025-07-21 20:20 ` [PATCH bpf-next v12 1/3] cfi: add C CFI type macro Sami Tolvanen
2025-07-21 20:20 ` [PATCH bpf-next v12 2/3] cfi: Move BPF CFI types and helpers to generic code Sami Tolvanen
@ 2025-07-21 20:20 ` Sami Tolvanen
2025-07-22 3:44 ` Xu Kuohai
2 siblings, 1 reply; 7+ messages in thread
From: Sami Tolvanen @ 2025-07-21 20:20 UTC (permalink / raw)
To: bpf, Puranjay Mohan, Alexei Starovoitov, Daniel Borkmann
Cc: Catalin Marinas, Will Deacon, Andrii Nakryiko, Mark Rutland,
linux-arm-kernel, linux-kernel, Maxwell Bland, Puranjay Mohan,
Sami Tolvanen, Dao Huang
From: Puranjay Mohan <puranjay12@gmail.com>
Currently, bpf_dispatcher_*_func() is marked with `__nocfi` therefore
calling BPF programs from this interface doesn't cause CFI warnings.
When BPF programs are called directly from C: from BPF helpers or
struct_ops, CFI warnings are generated.
Implement proper CFI prologues for the BPF programs and callbacks and
drop __nocfi for arm64. Fix the trampoline generation code to emit kCFI
prologue when a struct_ops trampoline is being prepared.
Signed-off-by: Puranjay Mohan <puranjay12@gmail.com>
Co-developed-by: Maxwell Bland <mbland@motorola.com>
Signed-off-by: Maxwell Bland <mbland@motorola.com>
Co-developed-by: Sami Tolvanen <samitolvanen@google.com>
Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Tested-by: Dao Huang <huangdao1@oppo.com>
Acked-by: Will Deacon <will@kernel.org>
---
arch/arm64/include/asm/cfi.h | 7 +++++++
arch/arm64/net/bpf_jit_comp.c | 22 +++++++++++++++++++---
2 files changed, 26 insertions(+), 3 deletions(-)
create mode 100644 arch/arm64/include/asm/cfi.h
diff --git a/arch/arm64/include/asm/cfi.h b/arch/arm64/include/asm/cfi.h
new file mode 100644
index 000000000000..ab90f0351b7a
--- /dev/null
+++ b/arch/arm64/include/asm/cfi.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_ARM64_CFI_H
+#define _ASM_ARM64_CFI_H
+
+#define __bpfcall
+
+#endif /* _ASM_ARM64_CFI_H */
diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c
index 89b1b8c248c6..f4a98c1a1583 100644
--- a/arch/arm64/net/bpf_jit_comp.c
+++ b/arch/arm64/net/bpf_jit_comp.c
@@ -10,6 +10,7 @@
#include <linux/arm-smccc.h>
#include <linux/bitfield.h>
#include <linux/bpf.h>
+#include <linux/cfi.h>
#include <linux/filter.h>
#include <linux/memory.h>
#include <linux/printk.h>
@@ -166,6 +167,12 @@ static inline void emit_bti(u32 insn, struct jit_ctx *ctx)
emit(insn, ctx);
}
+static inline void emit_kcfi(u32 hash, struct jit_ctx *ctx)
+{
+ if (IS_ENABLED(CONFIG_CFI_CLANG))
+ emit(hash, ctx);
+}
+
/*
* Kernel addresses in the vmalloc space use at most 48 bits, and the
* remaining bits are guaranteed to be 0x1. So we can compose the address
@@ -476,7 +483,6 @@ static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf)
const bool is_main_prog = !bpf_is_subprog(prog);
const u8 fp = bpf2a64[BPF_REG_FP];
const u8 arena_vm_base = bpf2a64[ARENA_VM_START];
- const int idx0 = ctx->idx;
int cur_offset;
/*
@@ -502,6 +508,9 @@ static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf)
*
*/
+ emit_kcfi(is_main_prog ? cfi_bpf_hash : cfi_bpf_subprog_hash, ctx);
+ const int idx0 = ctx->idx;
+
/* bpf function may be invoked by 3 instruction types:
* 1. bl, attached via freplace to bpf prog via short jump
* 2. br, attached via freplace to bpf prog via long jump
@@ -2055,9 +2064,9 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
jit_data->ro_header = ro_header;
}
- prog->bpf_func = (void *)ctx.ro_image;
+ prog->bpf_func = (void *)ctx.ro_image + cfi_get_offset();
prog->jited = 1;
- prog->jited_len = prog_size;
+ prog->jited_len = prog_size - cfi_get_offset();
if (!prog->is_func || extra_pass) {
int i;
@@ -2426,6 +2435,12 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,
/* return address locates above FP */
retaddr_off = stack_size + 8;
+ if (flags & BPF_TRAMP_F_INDIRECT) {
+ /*
+ * Indirect call for bpf_struct_ops
+ */
+ emit_kcfi(cfi_get_func_hash(func_addr), ctx);
+ }
/* bpf trampoline may be invoked by 3 instruction types:
* 1. bl, attached to bpf prog or kernel function via short jump
* 2. br, attached to bpf prog or kernel function via long jump
@@ -2942,6 +2957,7 @@ void bpf_jit_free(struct bpf_prog *prog)
sizeof(jit_data->header->size));
kfree(jit_data);
}
+ prog->bpf_func -= cfi_get_offset();
hdr = bpf_jit_binary_pack_hdr(prog);
bpf_jit_binary_pack_free(hdr, NULL);
WARN_ON_ONCE(!bpf_prog_kallsyms_verify_off(prog));
--
2.50.0.727.gbf7dc18ff4-goog
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH bpf-next v12 3/3] arm64/cfi,bpf: Support kCFI + BPF on arm64
2025-07-21 20:20 ` [PATCH bpf-next v12 3/3] arm64/cfi,bpf: Support kCFI + BPF on arm64 Sami Tolvanen
@ 2025-07-22 3:44 ` Xu Kuohai
2025-07-22 12:04 ` Xu Kuohai
2025-07-22 14:33 ` Sami Tolvanen
0 siblings, 2 replies; 7+ messages in thread
From: Xu Kuohai @ 2025-07-22 3:44 UTC (permalink / raw)
To: Sami Tolvanen, bpf, Puranjay Mohan, Alexei Starovoitov,
Daniel Borkmann
Cc: Catalin Marinas, Will Deacon, Andrii Nakryiko, Mark Rutland,
linux-arm-kernel, linux-kernel, Maxwell Bland, Puranjay Mohan,
Dao Huang
On 7/22/2025 4:20 AM, Sami Tolvanen wrote:
> From: Puranjay Mohan <puranjay12@gmail.com>
>
> Currently, bpf_dispatcher_*_func() is marked with `__nocfi` therefore
> calling BPF programs from this interface doesn't cause CFI warnings.
>
> When BPF programs are called directly from C: from BPF helpers or
> struct_ops, CFI warnings are generated.
>
> Implement proper CFI prologues for the BPF programs and callbacks and
> drop __nocfi for arm64. Fix the trampoline generation code to emit kCFI
> prologue when a struct_ops trampoline is being prepared.
>
> Signed-off-by: Puranjay Mohan <puranjay12@gmail.com>
> Co-developed-by: Maxwell Bland <mbland@motorola.com>
> Signed-off-by: Maxwell Bland <mbland@motorola.com>
> Co-developed-by: Sami Tolvanen <samitolvanen@google.com>
> Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
> Tested-by: Dao Huang <huangdao1@oppo.com>
> Acked-by: Will Deacon <will@kernel.org>
> ---
> arch/arm64/include/asm/cfi.h | 7 +++++++
> arch/arm64/net/bpf_jit_comp.c | 22 +++++++++++++++++++---
> 2 files changed, 26 insertions(+), 3 deletions(-)
> create mode 100644 arch/arm64/include/asm/cfi.h
>
> diff --git a/arch/arm64/include/asm/cfi.h b/arch/arm64/include/asm/cfi.h
> new file mode 100644
> index 000000000000..ab90f0351b7a
> --- /dev/null
> +++ b/arch/arm64/include/asm/cfi.h
> @@ -0,0 +1,7 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _ASM_ARM64_CFI_H
> +#define _ASM_ARM64_CFI_H
> +
> +#define __bpfcall
> +
> +#endif /* _ASM_ARM64_CFI_H */
> diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c
> index 89b1b8c248c6..f4a98c1a1583 100644
> --- a/arch/arm64/net/bpf_jit_comp.c
> +++ b/arch/arm64/net/bpf_jit_comp.c
> @@ -10,6 +10,7 @@
> #include <linux/arm-smccc.h>
> #include <linux/bitfield.h>
> #include <linux/bpf.h>
> +#include <linux/cfi.h>
> #include <linux/filter.h>
> #include <linux/memory.h>
> #include <linux/printk.h>
> @@ -166,6 +167,12 @@ static inline void emit_bti(u32 insn, struct jit_ctx *ctx)
> emit(insn, ctx);
> }
>
> +static inline void emit_kcfi(u32 hash, struct jit_ctx *ctx)
> +{
> + if (IS_ENABLED(CONFIG_CFI_CLANG))
> + emit(hash, ctx);
I guess this won't work on big-endian cpus, since arm64 instructions
are always stored in little-endian, but data not.
> +}
> +
> /*
> * Kernel addresses in the vmalloc space use at most 48 bits, and the
> * remaining bits are guaranteed to be 0x1. So we can compose the address
> @@ -476,7 +483,6 @@ static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf)
> const bool is_main_prog = !bpf_is_subprog(prog);
> const u8 fp = bpf2a64[BPF_REG_FP];
> const u8 arena_vm_base = bpf2a64[ARENA_VM_START];
> - const int idx0 = ctx->idx;
> int cur_offset;
>
> /*
> @@ -502,6 +508,9 @@ static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf)
> *
> */
>
> + emit_kcfi(is_main_prog ? cfi_bpf_hash : cfi_bpf_subprog_hash, ctx);
> + const int idx0 = ctx->idx;
move the idx0 definition back to its original position to match the
coding style of the rest of the file?
> +
> /* bpf function may be invoked by 3 instruction types:
> * 1. bl, attached via freplace to bpf prog via short jump
> * 2. br, attached via freplace to bpf prog via long jump
> @@ -2055,9 +2064,9 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
> jit_data->ro_header = ro_header;
> }
>
> - prog->bpf_func = (void *)ctx.ro_image;
> + prog->bpf_func = (void *)ctx.ro_image + cfi_get_offset();
> prog->jited = 1;
> - prog->jited_len = prog_size;
> + prog->jited_len = prog_size - cfi_get_offset();
>
> if (!prog->is_func || extra_pass) {
> int i;
> @@ -2426,6 +2435,12 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,
> /* return address locates above FP */
> retaddr_off = stack_size + 8;
>
> + if (flags & BPF_TRAMP_F_INDIRECT) {
> + /*
> + * Indirect call for bpf_struct_ops
> + */
> + emit_kcfi(cfi_get_func_hash(func_addr), ctx);
> + }
> /* bpf trampoline may be invoked by 3 instruction types:
> * 1. bl, attached to bpf prog or kernel function via short jump
> * 2. br, attached to bpf prog or kernel function via long jump
> @@ -2942,6 +2957,7 @@ void bpf_jit_free(struct bpf_prog *prog)
> sizeof(jit_data->header->size));
> kfree(jit_data);
> }
> + prog->bpf_func -= cfi_get_offset();
> hdr = bpf_jit_binary_pack_hdr(prog);
> bpf_jit_binary_pack_free(hdr, NULL);
> WARN_ON_ONCE(!bpf_prog_kallsyms_verify_off(prog));
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH bpf-next v12 3/3] arm64/cfi,bpf: Support kCFI + BPF on arm64
2025-07-22 3:44 ` Xu Kuohai
@ 2025-07-22 12:04 ` Xu Kuohai
2025-07-22 14:33 ` Sami Tolvanen
1 sibling, 0 replies; 7+ messages in thread
From: Xu Kuohai @ 2025-07-22 12:04 UTC (permalink / raw)
To: Sami Tolvanen, bpf, Puranjay Mohan, Alexei Starovoitov,
Daniel Borkmann
Cc: Catalin Marinas, Will Deacon, Andrii Nakryiko, Mark Rutland,
linux-arm-kernel, linux-kernel, Maxwell Bland, Puranjay Mohan,
Dao Huang
On 7/22/2025 11:44 AM, Xu Kuohai wrote:
> On 7/22/2025 4:20 AM, Sami Tolvanen wrote:
>> From: Puranjay Mohan <puranjay12@gmail.com>
>>
>> Currently, bpf_dispatcher_*_func() is marked with `__nocfi` therefore
>> calling BPF programs from this interface doesn't cause CFI warnings.
>>
>> When BPF programs are called directly from C: from BPF helpers or
>> struct_ops, CFI warnings are generated.
>>
>> Implement proper CFI prologues for the BPF programs and callbacks and
>> drop __nocfi for arm64. Fix the trampoline generation code to emit kCFI
>> prologue when a struct_ops trampoline is being prepared.
>>
>> Signed-off-by: Puranjay Mohan <puranjay12@gmail.com>
>> Co-developed-by: Maxwell Bland <mbland@motorola.com>
>> Signed-off-by: Maxwell Bland <mbland@motorola.com>
>> Co-developed-by: Sami Tolvanen <samitolvanen@google.com>
>> Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
>> Tested-by: Dao Huang <huangdao1@oppo.com>
>> Acked-by: Will Deacon <will@kernel.org>
>> ---
>> arch/arm64/include/asm/cfi.h | 7 +++++++
>> arch/arm64/net/bpf_jit_comp.c | 22 +++++++++++++++++++---
>> 2 files changed, 26 insertions(+), 3 deletions(-)
>> create mode 100644 arch/arm64/include/asm/cfi.h
>>
>> diff --git a/arch/arm64/include/asm/cfi.h b/arch/arm64/include/asm/cfi.h
>> new file mode 100644
>> index 000000000000..ab90f0351b7a
>> --- /dev/null
>> +++ b/arch/arm64/include/asm/cfi.h
>> @@ -0,0 +1,7 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +#ifndef _ASM_ARM64_CFI_H
>> +#define _ASM_ARM64_CFI_H
>> +
>> +#define __bpfcall
>> +
>> +#endif /* _ASM_ARM64_CFI_H */
>> diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c
>> index 89b1b8c248c6..f4a98c1a1583 100644
>> --- a/arch/arm64/net/bpf_jit_comp.c
>> +++ b/arch/arm64/net/bpf_jit_comp.c
>> @@ -10,6 +10,7 @@
>> #include <linux/arm-smccc.h>
>> #include <linux/bitfield.h>
>> #include <linux/bpf.h>
>> +#include <linux/cfi.h>
>> #include <linux/filter.h>
>> #include <linux/memory.h>
>> #include <linux/printk.h>
>> @@ -166,6 +167,12 @@ static inline void emit_bti(u32 insn, struct jit_ctx *ctx)
>> emit(insn, ctx);
>> }
>> +static inline void emit_kcfi(u32 hash, struct jit_ctx *ctx)
>> +{
>> + if (IS_ENABLED(CONFIG_CFI_CLANG))
>> + emit(hash, ctx);
>
> I guess this won't work on big-endian cpus, since arm64 instructions
> are always stored in little-endian, but data not.
>
There is indeed an issue. I built a big-endian kernel with this patch
and tested it on qemu, a CFI failure is triggered on kernel booting:
CFI failure at kern_sys_bpf+0x2d4/0x4f0 (target: bpf_prog_dc1d7467ed3b3c17___loader.prog+0x0/0x6dc; expected type: 0xd9421881)
Internal error: Oops - CFI: 00000000f2008228 [#1] SMP
Modules linked in:
CPU: 2 UID: 0 PID: 1 Comm: swapper/0 Not tainted 6.16.0-rc6-ge72c32d6c27a-dirty #10 NONE
Hardware name: linux,dummy-virt (DT)
pstate: 80000005 (Nzcv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
pc : kern_sys_bpf+0x2d4/0x4f0
lr : kern_sys_bpf+0x290/0x4f0
sp : ffff8000844e7320
x29: ffff8000844e7390 x28: ffff80008436f000 x27: 1fffe00018268040
x26: ffff8000844e7400 x25: ffff8000844e77c0 x24: 0000000000000030
x23: 1ffff0001089ce68 x22: dfff800000000000 x21: ffff80008455b030
x20: ffff0000c1340200 x19: ffff80008455b000 x18: ffffffff00000000
x17: 00000000d9421881 x16: 00000000811842d9 x15: 0000000000000001
x14: 0000000000000001 x13: ffff0001b5b947f4 x12: 1fffe0001807a001
x11: 0000000000000001 x10: 0000000000000000 x9 : 1ffff000108ab606
x8 : ffff800084979894 x7 : ffff8000805a5ce8 x6 : 0000000000000000
x5 : 0000000000000000 x4 : 0000000000000000 x3 : 0000000000000010
x2 : 0000000000000000 x1 : ffff80008455b048 x0 : ffff0000c1340200
Call trace:
kern_sys_bpf+0x2d4/0x4f0 (P)
load+0x324/0x7a4
do_one_initcall+0x1e8/0x7a0
do_initcall_level+0x180/0x36c
do_initcalls+0x60/0xa4
do_basic_setup+0x9c/0xb0
kernel_init_freeable+0x270/0x390
kernel_init+0x2c/0x1c8
ret_from_fork+0x10/0x20
Code: 72831031 72bb2851 6b11021f 54000040 (d4304500)
---[ end trace 0000000000000000 ]---
Kernel panic - not syncing: Oops - CFI: Fatal exception
SMP: stopping secondary CPUs
Kernel Offset: disabled
CPU features: 0x1000,000800d0,02000800,0400420b
Memory Limit: none
---[ end Kernel panic - not syncing: Oops - CFI: Fatal exception ]---
And the failure can be fixed with the following change:
--- a/arch/arm64/net/bpf_jit_comp.c
+++ b/arch/arm64/net/bpf_jit_comp.c
@@ -107,6 +107,14 @@ static inline void emit(const u32 insn, struct jit_ctx *ctx)
ctx->idx++;
}
+static inline void emit_u32_data(const u32 data, struct jit_ctx *ctx)
+{
+ if (ctx->image != NULL && ctx->write)
+ ctx->image[ctx->idx] = data;
+
+ ctx->idx++;
+}
+
static inline void emit_a64_mov_i(const int is64, const int reg,
const s32 val, struct jit_ctx *ctx)
{
@@ -170,7 +178,7 @@ static inline void emit_bti(u32 insn, struct jit_ctx *ctx)
static inline void emit_kcfi(u32 hash, struct jit_ctx *ctx)
{
if (IS_ENABLED(CONFIG_CFI_CLANG))
- emit(hash, ctx);
+ emit_u32_data(hash, ctx);
}
>> +}
>> +
>> /*
>> * Kernel addresses in the vmalloc space use at most 48 bits, and the
>> * remaining bits are guaranteed to be 0x1. So we can compose the address
>> @@ -476,7 +483,6 @@ static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf)
>> const bool is_main_prog = !bpf_is_subprog(prog);
>> const u8 fp = bpf2a64[BPF_REG_FP];
>> const u8 arena_vm_base = bpf2a64[ARENA_VM_START];
>> - const int idx0 = ctx->idx;
>> int cur_offset;
>> /*
>> @@ -502,6 +508,9 @@ static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf)
>> *
>> */
>> + emit_kcfi(is_main_prog ? cfi_bpf_hash : cfi_bpf_subprog_hash, ctx);
>> + const int idx0 = ctx->idx;
>
> move the idx0 definition back to its original position to match the
> coding style of the rest of the file?
>
>> +
>> /* bpf function may be invoked by 3 instruction types:
>> * 1. bl, attached via freplace to bpf prog via short jump
>> * 2. br, attached via freplace to bpf prog via long jump
>> @@ -2055,9 +2064,9 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
>> jit_data->ro_header = ro_header;
>> }
>> - prog->bpf_func = (void *)ctx.ro_image;
>> + prog->bpf_func = (void *)ctx.ro_image + cfi_get_offset();
>> prog->jited = 1;
>> - prog->jited_len = prog_size;
>> + prog->jited_len = prog_size - cfi_get_offset();
>> if (!prog->is_func || extra_pass) {
>> int i;
>> @@ -2426,6 +2435,12 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,
>> /* return address locates above FP */
>> retaddr_off = stack_size + 8;
>> + if (flags & BPF_TRAMP_F_INDIRECT) {
>> + /*
>> + * Indirect call for bpf_struct_ops
>> + */
>> + emit_kcfi(cfi_get_func_hash(func_addr), ctx);
>> + }
>> /* bpf trampoline may be invoked by 3 instruction types:
>> * 1. bl, attached to bpf prog or kernel function via short jump
>> * 2. br, attached to bpf prog or kernel function via long jump
>> @@ -2942,6 +2957,7 @@ void bpf_jit_free(struct bpf_prog *prog)
>> sizeof(jit_data->header->size));
>> kfree(jit_data);
>> }
>> + prog->bpf_func -= cfi_get_offset();
>> hdr = bpf_jit_binary_pack_hdr(prog);
>> bpf_jit_binary_pack_free(hdr, NULL);
>> WARN_ON_ONCE(!bpf_prog_kallsyms_verify_off(prog));
>
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH bpf-next v12 3/3] arm64/cfi,bpf: Support kCFI + BPF on arm64
2025-07-22 3:44 ` Xu Kuohai
2025-07-22 12:04 ` Xu Kuohai
@ 2025-07-22 14:33 ` Sami Tolvanen
1 sibling, 0 replies; 7+ messages in thread
From: Sami Tolvanen @ 2025-07-22 14:33 UTC (permalink / raw)
To: Xu Kuohai
Cc: bpf, Puranjay Mohan, Alexei Starovoitov, Daniel Borkmann,
Catalin Marinas, Will Deacon, Andrii Nakryiko, Mark Rutland,
linux-arm-kernel, linux-kernel, Maxwell Bland, Puranjay Mohan,
Dao Huang
Hi,
On Mon, Jul 21, 2025 at 8:44 PM Xu Kuohai <xukuohai@huaweicloud.com> wrote:
>
> On 7/22/2025 4:20 AM, Sami Tolvanen wrote:
> > +static inline void emit_kcfi(u32 hash, struct jit_ctx *ctx)
> > +{
> > + if (IS_ENABLED(CONFIG_CFI_CLANG))
> > + emit(hash, ctx);
>
> I guess this won't work on big-endian cpus, since arm64 instructions
> are always stored in little-endian, but data not.
Nice catch! I'll send a new version with your suggested fix.
> > +}
> > +
> > /*
> > * Kernel addresses in the vmalloc space use at most 48 bits, and the
> > * remaining bits are guaranteed to be 0x1. So we can compose the address
> > @@ -476,7 +483,6 @@ static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf)
> > const bool is_main_prog = !bpf_is_subprog(prog);
> > const u8 fp = bpf2a64[BPF_REG_FP];
> > const u8 arena_vm_base = bpf2a64[ARENA_VM_START];
> > - const int idx0 = ctx->idx;
> > int cur_offset;
> >
> > /*
> > @@ -502,6 +508,9 @@ static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf)
> > *
> > */
> >
> > + emit_kcfi(is_main_prog ? cfi_bpf_hash : cfi_bpf_subprog_hash, ctx);
> > + const int idx0 = ctx->idx;
>
> move the idx0 definition back to its original position to match the
> coding style of the rest of the file?
The const definition needs to happen after emit_kcfi, because we
increment ctx->idx when CFI is enabled.
Sami
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2025-07-22 14:34 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-21 20:20 [PATCH bpf-next v12 0/3] Support kCFI + BPF on arm64 Sami Tolvanen
2025-07-21 20:20 ` [PATCH bpf-next v12 1/3] cfi: add C CFI type macro Sami Tolvanen
2025-07-21 20:20 ` [PATCH bpf-next v12 2/3] cfi: Move BPF CFI types and helpers to generic code Sami Tolvanen
2025-07-21 20:20 ` [PATCH bpf-next v12 3/3] arm64/cfi,bpf: Support kCFI + BPF on arm64 Sami Tolvanen
2025-07-22 3:44 ` Xu Kuohai
2025-07-22 12:04 ` Xu Kuohai
2025-07-22 14:33 ` Sami Tolvanen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).