path: root/include
diff options
authorDaniel Borkmann <>2014-09-02 22:53:44 +0200
committerDavid S. Miller <>2014-09-05 12:02:48 -0700
commit60a3b2253c413cf601783b070507d7dd6620c954 (patch)
treed5682002b80cfeb75cb765df5ba097e7c889e9fe /include
parent4a804c01635a43ed073893532c058fbaa1f5154e (diff)
net: bpf: make eBPF interpreter images read-only
With eBPF getting more extended and exposure to user space is on it's way, hardening the memory range the interpreter uses to steer its command flow seems appropriate. This patch moves the to be interpreted bytecode to read-only pages. In case we execute a corrupted BPF interpreter image for some reason e.g. caused by an attacker which got past a verifier stage, it would not only provide arbitrary read/write memory access but arbitrary function calls as well. After setting up the BPF interpreter image, its contents do not change until destruction time, thus we can setup the image on immutable made pages in order to mitigate modifications to that code. The idea is derived from commit 314beb9bcabf ("x86: bpf_jit_comp: secure bpf jit against spraying attacks"). This is possible because bpf_prog is not part of sk_filter anymore. After setup bpf_prog cannot be altered during its life-time. This prevents any modifications to the entire bpf_prog structure (incl. function/JIT image pointer). Every eBPF program (including classic BPF that are migrated) have to call bpf_prog_select_runtime() to select either interpreter or a JIT image as a last setup step, and they all are being freed via bpf_prog_free(), including non-JIT. Therefore, we can easily integrate this into the eBPF life-time, plus since we directly allocate a bpf_prog, we have no performance penalty. Tested with seccomp and test_bpf testsuite in JIT/non-JIT mode and manual inspection of kernel_page_tables. Brad Spengler proposed the same idea via Twitter during development of this patch. Joint work with Hannes Frederic Sowa. Suggested-by: Brad Spengler <> Signed-off-by: Daniel Borkmann <> Signed-off-by: Hannes Frederic Sowa <> Cc: Alexei Starovoitov <> Cc: Kees Cook <> Acked-by: Alexei Starovoitov <> Signed-off-by: David S. Miller <>
Diffstat (limited to 'include')
1 files changed, 44 insertions, 5 deletions
diff --git a/include/linux/filter.h b/include/linux/filter.h
index a5227ab8ccb1..c78994593355 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -9,6 +9,11 @@
#include <linux/skbuff.h>
#include <linux/workqueue.h>
#include <uapi/linux/filter.h>
+#include <asm/cacheflush.h>
+struct sk_buff;
+struct sock;
+struct seccomp_data;
/* Internally used and optimized filter representation with extended
* instruction set based on top of classic BPF.
@@ -320,20 +325,23 @@ struct sock_fprog_kern {
struct sock_filter *filter;
-struct sk_buff;
-struct sock;
-struct seccomp_data;
+struct bpf_work_struct {
+ struct bpf_prog *prog;
+ struct work_struct work;
struct bpf_prog {
+ u32 pages; /* Number of allocated pages */
u32 jited:1, /* Is our filter JIT'ed? */
len:31; /* Number of filter blocks */
struct sock_fprog_kern *orig_prog; /* Original BPF program */
+ struct bpf_work_struct *work; /* Deferred free work struct */
unsigned int (*bpf_func)(const struct sk_buff *skb,
const struct bpf_insn *filter);
+ /* Instructions for interpreter */
union {
struct sock_filter insns[0];
struct bpf_insn insnsi[0];
- struct work_struct work;
@@ -353,6 +361,26 @@ static inline unsigned int bpf_prog_size(unsigned int proglen)
#define bpf_classic_proglen(fprog) (fprog->len * sizeof(fprog->filter[0]))
+static inline void bpf_prog_lock_ro(struct bpf_prog *fp)
+ set_memory_ro((unsigned long)fp, fp->pages);
+static inline void bpf_prog_unlock_ro(struct bpf_prog *fp)
+ set_memory_rw((unsigned long)fp, fp->pages);
+static inline void bpf_prog_lock_ro(struct bpf_prog *fp)
+static inline void bpf_prog_unlock_ro(struct bpf_prog *fp)
int sk_filter(struct sock *sk, struct sk_buff *skb);
void bpf_prog_select_runtime(struct bpf_prog *fp);
@@ -361,6 +389,17 @@ void bpf_prog_free(struct bpf_prog *fp);
int bpf_convert_filter(struct sock_filter *prog, int len,
struct bpf_insn *new_prog, int *new_len);
+struct bpf_prog *bpf_prog_alloc(unsigned int size, gfp_t gfp_extra_flags);
+struct bpf_prog *bpf_prog_realloc(struct bpf_prog *fp_old, unsigned int size,
+ gfp_t gfp_extra_flags);
+void __bpf_prog_free(struct bpf_prog *fp);
+static inline void bpf_prog_unlock_free(struct bpf_prog *fp)
+ bpf_prog_unlock_ro(fp);
+ __bpf_prog_free(fp);
int bpf_prog_create(struct bpf_prog **pfp, struct sock_fprog_kern *fprog);
void bpf_prog_destroy(struct bpf_prog *fp);
@@ -450,7 +489,7 @@ static inline void bpf_jit_compile(struct bpf_prog *fp)
static inline void bpf_jit_free(struct bpf_prog *fp)
- kfree(fp);
+ bpf_prog_unlock_free(fp);
#endif /* CONFIG_BPF_JIT */