ANDROID: bpf: validate bpf_func when BPF_JIT is enabled with CFI
authorSami Tolvanen <samitolvanen@google.com>
Wed, 4 Sep 2019 21:08:16 +0000 (14:08 -0700)
committerCosmin Tanislav <demonsingur@gmail.com>
Mon, 22 Apr 2024 17:24:58 +0000 (20:24 +0300)
With CONFIG_BPF_JIT, the kernel makes indirect calls to dynamically
generated code, which the compile-time Control-Flow Integrity (CFI)
checking cannot validate. This change adds basic sanity checking to
ensure we are jumping to a valid location, which narrows down the
attack surface on the stored pointer.

In addition, this change adds a weak arch_bpf_jit_check_func function,
which architectures that implement BPF JIT can override to perform
additional validation, such as verifying that the pointer points to
the correct memory region.

Bug: 140377409
Change-Id: I8ebac6637ab6bd9db44716b1c742add267298669
Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
include/linux/filter.h
kernel/bpf/core.c
net/Kconfig

index 7c0e616362f057b1484065938acb2347bd49765b..0ec7f787a9db9a55eaae662bae5997f3bf6b694e 100644 (file)
@@ -443,7 +443,12 @@ struct sock_fprog_kern {
        struct sock_filter      *filter;
 };
 
+#define BPF_BINARY_HEADER_MAGIC        0x05de0e82
+
 struct bpf_binary_header {
+#ifdef CONFIG_CFI_CLANG
+       u32 magic;
+#endif
        unsigned int pages;
        u8 image[];
 };
@@ -476,7 +481,62 @@ struct sk_filter {
        struct bpf_prog *prog;
 };
 
-#define BPF_PROG_RUN(filter, ctx)  (*filter->bpf_func)(ctx, filter->insnsi)
+#if IS_ENABLED(CONFIG_BPF_JIT) && IS_ENABLED(CONFIG_CFI_CLANG)
+/*
+ * With JIT, the kernel makes an indirect call to dynamically generated
+ * code. Use bpf_call_func to perform additional validation of the call
+ * target to narrow down attack surface. Architectures implementing BPF
+ * JIT can override arch_bpf_jit_check_func for arch-specific checking.
+ */
+extern bool arch_bpf_jit_check_func(const struct bpf_prog *prog);
+
+static inline unsigned int __bpf_call_func(const struct bpf_prog *prog,
+                                          const void *ctx)
+{
+       /* Call interpreter with CFI checking. */
+       return prog->bpf_func(ctx, prog->insnsi);
+}
+
+static inline struct bpf_binary_header *
+bpf_jit_binary_hdr(const struct bpf_prog *fp);
+
+static inline unsigned int __nocfi bpf_call_func(const struct bpf_prog *prog,
+                                                const void *ctx)
+{
+       const struct bpf_binary_header *hdr = bpf_jit_binary_hdr(prog);
+
+       if (!IS_ENABLED(CONFIG_BPF_JIT_ALWAYS_ON) && !prog->jited)
+               return __bpf_call_func(prog, ctx);
+
+       /*
+        * We are about to call dynamically generated code. Check that the
+        * page has bpf_binary_header with a valid magic to limit possible
+        * call targets.
+        */
+       BUG_ON(hdr->magic != BPF_BINARY_HEADER_MAGIC ||
+               !arch_bpf_jit_check_func(prog));
+
+       /* Call jited function without CFI checking. */
+       return prog->bpf_func(ctx, prog->insnsi);
+}
+
+static inline void bpf_jit_set_header_magic(struct bpf_binary_header *hdr)
+{
+       hdr->magic = BPF_BINARY_HEADER_MAGIC;
+}
+#else
+static inline unsigned int bpf_call_func(const struct bpf_prog *prog,
+                                        const void *ctx)
+{
+       return prog->bpf_func(ctx, prog->insnsi);
+}
+
+static inline void bpf_jit_set_header_magic(struct bpf_binary_header *hdr)
+{
+}
+#endif
+
+#define BPF_PROG_RUN(filter, ctx)  bpf_call_func(filter, ctx)
 
 #define BPF_SKB_CB_LEN QDISC_CB_PRIV_LEN
 
index 5c5b20e0e730c41a9a0adaa26ba8f146d1445559..1d4a58d48e8f160bde55668a5eaed85e0e1f79a8 100644 (file)
@@ -579,6 +579,14 @@ static void bpf_jit_uncharge_modmem(u32 pages)
        atomic_long_sub(pages, &bpf_jit_current);
 }
 
+#if IS_ENABLED(CONFIG_BPF_JIT) && IS_ENABLED(CONFIG_CFI_CLANG)
+bool __weak arch_bpf_jit_check_func(const struct bpf_prog *prog)
+{
+       return true;
+}
+EXPORT_SYMBOL(arch_bpf_jit_check_func);
+#endif
+
 struct bpf_binary_header *
 bpf_jit_binary_alloc(unsigned int proglen, u8 **image_ptr,
                     unsigned int alignment,
@@ -605,6 +613,7 @@ bpf_jit_binary_alloc(unsigned int proglen, u8 **image_ptr,
        /* Fill space with illegal/arch-dep instructions. */
        bpf_fill_ill_insns(hdr, size);
 
+       bpf_jit_set_header_magic(hdr);
        hdr->pages = pages;
        hole = min_t(unsigned int, size - (proglen + sizeof(*hdr)),
                     PAGE_SIZE - sizeof(*hdr));
index 999cc6beacc3bbc79f8c8d2a2f72c68f2463eb27..454a26178f6d697302f86602f0946091cb611ff7 100644 (file)
@@ -290,7 +290,6 @@ config BPF_JIT
        bool "enable BPF Just In Time compiler"
        depends on HAVE_CBPF_JIT || HAVE_EBPF_JIT
        depends on MODULES
-       depends on !CFI
        ---help---
          Berkeley Packet Filter filtering capabilities are normally handled
          by an interpreter. This option allows kernel to generate a native