bpf: Add bpf_arch_text_poke() helper
authorAlexei Starovoitov <ast@kernel.org>
Thu, 14 Nov 2019 18:57:03 +0000 (10:57 -0800)
committerDaniel Borkmann <daniel@iogearbox.net>
Fri, 15 Nov 2019 22:41:28 +0000 (23:41 +0100)
Add bpf_arch_text_poke() helper that is used by BPF trampoline logic to patch
nops/calls in kernel text into calls into BPF trampoline and to patch
calls/nops inside BPF programs too.

Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Song Liu <songliubraving@fb.com>
Acked-by: Andrii Nakryiko <andriin@fb.com>
Link: https://lore.kernel.org/bpf/20191114185720.1641606-4-ast@kernel.org
arch/x86/net/bpf_jit_comp.c
include/linux/bpf.h
kernel/bpf/core.c

index fb99d97..254b288 100644 (file)
@@ -9,9 +9,11 @@
 #include <linux/filter.h>
 #include <linux/if_vlan.h>
 #include <linux/bpf.h>
+#include <linux/memory.h>
 #include <asm/extable.h>
 #include <asm/set_memory.h>
 #include <asm/nospec-branch.h>
+#include <asm/text-patching.h>
 
 static u8 *emit_code(u8 *ptr, u32 bytes, unsigned int len)
 {
@@ -486,6 +488,55 @@ static int emit_call(u8 **pprog, void *func, void *ip)
        return 0;
 }
 
+int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type t,
+                      void *old_addr, void *new_addr)
+{
+       u8 old_insn[X86_CALL_SIZE] = {};
+       u8 new_insn[X86_CALL_SIZE] = {};
+       u8 *prog;
+       int ret;
+
+       if (!is_kernel_text((long)ip))
+               /* BPF trampoline in modules is not supported */
+               return -EINVAL;
+
+       if (old_addr) {
+               prog = old_insn;
+               ret = emit_call(&prog, old_addr, (void *)ip);
+               if (ret)
+                       return ret;
+       }
+       if (new_addr) {
+               prog = new_insn;
+               ret = emit_call(&prog, new_addr, (void *)ip);
+               if (ret)
+                       return ret;
+       }
+       ret = -EBUSY;
+       mutex_lock(&text_mutex);
+       switch (t) {
+       case BPF_MOD_NOP_TO_CALL:
+               if (memcmp(ip, ideal_nops[NOP_ATOMIC5], X86_CALL_SIZE))
+                       goto out;
+               text_poke_bp(ip, new_insn, X86_CALL_SIZE, NULL);
+               break;
+       case BPF_MOD_CALL_TO_CALL:
+               if (memcmp(ip, old_insn, X86_CALL_SIZE))
+                       goto out;
+               text_poke_bp(ip, new_insn, X86_CALL_SIZE, NULL);
+               break;
+       case BPF_MOD_CALL_TO_NOP:
+               if (memcmp(ip, old_insn, X86_CALL_SIZE))
+                       goto out;
+               text_poke_bp(ip, ideal_nops[NOP_ATOMIC5], X86_CALL_SIZE, NULL);
+               break;
+       }
+       ret = 0;
+out:
+       mutex_unlock(&text_mutex);
+       return ret;
+}
+
 static bool ex_handler_bpf(const struct exception_table_entry *x,
                           struct pt_regs *regs, int trapnr,
                           unsigned long error_code, unsigned long fault_addr)
index 7c7f518..8b90db2 100644 (file)
@@ -1157,4 +1157,12 @@ static inline u32 bpf_xdp_sock_convert_ctx_access(enum bpf_access_type type,
 }
 #endif /* CONFIG_INET */
 
+enum bpf_text_poke_type {
+       BPF_MOD_NOP_TO_CALL,
+       BPF_MOD_CALL_TO_CALL,
+       BPF_MOD_CALL_TO_NOP,
+};
+int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type t,
+                      void *addr1, void *addr2);
+
 #endif /* _LINUX_BPF_H */
index 99693f3..434a0d9 100644 (file)
@@ -2144,6 +2144,12 @@ int __weak skb_copy_bits(const struct sk_buff *skb, int offset, void *to,
        return -EFAULT;
 }
 
+int __weak bpf_arch_text_poke(void *ip, enum bpf_text_poke_type t,
+                             void *addr1, void *addr2)
+{
+       return -ENOTSUPP;
+}
+
 DEFINE_STATIC_KEY_FALSE(bpf_stats_enabled_key);
 EXPORT_SYMBOL(bpf_stats_enabled_key);