From 542d0b7b5e48d44ad3986c149d552e890f425b72 Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Wed, 4 Sep 2019 14:08:16 -0700 Subject: [PATCH] ANDROID: bpf: validate bpf_func when BPF_JIT is enabled with CFI 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 --- include/linux/filter.h | 62 +++++++++++++++++++++++++++++++++++++++++- kernel/bpf/core.c | 9 ++++++ net/Kconfig | 1 - 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/include/linux/filter.h b/include/linux/filter.h index 7c0e616362f0..0ec7f787a9db 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -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 diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 5c5b20e0e730..1d4a58d48e8f 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -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)); diff --git a/net/Kconfig b/net/Kconfig index 999cc6beacc3..454a26178f6d 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -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 -- 2.20.1