|
@@ -12,6 +12,7 @@
|
|
|
#include <linux/netdevice.h>
|
|
|
#include <linux/filter.h>
|
|
|
#include <linux/if_vlan.h>
|
|
|
+#include <linux/random.h>
|
|
|
|
|
|
/*
|
|
|
* Conventions :
|
|
@@ -144,6 +145,39 @@ static int pkt_type_offset(void)
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
+struct bpf_binary_header {
|
|
|
+ unsigned int pages;
|
|
|
+ /* Note : for security reasons, bpf code will follow a randomly
|
|
|
+ * sized amount of int3 instructions
|
|
|
+ */
|
|
|
+ u8 image[];
|
|
|
+};
|
|
|
+
|
|
|
+static struct bpf_binary_header *bpf_alloc_binary(unsigned int proglen,
|
|
|
+ u8 **image_ptr)
|
|
|
+{
|
|
|
+ unsigned int sz, hole;
|
|
|
+ struct bpf_binary_header *header;
|
|
|
+
|
|
|
+ /* Most of BPF filters are really small,
|
|
|
+ * but if some of them fill a page, allow at least
|
|
|
+ * 128 extra bytes to insert a random section of int3
|
|
|
+ */
|
|
|
+ sz = round_up(proglen + sizeof(*header) + 128, PAGE_SIZE);
|
|
|
+ header = module_alloc(sz);
|
|
|
+ if (!header)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ memset(header, 0xcc, sz); /* fill whole space with int3 instructions */
|
|
|
+
|
|
|
+ header->pages = sz / PAGE_SIZE;
|
|
|
+ hole = sz - (proglen + sizeof(*header));
|
|
|
+
|
|
|
+ /* insert a random number of int3 instructions before BPF code */
|
|
|
+ *image_ptr = &header->image[prandom_u32() % hole];
|
|
|
+ return header;
|
|
|
+}
|
|
|
+
|
|
|
void bpf_jit_compile(struct sk_filter *fp)
|
|
|
{
|
|
|
u8 temp[64];
|
|
@@ -153,6 +187,7 @@ void bpf_jit_compile(struct sk_filter *fp)
|
|
|
int t_offset, f_offset;
|
|
|
u8 t_op, f_op, seen = 0, pass;
|
|
|
u8 *image = NULL;
|
|
|
+ struct bpf_binary_header *header = NULL;
|
|
|
u8 *func;
|
|
|
int pc_ret0 = -1; /* bpf index of first RET #0 instruction (if any) */
|
|
|
unsigned int cleanup_addr; /* epilogue code offset */
|
|
@@ -693,7 +728,7 @@ cond_branch: f_offset = addrs[i + filter[i].jf] - addrs[i];
|
|
|
if (unlikely(proglen + ilen > oldproglen)) {
|
|
|
pr_err("bpb_jit_compile fatal error\n");
|
|
|
kfree(addrs);
|
|
|
- module_free(NULL, image);
|
|
|
+ module_free(NULL, header);
|
|
|
return;
|
|
|
}
|
|
|
memcpy(image + proglen, temp, ilen);
|
|
@@ -717,8 +752,8 @@ cond_branch: f_offset = addrs[i + filter[i].jf] - addrs[i];
|
|
|
break;
|
|
|
}
|
|
|
if (proglen == oldproglen) {
|
|
|
- image = module_alloc(proglen);
|
|
|
- if (!image)
|
|
|
+ header = bpf_alloc_binary(proglen, &image);
|
|
|
+ if (!header)
|
|
|
goto out;
|
|
|
}
|
|
|
oldproglen = proglen;
|
|
@@ -728,7 +763,8 @@ cond_branch: f_offset = addrs[i + filter[i].jf] - addrs[i];
|
|
|
bpf_jit_dump(flen, proglen, pass, image);
|
|
|
|
|
|
if (image) {
|
|
|
- bpf_flush_icache(image, image + proglen);
|
|
|
+ bpf_flush_icache(header, image + proglen);
|
|
|
+ set_memory_ro((unsigned long)header, header->pages);
|
|
|
fp->bpf_func = (void *)image;
|
|
|
}
|
|
|
out:
|
|
@@ -738,6 +774,11 @@ out:
|
|
|
|
|
|
void bpf_jit_free(struct sk_filter *fp)
|
|
|
{
|
|
|
- if (fp->bpf_func != sk_run_filter)
|
|
|
- module_free(NULL, fp->bpf_func);
|
|
|
+ if (fp->bpf_func != sk_run_filter) {
|
|
|
+ unsigned long addr = (unsigned long)fp->bpf_func & PAGE_MASK;
|
|
|
+ struct bpf_binary_header *header = (void *)addr;
|
|
|
+
|
|
|
+ set_memory_rw(addr, header->pages);
|
|
|
+ module_free(NULL, header);
|
|
|
+ }
|
|
|
}
|